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
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
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
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
// Copyright (C) 2015-2023 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/.

#ifndef LIBDHCPSRV_ALLOC_ENGINE_UTILS_H
#define LIBDHCPSRV_ALLOC_ENGINE_UTILS_H

#include <asiolink/io_address.h>
#include <dhcpsrv/alloc_engine.h>
#include <dhcpsrv/cfgmgr.h>
#include <dhcpsrv/iterative_allocator.h>
#include <dhcpsrv/lease_mgr.h>
#include <dhcpsrv/lease_mgr_factory.h>

#include <gtest/gtest.h><--- Include file:  not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <vector><--- Include file:  not found. Please note: Cppcheck does not need standard library headers to get proper results.

namespace isc {
namespace dhcp {
namespace test {

/// @file   alloc_engine_utils.h
///
/// @brief This is a header file for all Allocation Engine tests.
///
/// There used to be one, huge (over 3kloc) alloc_engine_unittest.cc. It is now
/// split into serveral smaller files:
/// alloc_engine_utils.h - contains test class definitions (this file)
/// alloc_engine_utils.cc - contains test class implementation
/// alloc_engine4_unittest.cc - all unit-tests dedicated to IPv4
/// alloc_engine6_unittest.cc - all unit-tests dedicated to IPv6
/// alloc_engine_hooks_unittest.cc - all unit-tests dedicated to hooks

/// @brief Test that statistic manager holds a given value.
///
/// This function may be used in many allocation tests and there's no
/// single base class for it. @todo consider moving it src/lib/util.
///
/// It checks statistics by name, either the global version when the subnet ID
/// is SUBNET_ID_UNUSED or the subnet statistic, including the pool 0 respective
/// statistic.
///
/// @param stat_name Statistic name.
/// @param exp_value Expected value.
/// @param subnet_id subnet_id of the desired subnet, if not zero
/// @param fail_missing flag which indicate if test should fail if the statistic
/// does not exist, or simply ignore it.
///
/// @return true if the statistic manager holds a particular value,
/// false otherwise.
bool testStatistics(const std::string& stat_name,
                    const int64_t exp_value,
                    const SubnetID subnet_id = SUBNET_ID_UNUSED,
                    bool fail_missing = true);

/// @brief Get a value held by statistic manager.
///
/// This function may be used in some allocation tests and there's no
/// single base class for it. @todo consider moving it src/lib/util.
///
/// @param stat_name Statistic name.
/// @param subnet_id subnet_id of the desired subnet, if not zero.
///
/// @return the value held by the statistic manager or zero.
int64_t getStatistics(const std::string& stat_name,
                      const SubnetID subnet_id = SUBNET_ID_UNUSED);

/// @brief IterativeAllocator with internal methods exposed
class NakedIterativeAllocator : public IterativeAllocator {
public:
    /// @brief constructor
    ///
    /// @param type pool types that will be iterated through
    NakedIterativeAllocator(Lease::Type type, const WeakSubnetPtr& subnet)
        : IterativeAllocator(type, subnet) {
    }

    using IterativeAllocator::increaseAddress;
    using IterativeAllocator::increasePrefix;
};

/// @brief Allocation engine with some internal methods exposed
class NakedAllocEngine : public AllocEngine {
public:
    /// @brief the sole constructor
    ///
    /// @param attempts number of lease selection attempts before giving up
    NakedAllocEngine(unsigned int attempts)
        : AllocEngine(attempts) {
    }

    // Expose internal classes for testing purposes
    using AllocEngine::updateLease4ExtendedInfo;

    /// @brief Wrapper method for invoking AllocEngine4::updateLease4ExtendedInfo().
    ///
    /// @param lease lease to update
    /// @param ctx current packet processing context
    ///
    /// @return true if extended information was changed
    bool callUpdateLease4ExtendedInfo(const Lease4Ptr& lease,
                                      AllocEngine::ClientContext4& ctx) const {<--- Parameter 'ctx' can be declared as reference to const
        return (updateLease4ExtendedInfo(lease, ctx));
    }

    /// @brief Wrapper method for invoking AllocEngine6::updateLease6ExtendedInfo().
    ///
    /// @param lease lease to update
    /// @param ctx current packet processing context
    void callUpdateLease6ExtendedInfo(const Lease6Ptr& lease,
                                      AllocEngine::ClientContext6& ctx) const {<--- Parameter 'ctx' can be declared as reference to const
        updateLease6ExtendedInfo(lease, ctx);
    }
};

/// @brief Used in Allocation Engine tests for IPv6
class AllocEngine6Test : public ::testing::Test {
public:

    /// @brief Specified expected result of a given operation
    enum ExpectedResult {
        SHOULD_PASS,
        SHOULD_FAIL
    };

    /// @brief Default constructor
    ///
    /// Sets duid_, iaid_, subnet_, pool_ fields to example values used
    /// in many tests, initializes cfg_mgr configuration and creates
    /// lease database.
    AllocEngine6Test();

    /// @brief Configures a subnet and adds one pool to it.
    ///
    /// This function removes existing v6 subnets before configuring
    /// a new one.
    ///
    /// @param subnet Address of a subnet to be configured.
    /// @param pool_start First address in the address pool.
    /// @param pool_end Last address in the address pool.
    /// @param pd_pool_prefix Prefix for the prefix delegation pool. It
    /// defaults to 0 which means that PD pool is not specified.
    /// @param pd_pool_length Length of the PD pool prefix.
    /// @param pd_delegated_length Delegated prefix length.
    void initSubnet(const asiolink::IOAddress& subnet,
                    const asiolink::IOAddress& pool_start,
                    const asiolink::IOAddress& pool_end,
                    const asiolink::IOAddress& pd_pool_prefix =
                    asiolink::IOAddress::IPV6_ZERO_ADDRESS(),
                    const uint8_t pd_pool_length = 0,
                    const uint8_t pd_delegated_length = 0);

    /// @brief Initializes FQDN data for a test.
    ///
    /// The initialized values are used by the test fixture class members to
    /// verify the correctness of a lease.
    ///
    /// @param hostname Hostname to be assigned to a lease.
    /// @param fqdn_fwd Indicates whether or not to perform forward DNS update
    /// for a lease.
    /// @param fqdn_fwd Indicates whether or not to perform reverse DNS update
    /// for a lease.
    void initFqdn(const std::string& hostname, const bool fqdn_fwd,
                  const bool fqdn_rev) {
        hostname_ = hostname;
        fqdn_fwd_ = fqdn_fwd;
        fqdn_rev_ = fqdn_rev;
    }

    /// @brief Wrapper around call to AllocEngine6::findReservation
    ///
    /// If a reservation is found by the engine, the function sets
    /// ctx.hostname_ accordingly.
    ///
    /// @param engine allocation engine to use
    /// @param ctx client context to pass into engine's findReservation method
    void findReservation(AllocEngine& engine, AllocEngine::ClientContext6& ctx);

    /// @brief attempts to convert leases collection to a single lease
    ///
    /// This operation makes sense if there is at most one lease in the
    /// collection. Otherwise it will throw.
    ///
    /// @param col collection of leases (zero or one leases allowed)
    /// @throw MultipleRecords if there is more than one lease
    ///
    /// @return Lease6 pointer (or NULL if collection was empty)
    Lease6Ptr expectOneLease(const Lease6Collection& col) {
        if (col.size() > 1) {
            isc_throw(db::MultipleRecords, "More than one lease found in collection");
        }
        if (col.empty()) {
            return (Lease6Ptr());
        }
        return (*col.begin());
    }

    /// @brief checks if Lease6 matches expected configuration
    ///
    /// @param duid pointer to the client's DUID.
    /// @param lease lease to be checked
    /// @param exp_type expected lease type
    /// @param exp_pd_len expected prefix length
    /// @param expected_in_subnet whether the lease is expected to be in subnet
    /// @param expected_in_pool whether the lease is expected to be in dynamic
    void checkLease6(const DuidPtr& duid,
                     const Lease6Ptr& lease,
                     Lease::Type exp_type,
                     uint8_t exp_pd_len = 128,
                     bool expected_in_subnet = true,
                     bool expected_in_pool = true) {

        // that is belongs to the right subnet
        EXPECT_EQ(lease->subnet_id_, subnet_->getID());

        if (expected_in_subnet) {
            EXPECT_TRUE(subnet_->inRange(lease->addr_))
                << " address: " << lease->addr_.toText();
        } else {
            EXPECT_FALSE(subnet_->inRange(lease->addr_))
                << " address: " << lease->addr_.toText();
        }

        if (expected_in_pool) {
            EXPECT_TRUE(subnet_->inPool(exp_type, lease->addr_));
        } else {
            EXPECT_FALSE(subnet_->inPool(exp_type, lease->addr_));
        }

        // that it have proper parameters
        EXPECT_EQ(exp_type, lease->type_);
        EXPECT_EQ(iaid_, lease->iaid_);
        if (subnet_->getValid().getMin() == subnet_->getValid().getMax()) {
            EXPECT_EQ(subnet_->getValid(), lease->valid_lft_);
        } else {
            EXPECT_LE(subnet_->getValid().getMin(), lease->valid_lft_);
            EXPECT_GE(subnet_->getValid().getMax(), lease->valid_lft_);
        }
        if (subnet_->getPreferred().getMin() == subnet_->getPreferred().getMax()) {
            EXPECT_EQ(subnet_->getPreferred(), lease->preferred_lft_);
        } else {
            EXPECT_LE(subnet_->getPreferred().getMin(), lease->preferred_lft_);
            EXPECT_GE(subnet_->getPreferred().getMax(), lease->preferred_lft_);
        }
        EXPECT_EQ(exp_pd_len, lease->prefixlen_);
        EXPECT_EQ(fqdn_fwd_, lease->fqdn_fwd_);
        EXPECT_EQ(fqdn_rev_, lease->fqdn_rev_);
        EXPECT_EQ(hostname_, lease->hostname_);
        EXPECT_TRUE(*lease->duid_ == *duid);
        EXPECT_EQ(0, lease->reuseable_valid_lft_);
        /// @todo: check cltt
    }

    /// @brief Checks if specified address or prefix has been recorded as
    /// allocated to the client.
    ///
    /// @param lease Allocated lease.
    /// @param ctx Context structure in which this function should check if
    /// leased address is stored as allocated resource.
    void checkAllocatedResources(const Lease6Ptr& lease,
                                 AllocEngine::ClientContext6& ctx) {<--- Parameter 'ctx' can be declared as reference to const
        EXPECT_TRUE(ctx.isAllocated(lease->addr_, lease->prefixlen_));
    }

    /// @brief Checks if specified address is increased properly
    ///
    /// Method uses gtest macros to mark check failure. This is a proxy
    /// method, since increaseAddress was moved to IOAddress class.
    ///
    /// @param alloc IterativeAllocator that is tested
    /// @param input address to be increased
    /// @param exp_output expected address after increase
    void checkAddrIncrease(NakedIterativeAllocator& alloc,
                           std::string input,<--- Function parameter 'input' should be passed by const reference.
                           std::string exp_output) {
        EXPECT_EQ(exp_output, alloc.increaseAddress(asiolink::IOAddress(input),
                                                    false, 0).toText());
    }

    /// @brief Checks if increasePrefix() works as expected
    ///
    /// Method uses gtest macros to mark check failure.
    ///
    /// @param alloc allocator to be tested
    /// @param input IPv6 prefix (as a string)
    /// @param prefix_len prefix len
    /// @param exp_output expected output (string)
    void checkPrefixIncrease(NakedIterativeAllocator& alloc,
                             std::string input,<--- Function parameter 'input' should be passed by const reference.
                             uint8_t prefix_len,
                             std::string exp_output) {
        EXPECT_EQ(exp_output,
                  alloc.increasePrefix(asiolink::IOAddress(input),
                                       prefix_len).toText());
    }

    /// @brief Checks if the simple allocation can succeed
    ///
    /// The type of lease is determined by pool type (pool->getType())
    ///
    /// @param pool pool from which the lease will be allocated from
    /// @param hint address to be used as a hint
    /// @param fake true - this is fake allocation (SOLICIT)
    /// @param in_pool specifies whether the lease is expected to be in pool
    ///
    /// @return allocated lease (or NULL)
    Lease6Ptr simpleAlloc6Test(const Pool6Ptr& pool,
                               const asiolink::IOAddress& hint,
                               bool fake,
                               bool in_pool = true);

    /// @brief Checks if the simple allocation can succeed with lifetimes.
    ///
    /// The type of lease is determined by pool type (pool->getType())
    ///
    /// @param pool pool from which the lease will be allocated from
    /// @param hint address to be used as a hint
    /// @param preferred preferred lifetime to be used as a hint
    /// @param valid valid lifetime to be used as a hint
    /// @param exp_preferred expected lease preferred lifetime
    /// @param exp_valid expected lease valid lifetime
    /// @param class_def class definition to add to the context
    ///
    /// @return allocated lease (or NULL)
    Lease6Ptr simpleAlloc6Test(const Pool6Ptr& pool,
                               const asiolink::IOAddress& hint,
                               uint32_t preferred,
                               uint32_t valid,
                               uint32_t exp_preferred,
                               uint32_t exp_valid,
                               ClientClassDefPtr class_def = ClientClassDefPtr());

    /// @brief Checks if the simple allocation can succeed for custom DUID.
    ///
    /// The type of lease is determined by pool type (pool->getType())
    ///
    /// @param pool pool from which the lease will be allocated from
    /// @param duid pointer to the DUID used for allocation.
    /// @param hint address to be used as a hint
    /// @param fake true - this is fake allocation (SOLICIT)
    /// @param in_pool specifies whether the lease is expected to be in pool
    ///
    /// @return allocated lease (or NULL)
    Lease6Ptr simpleAlloc6Test(const Pool6Ptr& pool,
                               const DuidPtr& duid,
                               const asiolink::IOAddress& hint,
                               bool fake,
                               bool in_pool = true);

    /// @brief Checks if the allocation can succeed.
    ///
    /// The type of lease is determined by pool type (pool->getType()).
    /// This test is particularly useful in connection with @ref renewTest.
    ///
    /// @param engine a reference to Allocation Engine
    /// @param pool pool from which the lease will be allocated from
    /// @param hint address to be used as a hint
    /// @param fake true - this is fake allocation (SOLICIT)
    /// @param in_pool specifies whether the lease is expected to be in pool
    /// @param hint_prefix_length The hint prefix length that the client
    /// provided.
    ///
    /// @return allocated lease(s) (may be empty)
    Lease6Collection allocateTest(AllocEngine& engine,
                                  const Pool6Ptr& pool,
                                  const asiolink::IOAddress& hint,
                                  bool fake,
                                  bool in_pool = true,
                                  uint8_t hint_prefix_length = 128);

    /// @brief Checks if the allocation can be renewed.
    ///
    /// The type of lease is determined by pool type (pool->getType()).
    /// This test is particularly useful as a follow up to @ref allocateTest.
    ///
    /// @param engine a reference to Allocation Engine
    /// @param pool pool from which the lease will be allocated from
    /// @param hints address to be used as a hint
    /// @param in_subnet whether the lease is expected to be in subnet
    /// @param in_pool specifies whether the lease is expected to be in pool
    ///
    /// @return allocated lease(s) (may be empty)
    Lease6Collection renewTest(AllocEngine& engine,
                               const Pool6Ptr& pool,
                               AllocEngine::HintContainer& hints,
                               bool in_subnet,
                               bool in_pool);

    /// @brief Checks if the address allocation with a hint that is in range,
    ///        in pool, but is currently used, can succeed
    ///
    /// Method uses gtest macros to mark check failure.
    ///
    /// @param type lease type
    /// @param used_addr address should be preallocated (simulates prior
    ///        allocation by some other user)
    /// @param requested address requested by the client
    /// @param expected_pd_len expected PD len (128 for addresses)
    void allocWithUsedHintTest(Lease::Type type,
                               asiolink::IOAddress used_addr,
                               asiolink::IOAddress requested,
                               uint8_t expected_pd_len);

    /// @brief Generic test used for IPv6 lease allocation and reuse
    ///
    /// This test inserts existing_lease (if specified, may be null) into the
    /// LeaseMgr, then conducts lease allocation (pretends that client
    /// sent either Solicit or Request, depending on fake_allocation).
    /// Allocated lease is then returned (using result) for further inspection.
    ///
    /// @param alloc_engine allocation engine
    /// @param existing_lease optional lease to be inserted in the database
    /// @param addr address to be requested by client
    /// @param fake_allocation true = SOLICIT, false = REQUEST
    /// @param exp_result expected result
    /// @param result [out] allocated lease
    void testReuseLease6(const AllocEnginePtr& alloc_engine,
                         Lease6Ptr& existing_lease,
                         const std::string& addr,
                         const bool fake_allocation,
                         ExpectedResult exp_result,
                         Lease6Ptr& result);

    /// @brief Creates a declined IPv6 lease with specified expiration time
    ///
    /// expired parameter controls probation period. Positive value
    /// means that the lease will expire in X seconds. Negative means
    /// that the lease expired X seconds ago. 0 means it expires now.
    /// Probation period is a parameter that specifies for how long
    /// a lease will stay unavailable after decline.
    ///
    /// @param addr address of the lease
    /// @param probation_period expressed in seconds
    /// @param expired number of seconds when the lease will expire
    ///
    /// @return generated lease
    Lease6Ptr generateDeclinedLease(const std::string& addr,
                                    time_t probation_period,
                                    int32_t expired);

    /// @brief checks if bogus hint can be ignored and the allocation succeeds
    ///
    /// This test checks if the allocation with a hing that is out of the blue
    /// can succeed. The invalid hint should be ignored completely.
    ///
    /// @param type Lease type
    /// @param hint hint (as sent by a client)
    /// @param expected_pd_len (used in validation)
    void allocBogusHint6(Lease::Type type, asiolink::IOAddress hint,
                         uint8_t expected_pd_len);

    /// @brief Utility function that creates a host reservation (duid)
    ///
    /// @param add_to_host_mgr true if the reservation should be added
    /// @param type specifies reservation type
    /// @param addr specifies reserved address or prefix
    /// @param prefix_len prefix length (should be 128 for addresses)
    ///
    /// @return created Host object.
    HostPtr createHost6(bool add_to_host_mgr,
                        IPv6Resrv::Type type,
                        const asiolink::IOAddress& addr,
                        uint8_t prefix_len) {
        HostPtr host(new Host(&duid_->getDuid()[0], duid_->getDuid().size(), Host::IDENT_DUID,
                              SUBNET_ID_UNUSED, subnet_->getID(), asiolink::IOAddress("0.0.0.0")));
        IPv6Resrv resv(type, addr, prefix_len);
        host->addReservation(resv);

        if (add_to_host_mgr) {
            addHost(host);
        }

        return (host);
    }

    /// @brief Add a host reservation to the current configuration
    ///
    /// Adds the given host reservation to the current configuration by
    /// casting it to non-const.  We do it this way rather than adding it to
    /// staging and then committing as that wipes out the current configuration
    /// such as subnets.
    ///
    /// @param host host reservation to add
    void addHost(HostPtr& host) {
        SrvConfigPtr cfg = boost::const_pointer_cast<SrvConfig>(CfgMgr::instance().getCurrentCfg());
        cfg->getCfgHosts()->add(host);
    }

    /// @brief Utility function that creates a host reservation (hwaddr)
    ///
    /// @param add_to_host_mgr true if the reservation should be added
    /// @param type specifies reservation type
    /// @param hwaddr hardware address to be reserved to
    /// @param addr specifies reserved address or prefix
    /// @param prefix_len prefix length (should be 128 for addresses)
    ///
    /// @return created Host object.
    HostPtr createHost6HWAddr(bool add_to_host_mgr,
                              IPv6Resrv::Type type,
                              HWAddrPtr& hwaddr,
                              const asiolink::IOAddress& addr,
                              uint8_t prefix_len);

    /// @brief Utility function that decrements cltt of a persisted lease
    ///
    /// This function is used to simulate the passage of time by decrementing
    /// the lease's cltt, currently by 1.  It fetches the desired lease from the
    /// lease manager, decrements the cltt, then updates the lease in the lease
    /// manager.  Next, it refetches the lease and verifies the update took place.
    ///
    /// @param[in][out] lease pointer reference to the lease to modify.  Upon
    /// return it will point to the newly updated lease.
    void rollbackPersistedCltt(Lease6Ptr& lease) {
        ASSERT_TRUE(lease) << "rollbackPersistedCltt lease is empty";

        // Fetch it, so we can update it.
        Lease6Ptr from_mgr = LeaseMgrFactory::instance().getLease6(lease->type_, lease->addr_);
        ASSERT_TRUE(from_mgr) << "rollbackPersistedCltt: lease not found?";

        // Decrement cltt then update it in the manager.
        --from_mgr->cltt_;
        ASSERT_NO_THROW(LeaseMgrFactory::instance().updateLease6(from_mgr));

        // Fetch it fresh.
        lease = LeaseMgrFactory::instance().getLease6(lease->type_, lease->addr_);

        // Make sure it stuck.
        ASSERT_EQ(lease->cltt_, from_mgr->cltt_);
    }

    virtual ~AllocEngine6Test() {
        factory_.destroy();
    }

    DuidPtr duid_;            ///< client-identifier (value used in tests)
    HWAddrPtr hwaddr_;        ///< client's hardware address
    uint32_t iaid_;           ///< IA identifier (value used in tests)
    Subnet6Ptr subnet_;       ///< subnet6 (used in tests)
    Pool6Ptr pool_;           ///< NA pool belonging to subnet_
    Pool6Ptr pd_pool_;        ///< PD pool belonging to subnet_
    std::string hostname_;    ///< Hostname
    bool fqdn_fwd_;           ///< Perform forward update for a lease.
    bool fqdn_rev_;           ///< Perform reverse update for a lease.
    LeaseMgrFactory factory_; ///< pointer to LeaseMgr factory
    ClientClasses cc_;        ///< client classes
};

/// @brief Used in Allocation Engine tests for IPv4
class AllocEngine4Test : public ::testing::Test {
public:

    /// @brief Specified expected result of a given operation
    enum ExpectedResult {
        SHOULD_PASS,
        SHOULD_FAIL
    };

    /// @brief Default constructor
    ///
    /// Sets clientid_, hwaddr_, subnet_, pool_ fields to example values
    /// used in many tests, initializes cfg_mgr configuration and creates
    /// lease database.
    ///
    /// It also re-initializes the Host Manager.
    AllocEngine4Test();

    /// @brief checks if Lease4 matches expected configuration
    ///
    /// @param lease lease to be checked
    void checkLease4(const Lease4Ptr& lease) {
        // Check that is belongs to the right subnet
        EXPECT_EQ(lease->subnet_id_, subnet_->getID());
        EXPECT_TRUE(subnet_->inRange(lease->addr_));
        EXPECT_TRUE(subnet_->inPool(Lease::TYPE_V4, lease->addr_));

        // Check that it has proper parameters
        if (subnet_->getValid().getMin() == subnet_->getValid().getMax()) {
            EXPECT_EQ(subnet_->getValid(), lease->valid_lft_);
        } else {
            EXPECT_LE(subnet_->getValid().getMin(), lease->valid_lft_);
            EXPECT_GE(subnet_->getValid().getMax(), lease->valid_lft_);
        }
        if (lease->client_id_ && !clientid_) {
            ADD_FAILURE() << "Lease4 has a client-id, while it should have none.";
        } else if (!lease->client_id_ && clientid_) {
            ADD_FAILURE() << "Lease4 has no client-id, but it was expected to have one.";
        } else if (lease->client_id_ && clientid_) {
            EXPECT_TRUE(*lease->client_id_ == *clientid_);
        }
        EXPECT_TRUE(*lease->hwaddr_ == *hwaddr_);
        EXPECT_EQ(0, lease->reuseable_valid_lft_);
        /// @todo: check cltt
    }

    /// @brief Generic test used for IPv4 lease allocation and reuse
    ///
    /// This test inserts existing_lease (if specified, may be null) into the
    /// LeaseMgr, then conducts lease allocation (pretends that client
    /// sent either Discover or Request, depending on fake_allocation).
    /// Allocated lease is then returned (using result) for further inspection.
    ///
    /// @param alloc_engine allocation engine
    /// @param existing_lease optional lease to be inserted in the database
    /// @param addr address to be requested by client
    /// @param fake_allocation true = DISCOVER, false = REQUEST
    /// @param exp_result expected result
    /// @param result [out] allocated lease
    void testReuseLease4(const AllocEnginePtr& alloc_engine,
                         Lease4Ptr& existing_lease,
                         const std::string& addr,
                         const bool fake_allocation,
                         ExpectedResult exp_result,
                         Lease4Ptr& result);

    /// @brief Creates a declined IPv4 lease with specified expiration time
    ///
    /// expired parameter controls probation period. Positive value
    /// means that the lease will expire in X seconds. Negative means
    /// that the lease expired X seconds ago. 0 means it expires now.
    /// Probation period is a parameter that specifies for how long
    /// a lease will stay unavailable after decline.
    ///
    /// @param addr address of the lease
    /// @param probation_period expressed in seconds
    /// @param expired number of seconds when the lease will expire
    ///
    /// @return generated lease
    Lease4Ptr generateDeclinedLease(const std::string& addr,
                                    time_t probation_period,
                                    int32_t expired);

    /// @brief Create a subnet with a specified pool of addresses.
    ///
    /// @param pool_start First address in the pool.
    /// @param pool_end Last address in the pool.
    void initSubnet(const asiolink::IOAddress& pool_start,
                    const asiolink::IOAddress& pool_end);

    /// @brief Default constructor
    virtual ~AllocEngine4Test() {
        factory_.destroy();
    }

    ClientIdPtr clientid_;            ///< Client-identifier (value used in tests)
    ClientIdPtr clientid2_;           ///< Alternative client-identifier.
    HWAddrPtr hwaddr_;                ///< Hardware address (value used in tests)
    HWAddrPtr hwaddr2_;               ///< Alternative hardware address.
    Subnet4Ptr subnet_;               ///< Subnet4 (used in tests)
    Pool4Ptr pool_;                   ///< Pool belonging to subnet_
    LeaseMgrFactory factory_;         ///< Pointer to LeaseMgr factory
    AllocEngine::ClientContext4 ctx_; ///< Context information passed to various
    ClientClasses cc_;                ///< Client classes
                                      ///< allocation engine functions.
};

} // namespace test
} // namespace dhcp
} // namespace isc

#endif