text:
//
// boost/radix/detail/ibitstream.hpp
//
// Copyright (c) Chris Glover, 2017-2018
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//

#ifndef BOOST_RADIX_DETAIL_IBITSTREAM_HPP
#define BOOST_RADIX_DETAIL_IBITSTREAM_HPP

#include <boost/radix/common.hpp>

#include <algorithm>
#include <boost/align/align_up.hpp>
#include <boost/assert.hpp>
#include <boost/static_assert.hpp>
#include <boost/type_traits/is_integral.hpp>
#include <boost/type_traits/make_unsigned.hpp>

#ifdef BOOST_HAS_PRAGMA_ONCE
#    pragma once
#endif

namespace boost { namespace radix { namespace detail {

/// \class ibitstream
template <typename InputIterator>
class ibitstream
{
public:
    ibitstream(InputIterator first, InputIterator last)
        : current_(first)
        , end_(last)
        , bit_(0)
    {}

    template <typename ValueType, typename BitReader>
    void read_bits(ValueType& v, BitReader reader)
    {
        BOOST_ASSERT(reader.num_bits() <= sizeof(v) * 8);
        BOOST_ASSERT(reader.num_bits() > 0);

        typedef typename std::iterator_traits<InputIterator>::value_type
            buffer_element_type;
        typedef typename boost::make_unsigned<buffer_element_type>::type
            unsigned_buffer_element_type;

        // For now, we only support bytes.
        BOOST_STATIC_ASSERT(sizeof(buffer_element_type) == 1);

        std::size_t const element_bits = sizeof(buffer_element_type) * 8;

        // Extract whatever is left in the current element.
        v = reader(*current_, bit_);

        std::size_t bits_extracted = element_bits - bit_;

        if(bits_extracted == reader.num_bits())
        {
            ++current_;
            bit_ = 0;
            return;
        }
        else if(bits_extracted < reader.num_bits())
        {
            std::size_t bits_remaining = reader.num_bits() - bits_extracted;
            std::size_t aligned_bits_remaining =
                boost::alignment::align_up(bits_remaining, element_bits);
            std::size_t elements_remaining =
                aligned_bits_remaining / element_bits;
            BOOST_ASSERT(elements_remaining <= sizeof(v));

            // cglover-note: There's opportunity for optimization here if we
            // know the endiness of the machine.
            while(elements_remaining-- && (++current_, true) && !finished())
            {
                ValueType rest = reader(*current_);
                v |= rest << bits_extracted;
                bits_extracted += element_bits;
            }
        }

        ValueType all_value_bit_mask = 0;
        all_value_bit_mask           = ~all_value_bit_mask;

        // Mask off any overhang we didn't ask for.
        v &= ~(all_value_bit_mask << reader.num_bits());

        // Reset the bit offset to account for overhang.
        bit_ += reader.num_bits();
        while(bit_ >= 8)
        {
            bit_ -= 8;
        }

        if(!finished() && bit_ == 0)
        {
            ++current_;
        }
    }

    bool finished() const
    {
        return current_ == end_;
    }

private:
    BOOST_STATIC_ASSERT(
        boost::is_integral<
            typename std::iterator_traits<InputIterator>::value_type>::value);

    InputIterator current_;
    InputIterator end_;
    std::size_t bit_;
};

class ibitstream_le
{
public:
    template <typename Iterator>
    ibitstream_le(Iterator& current)
        : byte_(*current++)
        , bit_(0)
    {}

    template <typename ValueType, typename Iterator, typename BitReader>
    void
    read_bits(ValueType& v, Iterator& next, Iterator last, BitReader reader)
    {
        BOOST_ASSERT(reader.num_bits() <= sizeof(v) * 8);
        BOOST_ASSERT(reader.num_bits() > 0);

        std::size_t const element_bits = 8;

        v = reader(byte_, bit_);

        std::size_t bits_extracted = element_bits - bit_;

        //// Extract whatever is left in the current element.
        // v = reader(byte_, bit_);
        //
        // std::size_t bits_extracted = element_bits - bit_;

        // if(bits_extracted == reader.num_bits())
        //{
        //    byte_ = *current_++;
        //    bit_ = 0;
        //    return;
        //}
        // else if(bits_extracted < reader.num_bits())
        //{
        //    std::size_t bits_remaining = reader.num_bits() - bits_extracted;
        //    std::size_t aligned_bits_remaining =
        //    boost::alignment::align_up(bits_remaining, element_bits);
        //    std::size_t elements_remaining = aligned_bits_remaining /
        //    element_bits; BOOST_ASSERT(elements_remaining <= sizeof(v));
        //
        //    // cglover-note: There's opportunity for optimization here if we
        //    know
        //    // the endiness of the machine.
        //    while(elements_remaining-- && (++current_, true) && (current !=
        //    last))
        //    {
        //        ValueType rest = reader(*current_);
        //        v |= rest << bits_extracted;
        //        bits_extracted += element_bits;
        //    }
        //}

        // ValueType all_value_bit_mask = 0;
        // all_value_bit_mask = ~all_value_bit_mask;

        //// Mask off any overhang we didn't ask for.
        // v &= ~(all_value_bit_mask << reader.num_bits());

        //// Reset the bit offset to account for overhang.
        // bit_ += reader.num_bits();
        // while(bit_ >= 8)
        //{
        //    bit_ -= 8;
        //}

        // if((current != last) && bit_ == 0)
        //{
        //    byte_ = *current++;
        //
        //}
    }

    // bool finished() const
    //{
    //    return current_ == end_;
    //}

private:
    bits_type byte_;
    std::size_t bit_;
};

class dynamic_bit_reader
{
public:
    dynamic_bit_reader(std::size_t num_bits)
        : num_bits_(num_bits)
    {}

    template <typename SourceType>
    typename boost::make_unsigned<SourceType>::type
    operator()(SourceType const& source, std::size_t source_bit_offset) const
    {
        return boost::make_unsigned<SourceType>::type(source) >>
               source_bit_offset;
    }

    template <typename SourceType>
    typename boost::make_unsigned<SourceType>::type
    operator()(SourceType const& source) const
    {
        return boost::make_unsigned<SourceType>::type(source);
    }

    std::size_t num_bits() const
    {
        return num_bits_;
    }

private:
    std::size_t num_bits_;
};

template <std::size_t Bits>
class static_bit_reader
{
public:
    template <typename SourceType>
    typename boost::make_unsigned<SourceType>::type
    operator()(SourceType const& source, std::size_t source_bit_offset) const
    {
        return boost::make_unsigned<SourceType>::type(source) >>
               source_bit_offset;
    }

    template <typename SourceType>
    typename boost::make_unsigned<SourceType>::type
    operator()(SourceType const& source) const
    {
        return boost::make_unsigned<SourceType>::type(source);
    }

    std::size_t num_bits() const
    {
        return Bits;
    }
};

}}} // namespace boost::radix::detail

#endif // BOOST_RADIX_DETAIL_IBITSTREAM_HPP


text:
/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */

/*
 Copyright (C) 2005, 2016 Klaus Spanderen

 This file is part of QuantLib, a free-software/open-source library
 for financial quantitative analysts and developers - http://quantlib.org/

 QuantLib is free software: you can redistribute it and/or modify it
 under the terms of the QuantLib license.  You should have received a
 copy of the license along with this program; if not, please email
 <quantlib-dev@lists.sf.net>. The license is also available online at
 <http://quantlib.org/license.shtml>.

 This program is distributed in the hope that it will be useful, but WITHOUT
 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 FOR A PARTICULAR PURPOSE.  See the license for more details.
*/

#include "gaussianquadratures.hpp"
#include "utilities.hpp"

#include <ql/types.hpp>
#include <ql/math/matrix.hpp>
#include <ql/math/functional.hpp>
#include <ql/math/distributions/normaldistribution.hpp>
#include <ql/math/integrals/gaussianquadratures.hpp>
#include <ql/math/integrals/momentbasedgaussianpolynomial.hpp>
#include <ql/math/integrals/gausslaguerrecosinepolynomial.hpp>
#include <ql/experimental/math/gaussiannoncentralchisquaredpolynomial.hpp>

#include <boost/math/distributions/non_central_chi_squared.hpp>

#ifndef TEST_BOOST_MULTIPRECISION_GAUSSIAN_QUADRATURE
//#define TEST_BOOST_MULTIPRECISION_GAUSSIAN_QUADRATURE
#endif

#ifdef TEST_BOOST_MULTIPRECISION_GAUSSIAN_QUADRATURE
    #if BOOST_VERSION < 105300
        #error This boost version is too old to support boost multi precision
    #endif

    #include <boost/multiprecision/cpp_dec_float.hpp>
#endif

using namespace QuantLib;
using namespace boost::unit_test_framework;

namespace gaussian_quadratures_test {

    template <class T>
    void testSingle(const T& I, const std::string& tag,
                    const boost::function<Real(Real)>& f, Real expected) {
        Real calculated = I(f);
        if (std::fabs(calculated-expected) > 1.0e-4) {
            BOOST_ERROR("integrating" << tag << "\n"
                        << "    calculated: " << calculated << "\n"
                        << "    expected:   " << expected);
        }
    }

    // test functions

    Real inv_exp(Real x) {
        return std::exp(-x);
    }

    Real x_inv_exp(Real x) {
        return x*std::exp(-x);
    }

    Real x_normaldistribution(Real x) {
        return x*NormalDistribution()(x);
    }

    Real x_x_normaldistribution(Real x) {
        return x*x*NormalDistribution()(x);
    }

    Real inv_cosh(Real x) {
        return 1/std::cosh(x);
    }

    Real x_inv_cosh(Real x) {
        return x/std::cosh(x);
    }

    Real x_x_nonCentralChiSquared(Real x) {
        return x * x * boost::math::pdf(
            boost::math::non_central_chi_squared_distribution<Real>(4.0,1.0),x);
    }

    Real x_sin_exp_nonCentralChiSquared(Real x) {
        return x * std::sin(0.1*x) * std::exp(0.3*x) * boost::math::pdf(
            boost::math::non_central_chi_squared_distribution<Real>(1.0,1.0),x);
    }

    template <class T>
    void testSingleJacobi(const T& I) {
        testSingle(I, "f(x) = 1",
                   constant<Real,Real>(1.0), 2.0);
        testSingle(I, "f(x) = x",
                   identity<Real>(),         0.0);
        testSingle(I, "f(x) = x^2",
                   square<Real>(),           2/3.);
        testSingle(I, "f(x) = sin(x)",
                   static_cast<Real(*)(Real)>(std::sin), 0.0);
        testSingle(I, "f(x) = cos(x)",
                   static_cast<Real(*)(Real)>(std::cos),
                   std::sin(1.0)-std::sin(-1.0));
        testSingle(I, "f(x) = Gaussian(x)",
                   NormalDistribution(),
                   CumulativeNormalDistribution()(1.0)
                   -CumulativeNormalDistribution()(-1.0));
    }

    template <class T>
    void testSingleLaguerre(const T& I) {
        testSingle(I, "f(x) = exp(-x)",
                   inv_exp, 1.0);
        testSingle(I, "f(x) = x*exp(-x)",
                   x_inv_exp, 1.0);
        testSingle(I, "f(x) = Gaussian(x)",
                   NormalDistribution(), 0.5);
    }

    void testSingleTabulated(const boost::function<Real(Real)>& f,
                             const std::string& tag,
                             Real expected, Real tolerance) {
        const Size order[] = { 6, 7, 12, 20 };
        TabulatedGaussLegendre quad;
        for (Size i=0; i<LENGTH(order); i++) {
            quad.order(order[i]);
            Real realised = quad(f);
            if (std::fabs(realised-expected) > tolerance) {
                BOOST_ERROR(" integrating " << tag << "\n"
                            << "    order " << order[i] << "\n"
                            << "    realised: " << realised << "\n"
                            << "    expected: " << expected);
            }
        }
    }

}

void GaussianQuadraturesTest::testJacobi() {
    BOOST_TEST_MESSAGE("Testing Gauss-Jacobi integration...");

    using namespace gaussian_quadratures_test;

    testSingleJacobi(GaussLegendreIntegration(16));
    testSingleJacobi(GaussChebyshevIntegration(130));
    testSingleJacobi(GaussChebyshev2ndIntegration(130));
    testSingleJacobi(GaussGegenbauerIntegration(50,0.55));
}

void GaussianQuadraturesTest::testLaguerre() {
     BOOST_TEST_MESSAGE("Testing Gauss-Laguerre integration...");

     using namespace gaussian_quadratures_test;

     testSingleLaguerre(GaussLaguerreIntegration(16));
     testSingleLaguerre(GaussLaguerreIntegration(150,0.01));

     testSingle(GaussLaguerreIntegration(16, 1.0), "f(x) = x*exp(-x)",
                x_inv_exp, 1.0);
     testSingle(GaussLaguerreIntegration(32, 0.9), "f(x) = x*exp(-x)",
                x_inv_exp, 1.0);
}

void GaussianQuadraturesTest::testHermite() {
     BOOST_TEST_MESSAGE("Testing Gauss-Hermite integration...");

     using namespace gaussian_quadratures_test;

     testSingle(GaussHermiteIntegration(16), "f(x) = Gaussian(x)",
                NormalDistribution(), 1.0);
     testSingle(GaussHermiteIntegration(16,0.5), "f(x) = x*Gaussian(x)",
                x_normaldistribution, 0.0);
     testSingle(GaussHermiteIntegration(64,0.9), "f(x) = x*x*Gaussian(x)",
                x_x_normaldistribution, 1.0);
}

void GaussianQuadraturesTest::testHyperbolic() {
     BOOST_TEST_MESSAGE("Testing Gauss hyperbolic integration...");

     using namespace gaussian_quadratures_test;

     testSingle(GaussHyperbolicIntegration(16), "f(x) = 1/cosh(x)",
                inv_cosh, M_PI);
     testSingle(GaussHyperbolicIntegration(16), "f(x) = x/cosh(x)",
                x_inv_cosh, 0.0);
}

void GaussianQuadraturesTest::testTabulated() {
     BOOST_TEST_MESSAGE("Testing tabulated Gauss-Laguerre integration...");

     using namespace gaussian_quadratures_test;

     testSingleTabulated(constant<Real,Real>(1.0), "f(x) = 1",
                         2.0,       1.0e-13);
     testSingleTabulated(identity<Real>(), "f(x) = x",
                         0.0,       1.0e-13);
     testSingleTabulated(square<Real>(), "f(x) = x^2",
                         (2.0/3.0), 1.0e-13);
     testSingleTabulated(cube<Real>(), "f(x) = x^3",
                         0.0,       1.0e-13);
     testSingleTabulated(fourth_power<Real>(), "f(x) = x^4",
                         (2.0/5.0), 1.0e-13);
}

void GaussianQuadraturesTest::testNonCentralChiSquared() {
     BOOST_TEST_MESSAGE(
         "Testing Gauss non-central chi-squared integration...");

     using namespace gaussian_quadratures_test;

     testSingle(
        GaussianQuadrature(2, GaussNonCentralChiSquaredPolynomial(4.0, 1.0)),
        "f(x) = x^2 * nonCentralChiSquared(4, 1)(x)",
        x_x_nonCentralChiSquared, 37.0);

     testSingle(
        GaussianQuadrature(14, GaussNonCentralChiSquaredPolynomial(1.0, 1.0)),
        "f(x) = x * sin(0.1*x)*exp(0.3*x)*nonCentralChiSquared(1, 1)(x)",
        x_sin_exp_nonCentralChiSquared, 17.408092);
}

void GaussianQuadraturesTest::testNonCentralChiSquaredSumOfNodes() {
     BOOST_TEST_MESSAGE(
         "Testing Gauss non-central chi-squared sum of nodes...");

     using namespace gaussian_quadratures_test;

     // Walter Gautschi, How and How not to check Gaussian Quadrature Formulae
     // https://www.cs.purdue.edu/homes/wxg/selected_works/section_08/084.pdf

     // Expected results have been calculated with a multi precision library
     // following the description of test #4 in the paper above.
     // Using QuantLib's own determinant function will not work here
     // as it supports only double precision.

     const Real expected[] = {
         47.53491786730293,
         70.6103295419633383,
         98.0593406849441607,
         129.853401537905341,
         165.96963582663912,
         206.389183233992043
     };

     const Real nu=4.0;
     const Real lambda=1.0;
     const GaussNonCentralChiSquaredPolynomial orthPoly(nu, lambda);

     const Real tol = 1e-5;

	 for (Size n = 4; n < 10; ++n) {
		 const Array x = GaussianQuadrature(n, orthPoly).x();
         const Real calculated = std::accumulate(x.begin(), x.end(), 0.0);

         if (std::fabs(calculated - expected[n-4]) > tol) {
             BOOST_ERROR("failed to reproduce rule of sum"
                         << "\n    calculated: " << calculated
                         << "\n    expected:   " << expected[n-4]
                         << "\n    diff    :   " << calculated - expected[n-4]);
         }
     }
}

namespace gaussian_quadratures_test {
    template <class mp_float>
    class MomentBasedGaussLaguerrePolynomial
            : public MomentBasedGaussianPolynomial<mp_float> {
      public:
        mp_float moment(Size i) const {
            if (i == 0)
                return mp_float(1.0);
            else
                return mp_float(i)*moment(i-1);
        }

        Real w(Real x) const {
            return std::exp(-x);
        }
    };
}

void GaussianQuadraturesTest::testMomentBasedGaussianPolynomial() {
     BOOST_TEST_MESSAGE("Testing moment based Gaussian polynomials...");

     using namespace gaussian_quadratures_test;

     GaussLaguerrePolynomial g;

     std::vector<ext::shared_ptr<GaussianOrthogonalPolynomial> > ml;
     ml.push_back(
         ext::make_shared<MomentBasedGaussLaguerrePolynomial<Real> >());

#ifdef TEST_BOOST_MULTIPRECISION_GAUSSIAN_QUADRATURE
     ml.push_back(
         ext::make_shared<MomentBasedGaussLaguerrePolynomial<
             boost::multiprecision::number<
                 boost::multiprecision::cpp_dec_float<20> > > >());
#endif

     const Real tol = 1e-12;
     for (Size k=0; k < ml.size(); ++k) {

         for (Size i=0; i < 10; ++i) {
             const Real diffAlpha = std::fabs(ml[k]->alpha(i)-g.alpha(i));
             const Real diffBeta = std::fabs(ml[k]->beta(i)-g.beta(i));

             if (diffAlpha > tol) {
                 BOOST_ERROR("failed to reproduce alpha for Laguerre quadrature"
                             << "\n    calculated: " << ml[k]->alpha(i)
                             << "\n    expected  : " << g.alpha(i)
                             << "\n    diff      : " << diffAlpha);
             }
             if (i > 0 && diffBeta > tol) {
                 BOOST_ERROR("failed to reproduce beta for Laguerre quadrature"
                             << "\n    calculated: " << ml[k]->beta(i)
                             << "\n    expected  : " << g.beta(i)
                             << "\n    diff      : " << diffBeta);
             }
         }
     }
}

void GaussianQuadraturesTest::testGaussLaguerreCosinePolynomial() {
    BOOST_TEST_MESSAGE("Testing Gauss-Laguerre-Cosine quadrature...");

    using namespace gaussian_quadratures_test;

    const GaussianQuadrature quadCosine(
            16, GaussLaguerreCosinePolynomial<Real>(0.2));

    testSingle(quadCosine, "f(x) = exp(-x)",
               inv_exp, 1.0);
    testSingle(quadCosine, "f(x) = x*exp(-x)",
               x_inv_exp, 1.0);

    const GaussianQuadrature quadSine(
            16, GaussLaguerreSinePolynomial<Real>(0.2));

    testSingle(quadSine, "f(x) = exp(-x)",
               inv_exp, 1.0);
    testSingle(quadSine, "f(x) = x*exp(-x)",
               x_inv_exp, 1.0);
}

test_suite* GaussianQuadraturesTest::suite() {
    test_suite* suite = BOOST_TEST_SUITE("Gaussian quadratures tests");
    suite->add(QUANTLIB_TEST_CASE(&GaussianQuadraturesTest::testJacobi));
    suite->add(QUANTLIB_TEST_CASE(&GaussianQuadraturesTest::testLaguerre));
    suite->add(QUANTLIB_TEST_CASE(&GaussianQuadraturesTest::testHermite));
    suite->add(QUANTLIB_TEST_CASE(&GaussianQuadraturesTest::testHyperbolic));
    suite->add(QUANTLIB_TEST_CASE(&GaussianQuadraturesTest::testTabulated));
    suite->add(QUANTLIB_TEST_CASE(
        &GaussianQuadraturesTest::testMomentBasedGaussianPolynomial));
    suite->add(QUANTLIB_TEST_CASE(
        &GaussianQuadraturesTest::testGaussLaguerreCosinePolynomial));

    return suite;
}

test_suite* GaussianQuadraturesTest::experimental() {
    test_suite* suite = BOOST_TEST_SUITE(
        "Gaussian quadratures experimental tests");

    suite->add(QUANTLIB_TEST_CASE(
        &GaussianQuadraturesTest::testNonCentralChiSquared));
    suite->add(QUANTLIB_TEST_CASE(
        &GaussianQuadraturesTest::testNonCentralChiSquaredSumOfNodes));

    return suite;
}

ext: cpp


text:
// Copyright (C) 2012-2019 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 <asiolink/io_address.h>
#include <dhcp/dhcp4.h>
#include <dhcp/dhcp6.h>
#include <dhcp/libdhcp++.h>
#include <dhcp/option.h>
#include <dhcp/option_custom.h>
#include <dhcp/option_definition.h>
#include <dhcp/dhcp6.h>
#include <dhcp/option_space.h>
#include <dhcpsrv/shared_network.h>
#include <dhcpsrv/subnet.h>
#include <exceptions/exceptions.h>

#include <boost/pointer_cast.hpp>
#include <boost/scoped_ptr.hpp>
#include <gtest/gtest.h>
#include <limits>

// don't import the entire boost namespace.  It will unexpectedly hide uint8_t
// for some systems.
using boost::scoped_ptr;
using namespace isc;
using namespace isc::dhcp;
using namespace isc::asiolink;

namespace {

TEST(Subnet4Test, constructor) {
    EXPECT_NO_THROW(Subnet4 subnet1(IOAddress("192.0.2.2"), 16,
                                    1, 2, 3, 10));

    EXPECT_THROW(Subnet4 subnet2(IOAddress("192.0.2.0"), 33, 1, 2, 3),
                BadValue); // invalid prefix length
    EXPECT_THROW(Subnet4 subnet3(IOAddress("2001:db8::1"), 24, 1, 2, 3),
                BadValue); // IPv6 addresses are not allowed in Subnet4
}

// This test verifies that the Subnet4 factory function creates a
// valid subnet instance.
TEST(Subnet4Test, create) {
    auto subnet = Subnet4::create(IOAddress("192.0.2.2"), 16,
                                  1, 2, 3, 10);
    ASSERT_TRUE(subnet);

    EXPECT_EQ("192.0.2.2/16", subnet->toText());
    EXPECT_EQ(1, subnet->getT1().get());
    EXPECT_EQ(2, subnet->getT2().get());
    EXPECT_EQ(3, subnet->getValid().get());
    EXPECT_EQ(10, subnet->getID());
}

// This test verifies the default values set for the subnets and verifies
// that the optional values are unspecified.
TEST(Subnet4Test, defaults) {
    Triplet<uint32_t> t1;
    Triplet<uint32_t> t2;
    Triplet<uint32_t> valid_lft;
    Subnet4 subnet(IOAddress("192.0.2.0"), 24, t1, t2, valid_lft);

    EXPECT_TRUE(subnet.getIface().unspecified());
    EXPECT_TRUE(subnet.getIface().empty());

    EXPECT_TRUE(subnet.getClientClass().unspecified());
    EXPECT_TRUE(subnet.getClientClass().empty());

    EXPECT_TRUE(subnet.getValid().unspecified());
    EXPECT_EQ(0, subnet.getValid().get());

    EXPECT_TRUE(subnet.getT1().unspecified());
    EXPECT_EQ(0, subnet.getT1().get());

    EXPECT_TRUE(subnet.getT2().unspecified());
    EXPECT_EQ(0, subnet.getT2().get());

    EXPECT_TRUE(subnet.getHostReservationMode().unspecified());
    EXPECT_EQ(Network::HR_ALL, subnet.getHostReservationMode().get());

    EXPECT_TRUE(subnet.getCalculateTeeTimes().unspecified());
    EXPECT_FALSE(subnet.getCalculateTeeTimes().get());

    EXPECT_TRUE(subnet.getT1Percent().unspecified());
    EXPECT_EQ(0.0, subnet.getT1Percent().get());

    EXPECT_TRUE(subnet.getT2Percent().unspecified());
    EXPECT_EQ(0.0, subnet.getT2Percent().get());

    EXPECT_TRUE(subnet.getMatchClientId().unspecified());
    EXPECT_TRUE(subnet.getMatchClientId().get());

    EXPECT_TRUE(subnet.getAuthoritative().unspecified());
    EXPECT_FALSE(subnet.getAuthoritative().get());

    EXPECT_TRUE(subnet.getSiaddr().unspecified());
    EXPECT_TRUE(subnet.getSiaddr().get().isV4Zero());

    EXPECT_TRUE(subnet.getSname().unspecified());
    EXPECT_TRUE(subnet.getSname().empty());

    EXPECT_TRUE(subnet.getFilename().unspecified());
    EXPECT_TRUE(subnet.getFilename().empty());

    EXPECT_FALSE(subnet.get4o6().enabled());

    EXPECT_TRUE(subnet.get4o6().getIface4o6().unspecified());
    EXPECT_TRUE(subnet.get4o6().getIface4o6().empty());

    EXPECT_TRUE(subnet.get4o6().getSubnet4o6().unspecified());
    EXPECT_TRUE(subnet.get4o6().getSubnet4o6().get().first.isV6Zero());
    EXPECT_EQ(128, subnet.get4o6().getSubnet4o6().get().second);

    EXPECT_TRUE(subnet.getDdnsSendUpdates().unspecified());
    EXPECT_FALSE(subnet.getDdnsSendUpdates().get());

    EXPECT_TRUE(subnet.getDdnsOverrideNoUpdate().unspecified());
    EXPECT_FALSE(subnet.getDdnsOverrideNoUpdate().get());

    EXPECT_TRUE(subnet.getDdnsOverrideClientUpdate().unspecified());
    EXPECT_FALSE(subnet.getDdnsOverrideClientUpdate().get());

    EXPECT_TRUE(subnet.getDdnsReplaceClientNameMode().unspecified());
    EXPECT_EQ(D2ClientConfig::RCM_NEVER, subnet.getDdnsReplaceClientNameMode().get());

    EXPECT_TRUE(subnet.getDdnsGeneratedPrefix().unspecified());
    EXPECT_TRUE(subnet.getDdnsGeneratedPrefix().empty());

    EXPECT_TRUE(subnet.getDdnsQualifyingSuffix().unspecified());
    EXPECT_TRUE(subnet.getDdnsQualifyingSuffix().empty());

    EXPECT_TRUE(subnet.getHostnameCharSet().unspecified());
    EXPECT_TRUE(subnet.getHostnameCharSet().empty());

    EXPECT_TRUE(subnet.getHostnameCharReplacement().unspecified());
    EXPECT_TRUE(subnet.getHostnameCharReplacement().empty());
}

// Checks that the subnet id can be either autogenerated or set to an
// arbitrary value through the constructor.
TEST(Subnet4Test, subnetID) {
    // Create subnet and don't specify id, so as it is autogenerated.
    Subnet4Ptr subnet(new Subnet4(IOAddress("192.0.2.0"), 24, 1000, 2000,
                                  3000));
    SubnetID id0 = subnet->getID();

    // Create another subnet and let id be autogenerated.
    subnet.reset(new Subnet4(IOAddress("192.0.3.0"), 24, 1000, 2000,
                             3000));
    SubnetID id1 = subnet->getID();

    // The autogenerated ids must not be equal.
    EXPECT_NE(id0, id1);

    // Create third subnet but this time select an arbitrary id. The id
    // we use the one of the second subnet. That way we ensure that the
    // subnet id we provide via constructor is used and it is not
    // autogenerated - if it was autogenerated we would get id other
    // than id1 because id1 has already been used.
    subnet.reset(new Subnet4(IOAddress("192.0.4.0"), 24, 1000, 2000,
                             3000, id1));
    EXPECT_EQ(id1, subnet->getID());
}

TEST(Subnet4Test, inRange) {
    Subnet4 subnet(IOAddress("192.0.2.1"), 24, 1000, 2000, 3000);

    EXPECT_EQ(1000, subnet.getT1());
    EXPECT_EQ(2000, subnet.getT2());
    EXPECT_EQ(3000, subnet.getValid());

    EXPECT_FALSE(subnet.hasRelays());

    EXPECT_FALSE(subnet.inRange(IOAddress("192.0.0.0")));
    EXPECT_TRUE(subnet.inRange(IOAddress("192.0.2.0")));
    EXPECT_TRUE(subnet.inRange(IOAddress("192.0.2.1")));
    EXPECT_TRUE(subnet.inRange(IOAddress("192.0.2.255")));
    EXPECT_FALSE(subnet.inRange(IOAddress("192.0.3.0")));
    EXPECT_FALSE(subnet.inRange(IOAddress("0.0.0.0")));
    EXPECT_FALSE(subnet.inRange(IOAddress("255.255.255.255")));
}

// Checks whether the relay list is empty by default
// and basic operations function
TEST(Subnet4Test, relay) {
    Subnet4 subnet(IOAddress("192.0.2.1"), 24, 1000, 2000, 3000);

    // Should be empty.
    EXPECT_FALSE(subnet.hasRelays());
    EXPECT_EQ(0, subnet.getRelayAddresses().size());

    // Matching should fail.
    EXPECT_FALSE(subnet.hasRelayAddress(IOAddress("192.0.123.45")));

    // Should be able to add them.
    subnet.addRelayAddress(IOAddress("192.0.123.45"));
    subnet.addRelayAddress(IOAddress("192.0.123.46"));

    // Should not be empty.
    EXPECT_TRUE(subnet.hasRelays());

    // Should be two in the list.
    EXPECT_EQ(2, subnet.getRelayAddresses().size());

    // Should be able to match them if they are there.
    EXPECT_TRUE(subnet.hasRelayAddress(IOAddress("192.0.123.45")));
    EXPECT_TRUE(subnet.hasRelayAddress(IOAddress("192.0.123.46")));

    // Should not match those that are not.
    EXPECT_FALSE(subnet.hasRelayAddress(IOAddress("192.0.123.47")));
}

// Checks whether siaddr field can be set and retrieved correctly.
TEST(Subnet4Test, siaddr) {
    Subnet4 subnet(IOAddress("192.0.2.1"), 24, 1000, 2000, 3000);

    // Check if the default is 0.0.0.0
    EXPECT_EQ("0.0.0.0", subnet.getSiaddr().get().toText());

    // Check that we can set it up
    EXPECT_NO_THROW(subnet.setSiaddr(IOAddress("1.2.3.4")));

    // Check that we can get it back
    EXPECT_EQ("1.2.3.4", subnet.getSiaddr().get().toText());

    // Check that only v4 addresses are supported
    EXPECT_THROW(subnet.setSiaddr(IOAddress("2001:db8::1")),
        BadValue);
}

// Checks whether server-hostname field can be set and retrieved correctly.
TEST(Subnet4Test, serverHostname) {
    Subnet4 subnet(IOAddress("192.0.2.1"), 24, 1000, 2000, 3000);

    // Check if the default is empty
    EXPECT_TRUE(subnet.getSname().empty());

    // Check that we can set it up
    EXPECT_NO_THROW(subnet.setSname("foobar"));

    // Check that we can get it back
    EXPECT_EQ("foobar", subnet.getSname().get());
}

// Checks whether boot-file-name field can be set and retrieved correctly.
TEST(Subnet4Test, bootFileName) {
    Subnet4 subnet(IOAddress("192.0.2.1"), 24, 1000, 2000, 3000);

    // Check if the default is empty
    EXPECT_TRUE(subnet.getFilename().empty());

    // Check that we can set it up
    EXPECT_NO_THROW(subnet.setFilename("foobar"));

    // Check that we can get it back
    EXPECT_EQ("foobar", subnet.getFilename().get());
}

// Checks if the match-client-id flag can be set and retrieved.
TEST(Subnet4Test, matchClientId) {
    Subnet4 subnet(IOAddress("192.0.2.1"), 24, 1000, 2000, 3000);

    // By default the flag should be set to true.
    EXPECT_TRUE(subnet.getMatchClientId());

    // Modify it and retrieve.
    subnet.setMatchClientId(false);
    EXPECT_FALSE(subnet.getMatchClientId());

    // Modify again.
    subnet.setMatchClientId(true);
    EXPECT_TRUE(subnet.getMatchClientId());
}

// Checks that it is possible to add and retrieve multiple pools.
TEST(Subnet4Test, pool4InSubnet4) {

    Subnet4Ptr subnet(new Subnet4(IOAddress("192.1.2.0"), 24, 1, 2, 3));

    PoolPtr pool1(new Pool4(IOAddress("192.1.2.0"), 25));
    PoolPtr pool2(new Pool4(IOAddress("192.1.2.128"), 26));
    PoolPtr pool3(new Pool4(IOAddress("192.1.2.192"), 30));
    pool3->allowClientClass("bar");
    PoolPtr pool4(new Pool4(IOAddress("192.1.2.200"), 30));

    // Add pools in reverse order to make sure that they get ordered by
    // first address.
    EXPECT_NO_THROW(subnet->addPool(pool4));

    // If there's only one pool, get that pool
    PoolPtr mypool = subnet->getAnyPool(Lease::TYPE_V4);
    EXPECT_EQ(mypool, pool4);

    EXPECT_NO_THROW(subnet->addPool(pool3));
    EXPECT_NO_THROW(subnet->addPool(pool2));
    EXPECT_NO_THROW(subnet->addPool(pool1));

    // If there are more than one pool and we didn't provide hint, we
    // should get the first pool
    EXPECT_NO_THROW(mypool = subnet->getAnyPool(Lease::TYPE_V4));

    EXPECT_EQ(mypool, pool1);

    // If we provide a hint, we should get a pool that this hint belongs to
    ASSERT_NO_THROW(mypool = subnet->getPool(Lease::TYPE_V4,
                                             IOAddress("192.1.2.201")));
    EXPECT_EQ(mypool, pool4);

    ASSERT_NO_THROW(mypool = subnet->getPool(Lease::TYPE_V4,
                                             IOAddress("192.1.2.129")));
    EXPECT_EQ(mypool, pool2);

    ASSERT_NO_THROW(mypool = subnet->getPool(Lease::TYPE_V4,
                                             IOAddress("192.1.2.64")));
    EXPECT_EQ(mypool, pool1);

    // Specify addresses which don't belong to any existing pools. The
    // third parameter prevents it from returning "any" available
    // pool if a good match is not found.
    ASSERT_NO_THROW(mypool = subnet->getPool(Lease::TYPE_V4,
                                             IOAddress("192.1.2.210"),
                                             false));
    EXPECT_FALSE(mypool);

    ASSERT_NO_THROW(mypool = subnet->getPool(Lease::TYPE_V4,
                                             IOAddress("192.1.1.254"),
                                             false));
    EXPECT_FALSE(mypool);

    // Now play with classes

    // This client does not belong to any class.
    isc::dhcp::ClientClasses no_class;

    // This client belongs to foo only.
    isc::dhcp::ClientClasses foo_class;
    foo_class.insert("foo");

    // This client belongs to bar only. I like that client.
    isc::dhcp::ClientClasses bar_class;
    bar_class.insert("bar");

    // This client belongs to foo, bar and baz classes.
    isc::dhcp::ClientClasses three_classes;
    three_classes.insert("foo");
    three_classes.insert("bar");
    three_classes.insert("baz");

    // If we provide a hint, we should get a pool that this hint belongs to
    ASSERT_NO_THROW(mypool = subnet->getPool(Lease::TYPE_V4, no_class,
                                             IOAddress("192.1.2.201")));
    EXPECT_EQ(mypool, pool4);

    ASSERT_NO_THROW(mypool = subnet->getPool(Lease::TYPE_V4, no_class,
                                             IOAddress("192.1.2.129")));
    EXPECT_EQ(mypool, pool2);

    ASSERT_NO_THROW(mypool = subnet->getPool(Lease::TYPE_V4, no_class,
                                             IOAddress("192.1.2.64")));
    EXPECT_EQ(mypool, pool1);

    // Specify addresses which don't belong to any existing pools.
    ASSERT_NO_THROW(mypool = subnet->getPool(Lease::TYPE_V4, three_classes,
                                             IOAddress("192.1.2.210")));
    EXPECT_FALSE(mypool);

    // Pool3 requires a member of bar
    ASSERT_NO_THROW(mypool = subnet->getPool(Lease::TYPE_V4, no_class,
                                             IOAddress("192.1.2.195")));
    EXPECT_FALSE(mypool);
    ASSERT_NO_THROW(mypool = subnet->getPool(Lease::TYPE_V4, foo_class,
                                             IOAddress("192.1.2.195")));
    EXPECT_FALSE(mypool);
    ASSERT_NO_THROW(mypool = subnet->getPool(Lease::TYPE_V4, bar_class,
                                             IOAddress("192.1.2.195")));
    EXPECT_EQ(mypool, pool3);
    ASSERT_NO_THROW(mypool = subnet->getPool(Lease::TYPE_V4, three_classes,
                                             IOAddress("192.1.2.195")));
    EXPECT_EQ(mypool, pool3);
}

// Check if it's possible to get specified number of possible leases for
// an IPv4 subnet.
TEST(Subnet4Test, getCapacity) {

    // There's one /24 pool.
    Subnet4Ptr subnet(new Subnet4(IOAddress("192.1.2.0"), 24, 1, 2, 3));

    // There are no pools defined, so the total number of available addrs is 0.
    EXPECT_EQ(0, subnet->getPoolCapacity(Lease::TYPE_V4));

    // Let's add a /25 pool. That's 128 addresses.
    PoolPtr pool1(new Pool4(IOAddress("192.1.2.0"), 25));
    subnet->addPool(pool1);
    EXPECT_EQ(128, subnet->getPoolCapacity(Lease::TYPE_V4));

    // Let's add another /26 pool. That's extra 64 addresses.
    PoolPtr pool2(new Pool4(IOAddress("192.1.2.128"), 26));
    subnet->addPool(pool2);
    EXPECT_EQ(192, subnet->getPoolCapacity(Lease::TYPE_V4));

    // Let's add a third pool /30. This one has 4 addresses.
    PoolPtr pool3(new Pool4(IOAddress("192.1.2.192"), 30));
    subnet->addPool(pool3);
    EXPECT_EQ(196, subnet->getPoolCapacity(Lease::TYPE_V4));

    // Let's add a forth pool /30. This one has 4 addresses.
    PoolPtr pool4(new Pool4(IOAddress("192.1.2.200"), 30));
    subnet->addPool(pool4);
    EXPECT_EQ(200, subnet->getPoolCapacity(Lease::TYPE_V4));

    // Now play with classes

    // This client does not belong to any class.
    isc::dhcp::ClientClasses no_class;

    // This client belongs to foo only.
    isc::dhcp::ClientClasses foo_class;
    foo_class.insert("foo");

    // This client belongs to bar only. I like that client.
    isc::dhcp::ClientClasses bar_class;
    bar_class.insert("bar");

    // This client belongs to foo, bar and baz classes.
    isc::dhcp::ClientClasses three_classes;
    three_classes.insert("foo");
    three_classes.insert("bar");
    three_classes.insert("baz");

    pool3->allowClientClass("bar");

    // Pool3 requires a member of bar
    EXPECT_EQ(196, subnet->getPoolCapacity(Lease::TYPE_V4, no_class));
    EXPECT_EQ(196, subnet->getPoolCapacity(Lease::TYPE_V4, foo_class));
    EXPECT_EQ(200, subnet->getPoolCapacity(Lease::TYPE_V4, bar_class));
    EXPECT_EQ(200, subnet->getPoolCapacity(Lease::TYPE_V4, three_classes));
}

// Checks that it is not allowed to add invalid pools.
TEST(Subnet4Test, pool4Checks) {

    Subnet4Ptr subnet(new Subnet4(IOAddress("192.0.2.0"), 8, 1, 2, 3));

    // this one is in subnet
    Pool4Ptr pool1(new Pool4(IOAddress("192.254.0.0"), 16));
    subnet->addPool(pool1);

    // this one is larger than the subnet!
    Pool4Ptr pool2(new Pool4(IOAddress("193.0.0.0"), 24));

    ASSERT_THROW(subnet->addPool(pool2), BadValue);

    // this one is totally out of blue
    Pool4Ptr pool3(new Pool4(IOAddress("1.2.3.4"), 16));
    ASSERT_THROW(subnet->addPool(pool3), BadValue);

    // This pool should be added just fine.
    Pool4Ptr pool4(new Pool4(IOAddress("192.0.2.10"),
                             IOAddress("192.0.2.20")));
    ASSERT_NO_THROW(subnet->addPool(pool4));

    // This one overlaps with the previous pool.
    Pool4Ptr pool5(new Pool4(IOAddress("192.0.2.1"),
                             IOAddress("192.0.2.15")));
    ASSERT_THROW(subnet->addPool(pool5), BadValue);

    // This one also overlaps.
    Pool4Ptr pool6(new Pool4(IOAddress("192.0.2.20"),
                             IOAddress("192.0.2.30")));
    ASSERT_THROW(subnet->addPool(pool6), BadValue);

    // This one "surrounds" the other pool.
    Pool4Ptr pool7(new Pool4(IOAddress("192.0.2.8"),
                             IOAddress("192.0.2.23")));
    ASSERT_THROW(subnet->addPool(pool7), BadValue);

    // This one does not overlap.
    Pool4Ptr pool8(new Pool4(IOAddress("192.0.2.30"),
                             IOAddress("192.0.2.40")));
    ASSERT_NO_THROW(subnet->addPool(pool8));

    // This one has a lower bound in the pool of 192.0.2.10-20.
    Pool4Ptr pool9(new Pool4(IOAddress("192.0.2.18"),
                             IOAddress("192.0.2.30")));
    ASSERT_THROW(subnet->addPool(pool9), BadValue);

    // This one has an upper bound in the pool of 192.0.2.30-40.
    Pool4Ptr pool10(new Pool4(IOAddress("192.0.2.25"),
                              IOAddress("192.0.2.32")));
    ASSERT_THROW(subnet->addPool(pool10), BadValue);

    // Add a pool with a single address.
    Pool4Ptr pool11(new Pool4(IOAddress("192.255.0.50"),
                              IOAddress("192.255.0.50")));
    ASSERT_NO_THROW(subnet->addPool(pool11));

    // Now we're going to add the same pool again. This is an interesting
    // case because we're checking if the code is properly using upper_bound
    // function, which returns a pool that has an address greater than the
    // specified one.
    ASSERT_THROW(subnet->addPool(pool11), BadValue);
}

// Tests whether Subnet4 object is able to store and process properly
// information about allowed client class (a single class).
TEST(Subnet4Test, clientClass) {
    // Create the V4 subnet.
    Subnet4Ptr subnet(new Subnet4(IOAddress("192.0.2.0"), 8, 1, 2, 3));

    // This client does not belong to any class.
    isc::dhcp::ClientClasses no_class;

    // This client belongs to foo only.
    isc::dhcp::ClientClasses foo_class;
    foo_class.insert("foo");

    // This client belongs to bar only. I like that client.
    isc::dhcp::ClientClasses bar_class;
    bar_class.insert("bar");

    // This client belongs to foo, bar and baz classes.
    isc::dhcp::ClientClasses three_classes;
    three_classes.insert("foo");
    three_classes.insert("bar");
    three_classes.insert("baz");

    // This client belongs to foo, bar, baz and network classes.
    isc::dhcp::ClientClasses four_classes;
    four_classes.insert("foo");
    four_classes.insert("bar");
    four_classes.insert("baz");
    four_classes.insert("network");

    // No class restrictions defined, any client should be supported
    EXPECT_TRUE(subnet->getClientClass().empty());
    EXPECT_TRUE(subnet->clientSupported(no_class));
    EXPECT_TRUE(subnet->clientSupported(foo_class));
    EXPECT_TRUE(subnet->clientSupported(bar_class));
    EXPECT_TRUE(subnet->clientSupported(three_classes));

    // Let's allow only clients belonging to "bar" class.
    subnet->allowClientClass("bar");
    EXPECT_EQ("bar", subnet->getClientClass().get());

    EXPECT_FALSE(subnet->clientSupported(no_class));
    EXPECT_FALSE(subnet->clientSupported(foo_class));
    EXPECT_TRUE(subnet->clientSupported(bar_class));
    EXPECT_TRUE(subnet->clientSupported(three_classes));

    // Add shared network which can only be selected when the client
    // class is "network".
    SharedNetwork4Ptr network(new SharedNetwork4("network"));
    network->allowClientClass("network");
    ASSERT_NO_THROW(network->add(subnet));

    // This time, if the client doesn't support network class,
    // the subnets from the shared network can't be selected.
    EXPECT_FALSE(subnet->clientSupported(bar_class));
    EXPECT_FALSE(subnet->clientSupported(three_classes));

    // If the classes include "network", the subnet is selected.
    EXPECT_TRUE(subnet->clientSupported(four_classes));
}

TEST(Subnet4Test, addInvalidOption) {
    // Create the V4 subnet.
    Subnet4Ptr subnet(new Subnet4(IOAddress("192.0.2.0"), 8, 1, 2, 3));

    // Create NULL pointer option. Attempt to add NULL option
    // should result in exception.
    OptionPtr option2;
    ASSERT_FALSE(option2);
    EXPECT_THROW(subnet->getCfgOption()->add(option2, false, DHCP4_OPTION_SPACE),
                 isc::BadValue);
}

// This test verifies that inRange() and inPool() methods work properly.
TEST(Subnet4Test, inRangeinPool) {
    Subnet4Ptr subnet(new Subnet4(IOAddress("192.0.0.0"), 8, 1, 2, 3));

    // this one is in subnet
    Pool4Ptr pool1(new Pool4(IOAddress("192.2.0.0"), 16));
    subnet->addPool(pool1);

    // 192.1.1.1 belongs to the subnet...
    EXPECT_TRUE(subnet->inRange(IOAddress("192.1.1.1")));

    // ... but it does not belong to any pool within
    EXPECT_FALSE(subnet->inPool(Lease::TYPE_V4, IOAddress("192.1.1.1")));

    // the last address that is in range, but out of pool
    EXPECT_TRUE(subnet->inRange(IOAddress("192.1.255.255")));
    EXPECT_FALSE(subnet->inPool(Lease::TYPE_V4, IOAddress("192.1.255.255")));

    // the first address that is in range, in pool
    EXPECT_TRUE(subnet->inRange(IOAddress("192.2.0.0")));
    EXPECT_TRUE(subnet->inPool(Lease::TYPE_V4, IOAddress("192.2.0.0")));

    // let's try something in the middle as well
    EXPECT_TRUE(subnet->inRange(IOAddress("192.2.3.4")));
    EXPECT_TRUE(subnet->inPool(Lease::TYPE_V4, IOAddress("192.2.3.4")));

    // the last address that is in range, in pool
    EXPECT_TRUE(subnet->inRange(IOAddress("192.2.255.255")));
    EXPECT_TRUE(subnet->inPool(Lease::TYPE_V4, IOAddress("192.2.255.255")));

    // the first address that is in range, but out of pool
    EXPECT_TRUE(subnet->inRange(IOAddress("192.3.0.0")));
    EXPECT_FALSE(subnet->inPool(Lease::TYPE_V4, IOAddress("192.3.0.0")));

    // Add with classes
    pool1->allowClientClass("bar");
    EXPECT_TRUE(subnet->inPool(Lease::TYPE_V4, IOAddress("192.2.3.4")));

    // This client does not belong to any class.
    isc::dhcp::ClientClasses no_class;
    EXPECT_FALSE(subnet->inPool(Lease::TYPE_V4, IOAddress("192.2.3.4"), no_class));

    // This client belongs to foo only
    isc::dhcp::ClientClasses foo_class;
    foo_class.insert("foo");
    EXPECT_FALSE(subnet->inPool(Lease::TYPE_V4, IOAddress("192.2.3.4"), foo_class));

    // This client belongs to bar only. I like that client.
    isc::dhcp::ClientClasses bar_class;
    bar_class.insert("bar");
    EXPECT_TRUE(subnet->inPool(Lease::TYPE_V4, IOAddress("192.2.3.4"), bar_class));

    // This client belongs to foo, bar and baz classes.
    isc::dhcp::ClientClasses three_classes;
    three_classes.insert("foo");
    three_classes.insert("bar");
    three_classes.insert("baz");
    EXPECT_TRUE(subnet->inPool(Lease::TYPE_V4, IOAddress("192.2.3.4"), three_classes));
}

// This test checks if the toText() method returns text representation
TEST(Subnet4Test, toText) {
    Subnet4Ptr subnet(new Subnet4(IOAddress("192.0.2.0"), 24, 1, 2, 3));
    EXPECT_EQ("192.0.2.0/24", subnet->toText());
}

// This test verifies that the IPv4 prefix can be parsed into prefix/length pair.
TEST(Subnet4Test, parsePrefix) {
    std::pair<IOAddress, uint8_t> parsed =
        std::make_pair(IOAddress::IPV4_ZERO_ADDRESS(), 0);

    // Valid prefix.
    EXPECT_NO_THROW(parsed = Subnet4::parsePrefix("192.0.5.0/24"));
    EXPECT_EQ("192.0.5.0", parsed.first.toText());
    EXPECT_EQ(24, static_cast<int>(parsed.second));

    // Invalid IPv4 address.
    EXPECT_THROW(Subnet4::parsePrefix("192.0.2.322/24"), BadValue);

    // Invalid prefix length.
    EXPECT_THROW(Subnet4::parsePrefix("192.0.2.0/64"), BadValue);
    EXPECT_THROW(Subnet4::parsePrefix("192.0.2.0/0"), BadValue);

    // No IP address.
    EXPECT_THROW(Subnet4::parsePrefix(" /24"), BadValue);

    // No prefix length but slash present.
    EXPECT_THROW(Subnet4::parsePrefix("10.0.0.0/ "), BadValue);

    // No slash sign.
    EXPECT_THROW(Subnet4::parsePrefix("10.0.0.1"), BadValue);

    // IPv6 is not allowed here.
    EXPECT_THROW(Subnet4::parsePrefix("3000::/24"), BadValue);
}

// This test checks if the get() method returns proper parameters
TEST(Subnet4Test, get) {
    Subnet4Ptr subnet(new Subnet4(IOAddress("192.0.2.0"), 28, 1, 2, 3));
    EXPECT_EQ("192.0.2.0", subnet->get().first.toText());
    EXPECT_EQ(28, subnet->get().second);
}

// Checks if last allocated address/prefix is stored/retrieved properly
TEST(Subnet4Test, lastAllocated) {
    IOAddress addr("192.0.2.17");

    IOAddress last("192.0.2.255");

    Subnet4Ptr subnet(new Subnet4(IOAddress("192.0.2.0"), 24, 1, 2, 3));

    // Check initial conditions (all should be set to the last address in range)
    EXPECT_EQ(last.toText(), subnet->getLastAllocated(Lease::TYPE_V4).toText());

    // Now set last allocated for IA
    EXPECT_NO_THROW(subnet->setLastAllocated(Lease::TYPE_V4, addr));
    EXPECT_EQ(addr.toText(), subnet->getLastAllocated(Lease::TYPE_V4).toText());

    // No, you can't set the last allocated IPv6 address in IPv4 subnet
    EXPECT_THROW(subnet->setLastAllocated(Lease::TYPE_TA, addr), BadValue);
    EXPECT_THROW(subnet->setLastAllocated(Lease::TYPE_TA, addr), BadValue);
    EXPECT_THROW(subnet->setLastAllocated(Lease::TYPE_PD, addr), BadValue);
}

// Checks if the V4 is the only allowed type for Pool4 and if getPool()
// is working properly.
TEST(Subnet4Test, PoolType) {

    Subnet4Ptr subnet(new Subnet4(IOAddress("192.2.0.0"), 16, 1, 2, 3));

    PoolPtr pool1(new Pool4(IOAddress("192.2.1.0"), 24));
    PoolPtr pool2(new Pool4(IOAddress("192.2.2.0"), 24));
    PoolPtr pool3(new Pool6(Lease::TYPE_NA, IOAddress("2001:db8:1:3::"), 64));
    PoolPtr pool4(new Pool6(Lease::TYPE_TA, IOAddress("2001:db8:1:4::"), 64));
    PoolPtr pool5(new Pool6(Lease::TYPE_PD, IOAddress("2001:db8:1:1::"), 64));

    // There should be no pools of any type by default
    EXPECT_EQ(PoolPtr(), subnet->getAnyPool(Lease::TYPE_V4));

    // It should not be possible to ask for V6 pools in Subnet4
    EXPECT_THROW(subnet->getAnyPool(Lease::TYPE_NA), BadValue);
    EXPECT_THROW(subnet->getAnyPool(Lease::TYPE_TA), BadValue);
    EXPECT_THROW(subnet->getAnyPool(Lease::TYPE_PD), BadValue);

    // Let's add a single V4 pool and check that it can be retrieved
    EXPECT_NO_THROW(subnet->addPool(pool1));

    // If there's only one IA pool, get that pool (without and with hint)
    EXPECT_EQ(pool1, subnet->getAnyPool(Lease::TYPE_V4));
    EXPECT_EQ(pool1, subnet->getPool(Lease::TYPE_V4, IOAddress("192.0.1.167")));

    // Let's add additional V4 pool
    EXPECT_NO_THROW(subnet->addPool(pool2));

    // Try without hints
    EXPECT_EQ(pool1, subnet->getAnyPool(Lease::TYPE_V4));

    // Try with valid hints
    EXPECT_EQ(pool1, subnet->getPool(Lease::TYPE_V4, IOAddress("192.2.1.5")));
    EXPECT_EQ(pool2, subnet->getPool(Lease::TYPE_V4, IOAddress("192.2.2.254")));

    // Try with bogus hints (hints should be ignored)
    EXPECT_EQ(pool1, subnet->getPool(Lease::TYPE_V4, IOAddress("10.1.1.1")));

    // Trying to add Pool6 to Subnet4 is a big no,no!
    EXPECT_THROW(subnet->addPool(pool3), BadValue);
    EXPECT_THROW(subnet->addPool(pool4), BadValue);
    EXPECT_THROW(subnet->addPool(pool5), BadValue);
}

// Tests if correct value of server identifier is returned when getServerId is
// called.
TEST(Subnet4Test, getServerId) {
    // Initially, the subnet has no server identifier.
    Subnet4 subnet(IOAddress("192.2.0.0"), 16, 1, 2, 3);
    EXPECT_TRUE(subnet.getServerId().isV4Zero());

    // Add server identifier.
    OptionDefinitionPtr option_def = LibDHCP::getOptionDef(DHCP4_OPTION_SPACE,
                                                         DHO_DHCP_SERVER_IDENTIFIER);
    OptionCustomPtr option_server_id(new OptionCustom(*option_def, Option::V4));
    option_server_id->writeAddress(IOAddress("1.2.3.4"));

    CfgOptionPtr cfg_option = subnet.getCfgOption();
    cfg_option->add(option_server_id, false, DHCP4_OPTION_SPACE);

    // Verify that the server identifier returned by the Subnet4 object is
    // correct.
    OptionBuffer server_id_buf = { 1, 2, 3, 4 };
    EXPECT_EQ("1.2.3.4", subnet.getServerId().toText());
}

// Tests for Subnet6

TEST(Subnet6Test, constructor) {

    EXPECT_NO_THROW(Subnet6 subnet1(IOAddress("2001:db8:1::"), 64,
                                    1, 2, 3, 4));

    EXPECT_THROW(Subnet6 subnet2(IOAddress("2001:db8:1::"), 129, 1, 2, 3, 4),
                BadValue); // invalid prefix length
    EXPECT_THROW(Subnet6 subnet3(IOAddress("192.168.0.0"), 32, 1, 2, 3, 4),
                BadValue); // IPv4 addresses are not allowed in Subnet6
}

// This test verifies that the Subnet6 factory function creates a
// valid subnet instance.
TEST(Subnet6Test, create) {
    auto subnet = Subnet6::create(IOAddress("2001:db8:1::"), 64,
                                  1, 2, 3, 4, 10);
    ASSERT_TRUE(subnet);

    EXPECT_EQ("2001:db8:1::/64", subnet->toText());
    EXPECT_EQ(1, subnet->getT1().get());
    EXPECT_EQ(2, subnet->getT2().get());
    EXPECT_EQ(3, subnet->getPreferred().get());
    EXPECT_EQ(4, subnet->getValid().get());
    EXPECT_EQ(10, subnet->getID());
}

// This test verifies the default values set for the shared
// networks and verifies that the optional values are unspecified.
TEST(SharedNetwork6Test, defaults) {
    Triplet<uint32_t> t1;
    Triplet<uint32_t> t2;
    Triplet<uint32_t> preferred_lft;
    Triplet<uint32_t> valid_lft;
    Subnet6 subnet(IOAddress("2001:db8:1::"), 64, t1, t2, preferred_lft,
                   valid_lft);

    EXPECT_TRUE(subnet.getIface().unspecified());
    EXPECT_TRUE(subnet.getIface().empty());

    EXPECT_TRUE(subnet.getClientClass().unspecified());
    EXPECT_TRUE(subnet.getClientClass().empty());

    EXPECT_TRUE(subnet.getValid().unspecified());
    EXPECT_EQ(0, subnet.getValid().get());

    EXPECT_TRUE(subnet.getT1().unspecified());
    EXPECT_EQ(0, subnet.getT1().get());

    EXPECT_TRUE(subnet.getT2().unspecified());
    EXPECT_EQ(0, subnet.getT2().get());

    EXPECT_TRUE(subnet.getHostReservationMode().unspecified());
    EXPECT_EQ(Network::HR_ALL, subnet.getHostReservationMode().get());

    EXPECT_TRUE(subnet.getCalculateTeeTimes().unspecified());
    EXPECT_FALSE(subnet.getCalculateTeeTimes().get());

    EXPECT_TRUE(subnet.getT1Percent().unspecified());
    EXPECT_EQ(0.0, subnet.getT1Percent().get());

    EXPECT_TRUE(subnet.getT2Percent().unspecified());
    EXPECT_EQ(0.0, subnet.getT2Percent().get());

    EXPECT_TRUE(subnet.getPreferred().unspecified());
    EXPECT_EQ(0, subnet.getPreferred().get());

    EXPECT_TRUE(subnet.getRapidCommit().unspecified());
    EXPECT_FALSE(subnet.getRapidCommit().get());

    EXPECT_TRUE(subnet.getDdnsSendUpdates().unspecified());
    EXPECT_FALSE(subnet.getDdnsSendUpdates().get());

    EXPECT_TRUE(subnet.getDdnsOverrideNoUpdate().unspecified());
    EXPECT_FALSE(subnet.getDdnsOverrideNoUpdate().get());

    EXPECT_TRUE(subnet.getDdnsOverrideClientUpdate().unspecified());
    EXPECT_FALSE(subnet.getDdnsOverrideClientUpdate().get());

    EXPECT_TRUE(subnet.getDdnsReplaceClientNameMode().unspecified());
    EXPECT_EQ(D2ClientConfig::RCM_NEVER, subnet.getDdnsReplaceClientNameMode().get());

    EXPECT_TRUE(subnet.getDdnsGeneratedPrefix().unspecified());
    EXPECT_TRUE(subnet.getDdnsGeneratedPrefix().empty());

    EXPECT_TRUE(subnet.getDdnsQualifyingSuffix().unspecified());
    EXPECT_TRUE(subnet.getDdnsQualifyingSuffix().empty());

    EXPECT_TRUE(subnet.getHostnameCharSet().unspecified());
    EXPECT_TRUE(subnet.getHostnameCharSet().empty());

    EXPECT_TRUE(subnet.getHostnameCharReplacement().unspecified());
    EXPECT_TRUE(subnet.getHostnameCharReplacement().empty());
}

// Checks that the subnet id can be either autogenerated or set to an
// arbitrary value through the constructor.
TEST(Subnet6Test, subnetID) {
    // Create subnet and don't specify id, so as it is autogenerated.
    Subnet6Ptr subnet(new Subnet6(IOAddress("2001:db8:1::"), 64, 1000, 2000,
                                  3000, 4000));
    SubnetID id0 = subnet->getID();

    // Create another subnet and let id be autogenerated.
    subnet.reset(new Subnet6(IOAddress("2001:db8:2::"), 64, 1000, 2000,
                             3000, 4000));
    SubnetID id1 = subnet->getID();

    // The autogenerated ids must not be equal.
    EXPECT_NE(id0, id1);

    // Create third subnet but this time select an arbitrary id. The id
    // we use us the one of second subnet. That way we ensure that the
    // subnet id we provide via constructor is used and it is not
    // autogenerated - if it was autogenerated we would get id other
    // than id1 because id1 has already been used.
    subnet.reset(new Subnet6(IOAddress("2001:db8:3::"), 64, 1000, 2000,
                             3000, 4000, id1));
    EXPECT_EQ(id1, subnet->getID());
}

TEST(Subnet6Test, inRange) {
    Subnet6 subnet(IOAddress("2001:db8:1::"), 64, 1000, 2000, 3000, 4000);

    EXPECT_EQ(1000, subnet.getT1());
    EXPECT_EQ(2000, subnet.getT2());
    EXPECT_EQ(3000, subnet.getPreferred());
    EXPECT_EQ(4000, subnet.getValid());

    EXPECT_FALSE(subnet.inRange(IOAddress("2001:db8:0:ffff:ffff:ffff:ffff:ffff")));
    EXPECT_TRUE(subnet.inRange(IOAddress("2001:db8:1::0")));
    EXPECT_TRUE(subnet.inRange(IOAddress("2001:db8:1::1")));
    EXPECT_TRUE(subnet.inRange(IOAddress("2001:db8:1::ffff:ffff:ffff:ffff")));
    EXPECT_FALSE(subnet.inRange(IOAddress("2001:db8:1:1::")));
    EXPECT_FALSE(subnet.inRange(IOAddress("::")));
}

// Checks whether the relay list is empty by default
// and basic operations function
TEST(Subnet6Test, relay) {
    Subnet6 subnet(IOAddress("2001:db8:1::"), 64, 1000, 2000, 3000, 4000);

    // Should be empty.
    EXPECT_FALSE(subnet.hasRelays());
    EXPECT_EQ(0, subnet.getRelayAddresses().size());

    // Matching should fail.
    EXPECT_FALSE(subnet.hasRelayAddress(IOAddress("2001:ffff::45")));

    // Should be able to add them.
    subnet.addRelayAddress(IOAddress("2001:ffff::45"));
    subnet.addRelayAddress(IOAddress("2001:ffff::46"));

    // Should not be empty.
    EXPECT_TRUE(subnet.hasRelays());

    // Should be two in the list.
    EXPECT_EQ(2, subnet.getRelayAddresses().size());

    // Should be able to match them if they are there.
    EXPECT_TRUE(subnet.hasRelayAddress(IOAddress("2001:ffff::45")));
    EXPECT_TRUE(subnet.hasRelayAddress(IOAddress("2001:ffff::46")));

    // Should not match those that are not.
    EXPECT_FALSE(subnet.hasRelayAddress(IOAddress("2001:ffff::47")));
}

// Test checks whether the number of addresses available in the pools are
// calculated properly.
TEST(Subnet6Test, Pool6getCapacity) {

    Subnet6Ptr subnet(new Subnet6(IOAddress("2001:db8:1::"), 56, 1, 2, 3, 4));

    // There's 2^16 = 65536 addresses in this one.
    PoolPtr pool1(new Pool6(Lease::TYPE_NA, IOAddress("2001:db8:1:1::"), 112));

    // There's 2^32 = 4294967296 addresses in each of those.
    PoolPtr pool2(new Pool6(Lease::TYPE_NA, IOAddress("2001:db8:1:2::"), 96));
    PoolPtr pool3(new Pool6(Lease::TYPE_NA, IOAddress("2001:db8:1:3::"), 96));

    EXPECT_EQ(0, subnet->getPoolCapacity(Lease::TYPE_NA));
    EXPECT_EQ(0, subnet->getPoolCapacity(Lease::TYPE_TA));
    EXPECT_EQ(0, subnet->getPoolCapacity(Lease::TYPE_PD));

    subnet->addPool(pool1);
    EXPECT_EQ(65536, subnet->getPoolCapacity(Lease::TYPE_NA));

    subnet->addPool(pool2);
    EXPECT_EQ(uint64_t(4294967296ull + 65536), subnet->getPoolCapacity(Lease::TYPE_NA));

    subnet->addPool(pool3);
    EXPECT_EQ(uint64_t(4294967296ull + 4294967296ull + 65536),
              subnet->getPoolCapacity(Lease::TYPE_NA));

    // Now play with classes

    // This client does not belong to any class.
    isc::dhcp::ClientClasses no_class;

    // This client belongs to foo only.
    isc::dhcp::ClientClasses foo_class;
    foo_class.insert("foo");

    // This client belongs to bar only. I like that client.
    isc::dhcp::ClientClasses bar_class;
    bar_class.insert("bar");

    // This client belongs to foo, bar and baz classes.
    isc::dhcp::ClientClasses three_classes;
    three_classes.insert("foo");
    three_classes.insert("bar");
    three_classes.insert("baz");

    pool3->allowClientClass("bar");

    // Pool3 requires a member of bar
    EXPECT_EQ(uint64_t(4294967296ull + 65536),
              subnet->getPoolCapacity(Lease::TYPE_NA, no_class));
    EXPECT_EQ(uint64_t(4294967296ull + 65536),
              subnet->getPoolCapacity(Lease::TYPE_NA, foo_class));
    EXPECT_EQ(uint64_t(4294967296ull + 4294967296ull + 65536),
              subnet->getPoolCapacity(Lease::TYPE_NA, bar_class));
    EXPECT_EQ(uint64_t(4294967296ull + 4294967296ull + 65536),
              subnet->getPoolCapacity(Lease::TYPE_NA, three_classes));
}

// Test checks whether the number of prefixes available in the pools are
// calculated properly.
TEST(Subnet6Test, Pool6PdgetPoolCapacity) {

    Subnet6Ptr subnet(new Subnet6(IOAddress("2001:db8::"), 32, 1, 2, 3, 4));

    // There's 2^16 = 65536 addresses in this one.
    PoolPtr pool1(new Pool6(Lease::TYPE_PD, IOAddress("2001:db8:1::"), 48, 64));

    // There's 2^32 = 4294967296 addresses in each of those.
    PoolPtr pool2(new Pool6(Lease::TYPE_PD, IOAddress("2001:db8:2::"), 48, 80));
    PoolPtr pool3(new Pool6(Lease::TYPE_PD, IOAddress("2001:db8:3::"), 48, 80));

    EXPECT_EQ(0, subnet->getPoolCapacity(Lease::TYPE_NA));
    EXPECT_EQ(0, subnet->getPoolCapacity(Lease::TYPE_TA));
    EXPECT_EQ(0, subnet->getPoolCapacity(Lease::TYPE_PD));

    subnet->addPool(pool1);
    EXPECT_EQ(65536, subnet->getPoolCapacity(Lease::TYPE_PD));

    subnet->addPool(pool2);
    EXPECT_EQ(uint64_t(4294967296ull + 65536), subnet->getPoolCapacity(Lease::TYPE_PD));

    subnet->addPool(pool3);
    EXPECT_EQ(uint64_t(4294967296ull + 4294967296ull + 65536),
              subnet->getPoolCapacity(Lease::TYPE_PD));

    // This is 2^64.
    PoolPtr pool4(new Pool6(Lease::TYPE_PD, IOAddress("2001:db8:4::"), 48, 112));
    subnet->addPool(pool4);
    EXPECT_EQ(std::numeric_limits<uint64_t>::max(),
              subnet->getPoolCapacity(Lease::TYPE_PD));

    PoolPtr pool5(new Pool6(Lease::TYPE_PD, IOAddress("2001:db8:5::"), 48, 112));
    subnet->addPool(pool5);
    EXPECT_EQ(std::numeric_limits<uint64_t>::max(),
              subnet->getPoolCapacity(Lease::TYPE_PD));
}

TEST(Subnet6Test, Pool6InSubnet6) {

    Subnet6Ptr subnet(new Subnet6(IOAddress("2001:db8:1::"), 56, 1, 2, 3, 4));

    PoolPtr pool1(new Pool6(Lease::TYPE_NA, IOAddress("2001:db8:1:1::"), 64));
    PoolPtr pool2(new Pool6(Lease::TYPE_NA, IOAddress("2001:db8:1:2::"), 64));
    PoolPtr pool3(new Pool6(Lease::TYPE_NA, IOAddress("2001:db8:1:3::"), 64));

    subnet->addPool(pool1);

    // If there's only one pool, get that pool
    PoolPtr mypool = subnet->getAnyPool(Lease::TYPE_NA);
    EXPECT_EQ(mypool, pool1);

    subnet->addPool(pool2);
    subnet->addPool(pool3);

    // If there are more than one pool and we didn't provide hint, we
    // should get the first pool
    mypool = subnet->getAnyPool(Lease::TYPE_NA);

    EXPECT_EQ(mypool, pool1);

    // If we provide a hint, we should get a pool that this hint belongs to
    mypool = subnet->getPool(Lease::TYPE_NA, IOAddress("2001:db8:1:3::dead:beef"));

    EXPECT_EQ(mypool, pool3);

    // Now play with classes

    // This client does not belong to any class.
    isc::dhcp::ClientClasses no_class;

    // This client belongs to foo only.
    isc::dhcp::ClientClasses foo_class;
    foo_class.insert("foo");

    // This client belongs to bar only. I like that client.
    isc::dhcp::ClientClasses bar_class;
    bar_class.insert("bar");

    // This client belongs to foo, bar and baz classes.
    isc::dhcp::ClientClasses three_classes;
    three_classes.insert("foo");
    three_classes.insert("bar");
    three_classes.insert("baz");

    pool3->allowClientClass("bar");

    // Pool3 requires a member of bar
    mypool = subnet->getPool(Lease::TYPE_NA, no_class,
                             IOAddress("2001:db8:1:3::dead:beef"));
    EXPECT_FALSE(mypool);
    mypool = subnet->getPool(Lease::TYPE_NA, foo_class,
                             IOAddress("2001:db8:1:3::dead:beef"));
    EXPECT_FALSE(mypool);
    mypool = subnet->getPool(Lease::TYPE_NA, bar_class,
                             IOAddress("2001:db8:1:3::dead:beef"));
    EXPECT_EQ(mypool, pool3);
    mypool = subnet->getPool(Lease::TYPE_NA, three_classes,
                             IOAddress("2001:db8:1:3::dead:beef"));
    EXPECT_EQ(mypool, pool3);
}

// Check if Subnet6 supports different types of pools properly.
TEST(Subnet6Test, poolTypes) {

    Subnet6Ptr subnet(new Subnet6(IOAddress("2001:db8:1::"), 56, 1, 2, 3, 4));

    PoolPtr pool1(new Pool6(Lease::TYPE_NA, IOAddress("2001:db8:1:1::"), 64));
    PoolPtr pool2(new Pool6(Lease::TYPE_TA, IOAddress("2001:db8:1:2::"), 64));
    PoolPtr pool3(new Pool6(Lease::TYPE_PD, IOAddress("2001:db8:1:3::"), 64));
    PoolPtr pool4(new Pool6(Lease::TYPE_PD, IOAddress("3000:1::"), 64));

    PoolPtr pool5(new Pool4(IOAddress("192.0.2.0"), 24));

    // There should be no pools of any type by default
    EXPECT_EQ(PoolPtr(), subnet->getAnyPool(Lease::TYPE_NA));
    EXPECT_EQ(PoolPtr(), subnet->getAnyPool(Lease::TYPE_TA));
    EXPECT_EQ(PoolPtr(), subnet->getAnyPool(Lease::TYPE_PD));

    // Trying to get IPv4 pool from Subnet6 is not allowed
    EXPECT_THROW(subnet->getAnyPool(Lease::TYPE_V4), BadValue);

    // Let's add a single IA pool and check that it can be retrieved
    EXPECT_NO_THROW(subnet->addPool(pool1));

    // If there's only one IA pool, get that pool
    EXPECT_EQ(pool1, subnet->getAnyPool(Lease::TYPE_NA));
    EXPECT_EQ(pool1, subnet->getPool(Lease::TYPE_NA, IOAddress("2001:db8:1:1::1")));

    // Check if pools of different type are not returned
    EXPECT_EQ(PoolPtr(), subnet->getAnyPool(Lease::TYPE_TA));
    EXPECT_EQ(PoolPtr(), subnet->getAnyPool(Lease::TYPE_PD));

    // We ask with good hints, but wrong types, should return nothing
    EXPECT_EQ(PoolPtr(), subnet->getPool(Lease::TYPE_PD, IOAddress("2001:db8:1:2::1")));
    EXPECT_EQ(PoolPtr(), subnet->getPool(Lease::TYPE_TA, IOAddress("2001:db8:1:3::1")));

    // Let's add TA and PD pools
    EXPECT_NO_THROW(subnet->addPool(pool2));
    EXPECT_NO_THROW(subnet->addPool(pool3));

    // Try without hints
    EXPECT_EQ(pool1, subnet->getAnyPool(Lease::TYPE_NA));
    EXPECT_EQ(pool2, subnet->getAnyPool(Lease::TYPE_TA));
    EXPECT_EQ(pool3, subnet->getAnyPool(Lease::TYPE_PD));

    // Try with valid hints
    EXPECT_EQ(pool1, subnet->getPool(Lease::TYPE_NA, IOAddress("2001:db8:1:1::1")));
    EXPECT_EQ(pool2, subnet->getPool(Lease::TYPE_TA, IOAddress("2001:db8:1:2::1")));
    EXPECT_EQ(pool3, subnet->getPool(Lease::TYPE_PD, IOAddress("2001:db8:1:3::1")));

    // Try with bogus hints (hints should be ignored)
    EXPECT_EQ(pool1, subnet->getPool(Lease::TYPE_NA, IOAddress("2001:db8:1:7::1")));
    EXPECT_EQ(pool2, subnet->getPool(Lease::TYPE_TA, IOAddress("2001:db8:1:7::1")));
    EXPECT_EQ(pool3, subnet->getPool(Lease::TYPE_PD, IOAddress("2001:db8:1:7::1")));

    // Let's add a second PD pool
    EXPECT_NO_THROW(subnet->addPool(pool4));

    // Without hints, it should return the first pool
    EXPECT_EQ(pool3, subnet->getAnyPool(Lease::TYPE_PD));

    // With valid hint, it should return that hint
    EXPECT_EQ(pool3, subnet->getPool(Lease::TYPE_PD, IOAddress("2001:db8:1:3::1")));
    EXPECT_EQ(pool4, subnet->getPool(Lease::TYPE_PD, IOAddress("3000:1::")));

    // With invalid hint, it should return the first pool
    EXPECT_EQ(pool3, subnet->getPool(Lease::TYPE_PD, IOAddress("2001:db8::123")));

    // Adding Pool4 to Subnet6 is a big no, no!
    EXPECT_THROW(subnet->addPool(pool5), BadValue);
}

// Tests whether Subnet6 object is able to store and process properly
// information about allowed client class (a single class).
TEST(Subnet6Test, clientClass) {
    // Create the V6 subnet.
    Subnet6Ptr subnet(new Subnet6(IOAddress("2001:db8:1::"), 56, 1, 2, 3, 4));

    // This client does not belong to any class.
    isc::dhcp::ClientClasses no_class;

    // This client belongs to foo only.
    isc::dhcp::ClientClasses foo_class;
    foo_class.insert("foo");

    // This client belongs to bar only. I like that client.
    isc::dhcp::ClientClasses bar_class;
    bar_class.insert("bar");

    // This client belongs to foo, bar and baz classes.
    isc::dhcp::ClientClasses three_classes;
    three_classes.insert("foo");
    three_classes.insert("bar");
    three_classes.insert("baz");

    // This client belongs to foo, bar, baz and network classes.
    isc::dhcp::ClientClasses four_classes;
    four_classes.insert("foo");
    four_classes.insert("bar");
    four_classes.insert("baz");
    four_classes.insert("network");

    // No class restrictions defined, any client should be supported
    EXPECT_TRUE(subnet->getClientClass().empty());
    EXPECT_TRUE(subnet->clientSupported(no_class));
    EXPECT_TRUE(subnet->clientSupported(foo_class));
    EXPECT_TRUE(subnet->clientSupported(bar_class));
    EXPECT_TRUE(subnet->clientSupported(three_classes));

    // Let's allow only clients belonging to "bar" class.
    subnet->allowClientClass("bar");
    EXPECT_EQ("bar", subnet->getClientClass().get());

    EXPECT_FALSE(subnet->clientSupported(no_class));
    EXPECT_FALSE(subnet->clientSupported(foo_class));
    EXPECT_TRUE(subnet->clientSupported(bar_class));
    EXPECT_TRUE(subnet->clientSupported(three_classes));

    // Add shared network which can only be selected when the client
    // class is "network".
    SharedNetwork6Ptr network(new SharedNetwork6("network"));
    network->allowClientClass("network");
    ASSERT_NO_THROW(network->add(subnet));

    // This time, if the client doesn't support network class,
    // the subnets from the shared network can't be selected.
    EXPECT_FALSE(subnet->clientSupported(bar_class));
    EXPECT_FALSE(subnet->clientSupported(three_classes));

    // If the classes include "network", the subnet is selected.
    EXPECT_TRUE(subnet->clientSupported(four_classes));
}

// Checks that it is not allowed to add invalid pools.
TEST(Subnet6Test, pool6Checks) {

    Subnet6Ptr subnet(new Subnet6(IOAddress("2001:db8:1::"), 56, 1, 2, 3, 4));

    // this one is in subnet
    Pool6Ptr pool1(new Pool6(Lease::TYPE_NA, IOAddress("2001:db8:1:1::"), 64));
    ASSERT_NO_THROW(subnet->addPool(pool1));

    // this one is larger than the subnet!
    Pool6Ptr pool2(new Pool6(Lease::TYPE_NA, IOAddress("2001:db8::"), 48));

    ASSERT_THROW(subnet->addPool(pool2), BadValue);

    // this one is totally out of blue
    Pool6Ptr pool3(new Pool6(Lease::TYPE_NA, IOAddress("3000::"), 16));
    ASSERT_THROW(subnet->addPool(pool3), BadValue);

    Pool6Ptr pool4(new Pool6(Lease::TYPE_NA, IOAddress("4001:db8:1::"), 80));
    ASSERT_THROW(subnet->addPool(pool4), BadValue);

    // This pool should be added just fine.
    Pool6Ptr pool5(new Pool6(Lease::TYPE_NA, IOAddress("2001:db8:1:2::100"),
                             IOAddress("2001:db8:1:2::200")));
    ASSERT_NO_THROW(subnet->addPool(pool5));

    // This pool overlaps with a previously added pool.
    Pool6Ptr pool6(new Pool6(Lease::TYPE_NA, IOAddress("2001:db8:1:2::1"),
                             IOAddress("2001:db8:1:2::150")));
    ASSERT_THROW(subnet->addPool(pool6), BadValue);

    // This pool also overlaps
    Pool6Ptr pool7(new Pool6(Lease::TYPE_NA, IOAddress("2001:db8:1:2::150"),
                             IOAddress("2001:db8:1:2::300")));
    ASSERT_THROW(subnet->addPool(pool7), BadValue);

    // This one "surrounds" the other pool.
    Pool6Ptr pool8(new Pool6(Lease::TYPE_NA, IOAddress("2001:db8:1:2::50"),
                             IOAddress("2001:db8:1:2::250")));
    ASSERT_THROW(subnet->addPool(pool8), BadValue);

    // This one does not overlap.
    Pool6Ptr pool9(new Pool6(Lease::TYPE_NA, IOAddress("2001:db8:1:2::300"),
                             IOAddress("2001:db8:1:2::400")));
    ASSERT_NO_THROW(subnet->addPool(pool9));

    // This one has a lower bound in the pool of 2001:db8:1::100-200.
    Pool6Ptr pool10(new Pool6(Lease::TYPE_NA, IOAddress("2001:db8:1:2::200"),
                              IOAddress("2001:db8:1:2::225")));
    ASSERT_THROW(subnet->addPool(pool10), BadValue);

    // This one has an upper bound in the pool of 2001:db8:1::300-400.
    Pool6Ptr pool11(new Pool6(Lease::TYPE_NA, IOAddress("2001:db8:1:2::250"),
                              IOAddress("2001:db8:1:2::300")));
    ASSERT_THROW(subnet->addPool(pool11), BadValue);

    // Add a pool with a single address.
    Pool6Ptr pool12(new Pool6(Lease::TYPE_NA, IOAddress("2001:db8:1:3::250"),
                              IOAddress("2001:db8:1:3::250")));
    ASSERT_NO_THROW(subnet->addPool(pool12));

    // Now we're going to add the same pool again. This is an interesting
    // case because we're checking if the code is properly using upper_bound
    // function, which returns a pool that has an address greater than the
    // specified one.
    ASSERT_THROW(subnet->addPool(pool12), BadValue);

    // Prefix pool overlaps with the pool1. We can't hand out addresses and
    // prefixes from the same range.
    Pool6Ptr pool13(new Pool6(Lease::TYPE_PD, IOAddress("2001:db8:1:1:2::"),
                              80, 96));
    ASSERT_THROW(subnet->addPool(pool13), BadValue);
}

TEST(Subnet6Test, addOptions) {
    // Create as subnet to add options to it.
    Subnet6Ptr subnet(new Subnet6(IOAddress("2001:db8:1::"), 56, 1, 2, 3, 4));

    // Differentiate options by their codes (100-109)
    for (uint16_t code = 100; code < 110; ++code) {
        OptionPtr option(new Option(Option::V6, code, OptionBuffer(10, 0xFF)));
        ASSERT_NO_THROW(subnet->getCfgOption()->add(option, false, DHCP6_OPTION_SPACE));
    }

    // Add 7 options to another option space. The option codes partially overlap
    // with option codes that we have added to dhcp6 option space.
    for (uint16_t code = 105; code < 112; ++code) {
        OptionPtr option(new Option(Option::V6, code, OptionBuffer(10, 0xFF)));
        ASSERT_NO_THROW(subnet->getCfgOption()->add(option, false, "isc"));
    }

    // Get options from the Subnet and check if all 10 are there.
    OptionContainerPtr options = subnet->getCfgOption()->getAll(DHCP6_OPTION_SPACE);
    ASSERT_TRUE(options);
    ASSERT_EQ(10, options->size());

    // Validate codes of options added to dhcp6 option space.
    uint16_t expected_code = 100;
    for (OptionContainer::const_iterator option_desc = options->begin();
         option_desc != options->end(); ++option_desc) {
        ASSERT_TRUE(option_desc->option_);
        EXPECT_EQ(expected_code, option_desc->option_->getType());
        ++expected_code;
    }

    options = subnet->getCfgOption()->getAll("isc");
    ASSERT_TRUE(options);
    ASSERT_EQ(7, options->size());

    // Validate codes of options added to isc option space.
    expected_code = 105;
    for (OptionContainer::const_iterator option_desc = options->begin();
         option_desc != options->end(); ++option_desc) {
        ASSERT_TRUE(option_desc->option_);
        EXPECT_EQ(expected_code, option_desc->option_->getType());
        ++expected_code;
    }

    // Try to get options from a non-existing option space.
    options = subnet->getCfgOption()->getAll("abcd");
    ASSERT_TRUE(options);
    EXPECT_TRUE(options->empty());
}

TEST(Subnet6Test, addNonUniqueOptions) {
    // Create as subnet to add options to it.
    Subnet6Ptr subnet(new Subnet6(IOAddress("2001:db8:1::"), 56, 1, 2, 3, 4));

    // Create a set of options with non-unique codes.
    for (int i = 0;  i < 2; ++i) {
        // In the inner loop we create options with unique codes (100-109).
        for (uint16_t code = 100; code < 110; ++code) {
            OptionPtr option(new Option(Option::V6, code, OptionBuffer(10, 0xFF)));
            ASSERT_NO_THROW(subnet->getCfgOption()->add(option, false, DHCP6_OPTION_SPACE));
        }
    }

    // Sanity check that all options are there.
    OptionContainerPtr options = subnet->getCfgOption()->getAll(DHCP6_OPTION_SPACE);
    ASSERT_EQ(20, options->size());

    // Use container index #1 to get the options by their codes.
    OptionContainerTypeIndex& idx = options->get<1>();
    // Look for the codes 100-109.
    for (uint16_t code = 100; code < 110; ++ code) {
        // For each code we should get two instances of options->
        OptionContainerTypeRange range = idx.equal_range(code);
        // Distance between iterators indicates how many options
        // have been returned for the particular code.
        ASSERT_EQ(2, distance(range.first, range.second));
        // Check that returned options actually have the expected option code.
        for (OptionContainerTypeIndex::const_iterator option_desc = range.first;
             option_desc != range.second; ++option_desc) {
            ASSERT_TRUE(option_desc->option_);
            EXPECT_EQ(code, option_desc->option_->getType());
        }
    }

    // Let's try to find some non-exiting option.
    const uint16_t non_existing_code = 150;
    OptionContainerTypeRange range = idx.equal_range(non_existing_code);
    // Empty set is expected.
    EXPECT_EQ(0, distance(range.first, range.second));
}

TEST(Subnet6Test, addPersistentOption) {
    // Create as subnet to add options to it.
    Subnet6Ptr subnet(new Subnet6(IOAddress("2001:db8:1::"), 56, 1, 2, 3, 4));

    // Add 10 options to the subnet with option codes 100 - 109.
    for (uint16_t code = 100; code < 110; ++code) {
        OptionPtr option(new Option(Option::V6, code, OptionBuffer(10, 0xFF)));
        // We create 10 options and want some of them to be flagged
        // persistent and some non-persistent. Persistent options are
        // those that server sends to clients regardless if they ask
        // for them or not. We pick 3 out of 10 options and mark them
        // non-persistent and 7 other options persistent.
        // Code values: 102, 105 and 108 are divisible by 3
        // and options with these codes will be flagged non-persistent.
        // Options with other codes will be flagged persistent.
        bool persistent = (code % 3) ? true : false;
        ASSERT_NO_THROW(subnet->getCfgOption()->add(option, persistent, DHCP6_OPTION_SPACE));
    }

    // Get added options from the subnet.
    OptionContainerPtr options = subnet->getCfgOption()->getAll(DHCP6_OPTION_SPACE);

    // options->get<2> returns reference to container index #2. This
    // index is used to access options by the 'persistent' flag.
    OptionContainerPersistIndex& idx = options->get<2>();

    // Get all persistent options->
    OptionContainerPersistRange range_persistent = idx.equal_range(true);
    // 7 out of 10 options have been flagged persistent.
    ASSERT_EQ(7, distance(range_persistent.first, range_persistent.second));

    // Get all non-persistent options->
    OptionContainerPersistRange range_non_persistent = idx.equal_range(false);
    // 3 out of 10 options have been flagged not persistent.
    ASSERT_EQ(3, distance(range_non_persistent.first, range_non_persistent.second));
}

TEST(Subnet6Test, getOptions) {
    Subnet6Ptr subnet(new Subnet6(IOAddress("2001:db8::"), 56, 1, 2, 3, 4));

    // Add 10 options to a "dhcp6" option space in the subnet.
    for (uint16_t code = 100; code < 110; ++code) {
        OptionPtr option(new Option(Option::V6, code, OptionBuffer(10, 0xFF)));
        ASSERT_NO_THROW(subnet->getCfgOption()->add(option, false, DHCP6_OPTION_SPACE));
    }

    // Check that we can get each added option descriptor using
    // individually.
    for (uint16_t code = 100; code < 110; ++code) {
        std::ostringstream stream;
        // First, try the invalid option space name.
        OptionDescriptor desc = subnet->getCfgOption()->get("isc", code);
        // Returned descriptor should contain NULL option ptr.
        EXPECT_FALSE(desc.option_);
        // Now, try the valid option space.
        desc = subnet->getCfgOption()->get(DHCP6_OPTION_SPACE, code);
        // Test that the option code matches the expected code.
        ASSERT_TRUE(desc.option_);
        EXPECT_EQ(code, desc.option_->getType());
    }
}

TEST(Subnet6Test, addVendorOption) {

    // Create as subnet to add options to it.
    Subnet6Ptr subnet(new Subnet6(IOAddress("2001:db8:1::"), 56, 1, 2, 3, 4));

    // Differentiate options by their codes (100-109)
    for (uint16_t code = 100; code < 110; ++code) {
        OptionPtr option(new Option(Option::V6, code, OptionBuffer(10, 0xFF)));
        ASSERT_NO_THROW(subnet->getCfgOption()->add(option, false, "vendor-12345678"));
    }

    // Add 7 options to another option space. The option codes partially overlap
    // with option codes that we have added to dhcp6 option space.
    for (uint16_t code = 105; code < 112; ++code) {
        OptionPtr option(new Option(Option::V6, code, OptionBuffer(10, 0xFF)));
        ASSERT_NO_THROW(subnet->getCfgOption()->add(option, false, "vendor-87654321"));
    }

    // Get options from the Subnet and check if all 10 are there.
    OptionContainerPtr options = subnet->getCfgOption()->getAll(12345678);
    ASSERT_TRUE(options);
    ASSERT_EQ(10, options->size());

    // Validate codes of options added to dhcp6 option space.
    uint16_t expected_code = 100;
    for (OptionContainer::const_iterator option_desc = options->begin();
         option_desc != options->end(); ++option_desc) {
        ASSERT_TRUE(option_desc->option_);
        EXPECT_EQ(expected_code, option_desc->option_->getType());
        ++expected_code;
    }

    options = subnet->getCfgOption()->getAll(87654321);
    ASSERT_TRUE(options);
    ASSERT_EQ(7, options->size());

    // Validate codes of options added to isc option space.
    expected_code = 105;
    for (OptionContainer::const_iterator option_desc = options->begin();
         option_desc != options->end(); ++option_desc) {
        ASSERT_TRUE(option_desc->option_);
        EXPECT_EQ(expected_code, option_desc->option_->getType());
        ++expected_code;
    }

    // Try to get options from a non-existing option space.
    options = subnet->getCfgOption()->getAll(1111111);
    ASSERT_TRUE(options);
    EXPECT_TRUE(options->empty());
}

// This test verifies that inRange() and inPool() methods work properly.
TEST(Subnet6Test, inRangeinPool) {
    Subnet6Ptr subnet(new Subnet6(IOAddress("2001:db8::"), 32, 1, 2, 3, 4));

    // this one is in subnet
    Pool6Ptr pool1(new Pool6(Lease::TYPE_NA, IOAddress("2001:db8::10"),
                             IOAddress("2001:db8::20")));
    subnet->addPool(pool1);

    // 2001:db8::1 belongs to the subnet...
    EXPECT_TRUE(subnet->inRange(IOAddress("2001:db8::1")));
    // ... but it does not belong to any pool within
    EXPECT_FALSE(subnet->inPool(Lease::TYPE_NA, IOAddress("2001:db8::1")));

    // the last address that is in range, but out of pool
    EXPECT_TRUE(subnet->inRange(IOAddress("2001:db8::f")));
    EXPECT_FALSE(subnet->inPool(Lease::TYPE_NA, IOAddress("2001:db8::f")));

    // the first address that is in range, in pool
    EXPECT_TRUE(subnet->inRange(IOAddress("2001:db8::10")));
    EXPECT_TRUE(subnet->inPool(Lease::TYPE_NA, IOAddress("2001:db8::10")));

    // let's try something in the middle as well
    EXPECT_TRUE(subnet->inRange(IOAddress("2001:db8::18")));
    EXPECT_TRUE(subnet->inPool(Lease::TYPE_NA, IOAddress("2001:db8::18")));

    // the last address that is in range, in pool
    EXPECT_TRUE(subnet->inRange(IOAddress("2001:db8::20")));
    EXPECT_TRUE (subnet->inPool(Lease::TYPE_NA, IOAddress("2001:db8::20")));

    // the first address that is in range, but out of pool
    EXPECT_TRUE(subnet->inRange(IOAddress("2001:db8::21")));
    EXPECT_FALSE(subnet->inPool(Lease::TYPE_NA, IOAddress("2001:db8::21")));

    // Add with classes
    pool1->allowClientClass("bar");
    EXPECT_TRUE(subnet->inPool(Lease::TYPE_NA, IOAddress("2001:db8::18")));

    // This client does not belong to any class.
    isc::dhcp::ClientClasses no_class;
    EXPECT_FALSE(subnet->inPool(Lease::TYPE_NA, IOAddress("2001:db8::18"), no_class));

    // This client belongs to foo only
    isc::dhcp::ClientClasses foo_class;
    foo_class.insert("foo");
    EXPECT_FALSE(subnet->inPool(Lease::TYPE_NA, IOAddress("2001:db8::18"), foo_class));

    // This client belongs to bar only. I like that client.
    isc::dhcp::ClientClasses bar_class;
    bar_class.insert("bar");
    EXPECT_TRUE(subnet->inPool(Lease::TYPE_NA, IOAddress("2001:db8::18"), bar_class));

    // This client belongs to foo, bar and baz classes.
    isc::dhcp::ClientClasses three_classes;
    three_classes.insert("foo");
    three_classes.insert("bar");
    three_classes.insert("baz");
    EXPECT_TRUE(subnet->inPool(Lease::TYPE_NA, IOAddress("2001:db8::18"), three_classes));
}

// This test verifies that inRange() and inPool() methods work properly
// for prefixes too.
TEST(Subnet6Test, PdinRangeinPool) {
    Subnet6Ptr subnet(new Subnet6(IOAddress("2001:db8::"), 64, 1, 2, 3, 4));

    // this one is in subnet
    Pool6Ptr pool1(new Pool6(Lease::TYPE_PD, IOAddress("2001:db8::"),
                             96, 112));
    subnet->addPool(pool1);

    // this one is not in subnet
    Pool6Ptr pool2(new Pool6(Lease::TYPE_PD, IOAddress("2001:db8:1::"),
                             96, 112));
    subnet->addPool(pool2);

    // 2001:db8::1:0:0 belongs to the subnet...
    EXPECT_TRUE(subnet->inRange(IOAddress("2001:db8::1:0:0")));
    // ... but it does not belong to any pool within
    EXPECT_FALSE(subnet->inPool(Lease::TYPE_PD, IOAddress("2001:db8::1:0:0")));

    // 2001:db8:1::1 does not belong to the subnet...
    EXPECT_FALSE(subnet->inRange(IOAddress("2001:db8:1::1")));
    // ... but it belongs to the second pool
    EXPECT_TRUE(subnet->inPool(Lease::TYPE_PD, IOAddress("2001:db8:1::1")));

    // 2001:db8::1 belongs to the subnet and to the first pool
    EXPECT_TRUE(subnet->inRange(IOAddress("2001:db8::1")));
    EXPECT_TRUE(subnet->inPool(Lease::TYPE_PD, IOAddress("2001:db8::1")));

    // 2001:db8:0:1:0:1:: does not belong to the subnet and any pool
    EXPECT_FALSE(subnet->inRange(IOAddress("2001:db8:0:1:0:1::")));
    EXPECT_FALSE(subnet->inPool(Lease::TYPE_PD, IOAddress("2001:db8:0:1:0:1::")));
}

// This test checks if the toText() method returns text representation
TEST(Subnet6Test, toText) {
    Subnet6 subnet(IOAddress("2001:db8::"), 32, 1, 2, 3, 4);
    EXPECT_EQ("2001:db8::/32", subnet.toText());
}

// This test verifies that the IPv6 prefix can be parsed into prefix/length pair.
TEST(Subnet6Test, parsePrefix) {
    std::pair<IOAddress, uint8_t> parsed =
        std::make_pair(IOAddress::IPV6_ZERO_ADDRESS(), 0);

    // Valid prefix.
    EXPECT_NO_THROW(parsed = Subnet6::parsePrefix("2001:db8:1::/64"));
    EXPECT_EQ("2001:db8:1::", parsed.first.toText());
    EXPECT_EQ(64, static_cast<int>(parsed.second));

    // Invalid IPv6 address.
    EXPECT_THROW(Subnet6::parsePrefix("2001:db8::1::/64"), BadValue);

    // Invalid prefix length.
    EXPECT_THROW(Subnet6::parsePrefix("2001:db8:1::/164"), BadValue);
    EXPECT_THROW(Subnet6::parsePrefix("2001:db8:1::/0"), BadValue);

    // No IP address.
    EXPECT_THROW(Subnet6::parsePrefix(" /64"), BadValue);

    // No prefix length but slash present.
    EXPECT_THROW(Subnet6::parsePrefix("3000::/ "), BadValue);

    // No slash sign.
    EXPECT_THROW(Subnet6::parsePrefix("3000::"), BadValue);

    // IPv4 is not allowed here.
    EXPECT_THROW(Subnet6::parsePrefix("192.0.2.0/24"), BadValue);
}

// This test checks if the get() method returns proper parameters
TEST(Subnet6Test, get) {
    Subnet6 subnet(IOAddress("2001:db8::"), 32, 1, 2, 3, 4);
    EXPECT_EQ("2001:db8::", subnet.get().first.toText());
    EXPECT_EQ(32, subnet.get().second);
}

// This trivial test checks if interface name is stored properly
// in Subnet6 objects.
TEST(Subnet6Test, iface) {
    Subnet6 subnet(IOAddress("2001:db8::"), 32, 1, 2, 3, 4);

    EXPECT_TRUE(subnet.getIface().empty());

    subnet.setIface("en1");
    EXPECT_EQ("en1", subnet.getIface().get());
}

// This trivial test checks if the interface-id option can be set and
// later retrieved for a subnet6 object.
TEST(Subnet6Test, interfaceId) {
    // Create as subnet to add options to it.
    Subnet6Ptr subnet(new Subnet6(IOAddress("2001:db8:1::"), 56, 1, 2, 3, 4));

    EXPECT_FALSE(subnet->getInterfaceId());

    OptionPtr option(new Option(Option::V6, D6O_INTERFACE_ID, OptionBuffer(10, 0xFF)));
    subnet->setInterfaceId(option);

    EXPECT_EQ(option, subnet->getInterfaceId());

}

// This test checks that the Rapid Commit support can be enabled or
// disabled for a subnet. It also checks that the Rapid Commit
// support is disabled by default.
TEST(Subnet6Test, rapidCommit) {
    Subnet6 subnet(IOAddress("2001:db8:1::"), 56, 1, 2, 3, 4);

    // By default, the RC should be disabled.
    EXPECT_FALSE(subnet.getRapidCommit());

    // Enable Rapid Commit.
    subnet.setRapidCommit(true);
    EXPECT_TRUE(subnet.getRapidCommit());

    // Disable again.
    subnet.setRapidCommit(false);
    EXPECT_FALSE(subnet.getRapidCommit());
}

// Checks if last allocated address/prefix is stored/retrieved properly
TEST(Subnet6Test, lastAllocated) {
    IOAddress ia("2001:db8:1::1");
    IOAddress ta("2001:db8:1::abcd");
    IOAddress pd("2001:db8:1::1234:5678");

    IOAddress last("2001:db8:1::ffff:ffff:ffff:ffff");

    Subnet6Ptr subnet(new Subnet6(IOAddress("2001:db8:1::"), 64, 1, 2, 3, 4));

    // Check initial conditions (all should be set to the last address in range)
    EXPECT_EQ(last.toText(), subnet->getLastAllocated(Lease::TYPE_NA).toText());
    EXPECT_EQ(last.toText(), subnet->getLastAllocated(Lease::TYPE_TA).toText());
    EXPECT_EQ(last.toText(), subnet->getLastAllocated(Lease::TYPE_PD).toText());

    // Now set last allocated for IA
    EXPECT_NO_THROW(subnet->setLastAllocated(Lease::TYPE_NA, ia));
    EXPECT_EQ(ia.toText(), subnet->getLastAllocated(Lease::TYPE_NA).toText());

    // TA and PD should be unchanged
    EXPECT_EQ(last.toText(), subnet->getLastAllocated(Lease::TYPE_TA).toText());
    EXPECT_EQ(last.toText(), subnet->getLastAllocated(Lease::TYPE_PD).toText());

    // Now set TA and PD
    EXPECT_NO_THROW(subnet->setLastAllocated(Lease::TYPE_TA, ta));
    EXPECT_NO_THROW(subnet->setLastAllocated(Lease::TYPE_PD, pd));

    EXPECT_EQ(ia.toText(), subnet->getLastAllocated(Lease::TYPE_NA).toText());
    EXPECT_EQ(ta.toText(), subnet->getLastAllocated(Lease::TYPE_TA).toText());
    EXPECT_EQ(pd.toText(), subnet->getLastAllocated(Lease::TYPE_PD).toText());

    // No, you can't set the last allocated IPv4 address in IPv6 subnet
    EXPECT_THROW(subnet->setLastAllocated(Lease::TYPE_V4, ia), BadValue);
}

// This test verifies that the IPv4 subnet can be fetched by id.
TEST(SubnetFetcherTest, getSubnet4ById) {
    Subnet4Collection collection;

    // Subnet hasn't been added to the collection. A null pointer should
    // be returned.
    auto subnet = SubnetFetcher4::get(collection, SubnetID(1024));
    EXPECT_FALSE(subnet);

    subnet.reset(new Subnet4(IOAddress("192.0.2.0"), 24, 1, 2, 3, 1024));
    EXPECT_NO_THROW(collection.push_back(subnet));

    subnet.reset(new Subnet4(IOAddress("192.0.3.0"), 24, 1, 2, 3, 2048));
    EXPECT_NO_THROW(collection.push_back(subnet));

    subnet = SubnetFetcher4::get(collection, SubnetID(1024));
    ASSERT_TRUE(subnet);
    EXPECT_EQ(1024, subnet->getID());
    EXPECT_EQ("192.0.2.0/24", subnet->toText());

    subnet = SubnetFetcher4::get(collection, SubnetID(2048));
    ASSERT_TRUE(subnet);
    EXPECT_EQ(2048, subnet->getID());
    EXPECT_EQ("192.0.3.0/24", subnet->toText());
}

// This test verifies that the IPv6 subnet can be fetched by id.
TEST(SubnetFetcherTest, getSubnet6ById) {
    Subnet6Collection collection;

    // Subnet hasn't been added to the collection. A null pointer should
    // be returned.
    auto subnet = SubnetFetcher6::get(collection, SubnetID(1026));
    EXPECT_FALSE(subnet);

    subnet.reset(new Subnet6(IOAddress("2001:db8:1::"), 64, 1, 2, 3, 4, 1024));
    EXPECT_NO_THROW(collection.push_back(subnet));

    subnet.reset(new Subnet6(IOAddress("2001:db8:2::"), 64, 1, 2, 3, 4, 2048));
    EXPECT_NO_THROW(collection.push_back(subnet));

    subnet = SubnetFetcher6::get(collection, SubnetID(1024));
    ASSERT_TRUE(subnet);
    EXPECT_EQ(1024, subnet->getID());
    EXPECT_EQ("2001:db8:1::/64", subnet->toText());

    subnet = SubnetFetcher6::get(collection, SubnetID(2048));
    ASSERT_TRUE(subnet);
    EXPECT_EQ(2048, subnet->getID());
    EXPECT_EQ("2001:db8:2::/64", subnet->toText());
}

};

ext: cc


text:
/**
 * Reactive
 *
 * (c) 2012-2014 Axel Etcheverry
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

#include <boost/test/unit_test.hpp>
#include <reactive/identifier/uniqid.hpp>
#include <boost/regex.hpp>

BOOST_AUTO_TEST_SUITE(reactive_identifier_uniqid)

BOOST_AUTO_TEST_CASE(test_uniqid)
{
    std::string id = reactive::identifier::uniqid();

    BOOST_CHECK_EQUAL(id.length(), 13);

    boost::regex r("[a-f0-9]{13}");
    boost::cmatch matches;
    BOOST_CHECK_EQUAL(boost::regex_match(id.c_str(), matches, r), true);
}

BOOST_AUTO_TEST_SUITE_END()

max_stars_repo_name: ReactiveFramework/Reactive

max_issues_repo_name: ReactiveFramework/Reactive

max_forks_repo_name: ReactiveFramework/Reactive


text:
//  (C) Copyright Eric Niebler 2005.
//  Use, modification and distribution are subject to the
//  Boost Software License, Version 1.0. (See accompanying file
//  LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

#include <boost/test/unit_test.hpp>
#include <boost/test/floating_point_comparison.hpp>
#include <boost/accumulators/accumulators.hpp>
#include <boost/accumulators/statistics/stats.hpp>
#include <boost/accumulators/statistics/weighted_mean.hpp>
#include <boost/accumulators/statistics/variates/covariate.hpp>
#include <sstream>
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>

using namespace boost;
using namespace unit_test;
using namespace accumulators;

template<typename T>
void assert_is_double(T const &)
{
    BOOST_MPL_ASSERT((is_same<T, double>));
}

typedef accumulator_set<int, stats<tag::mean,
        tag::mean_of_variates<int, tag::covariate1> > > mean_t;

typedef accumulator_set<int, stats<tag::mean(immediate),
        tag::mean_of_variates<int, tag::covariate1>(immediate) > > immediate_mean_t;

///////////////////////////////////////////////////////////////////////////////
// test_stat
//
void test_stat()
{
    mean_t acc, test_acc(sample = 0);

    acc(1, covariate1 = 3);
    BOOST_CHECK_CLOSE(1., mean(acc), 1e-5);
    BOOST_CHECK_EQUAL(1u, count(acc));
    BOOST_CHECK_EQUAL(1, sum(acc));
    BOOST_CHECK_CLOSE(3., (accumulators::mean_of_variates<int, tag::covariate1>(acc)), 1e-5);

    acc(0, covariate1 = 4);
    BOOST_CHECK_CLOSE(0.5, mean(acc), 1e-5);
    BOOST_CHECK_EQUAL(2u, count(acc));
    BOOST_CHECK_EQUAL(1, sum(acc));
    BOOST_CHECK_CLOSE(3.5, (accumulators::mean_of_variates<int, tag::covariate1>(acc)), 1e-5);

    acc(2, covariate1 = 8);
    BOOST_CHECK_CLOSE(1., mean(acc), 1e-5);
    BOOST_CHECK_EQUAL(3u, count(acc));
    BOOST_CHECK_EQUAL(3, sum(acc));
    BOOST_CHECK_CLOSE(5., (accumulators::mean_of_variates<int, tag::covariate1>(acc)), 1e-5);

    assert_is_double(mean(acc));

    immediate_mean_t acc2, test_acc2(sample = 0);

    acc2(1, covariate1 = 3);
    BOOST_CHECK_CLOSE(1., mean(acc2), 1e-5);
    BOOST_CHECK_EQUAL(1u, count(acc2));
    BOOST_CHECK_CLOSE(3., (accumulators::mean_of_variates<int, tag::covariate1>(acc2)), 1e-5);

    acc2(0, covariate1 = 4);
    BOOST_CHECK_CLOSE(0.5, mean(acc2), 1e-5);
    BOOST_CHECK_EQUAL(2u, count(acc2));
    BOOST_CHECK_CLOSE(3.5, (accumulators::mean_of_variates<int, tag::covariate1>(acc2)), 1e-5);

    acc2(2, covariate1 = 8);
    BOOST_CHECK_CLOSE(1., mean(acc2), 1e-5);
    BOOST_CHECK_EQUAL(3u, count(acc2));
    BOOST_CHECK_CLOSE(5., (accumulators::mean_of_variates<int, tag::covariate1>(acc2)), 1e-5);

    assert_is_double(mean(acc2));
}

///////////////////////////////////////////////////////////////////////////////
// test_persistency
//
void test_persistency()
{
    std::stringstream ss;
    {
        mean_t acc1;
        immediate_mean_t acc2;

        acc1(1, covariate1 = 3);
        acc1(0, covariate1 = 4);
        acc1(2, covariate1 = 8);
        acc2(1, covariate1 = 3);
        acc2(0, covariate1 = 4);
        acc2(2, covariate1 = 8);
        BOOST_CHECK_CLOSE(1., mean(acc1), 1e-5);
        BOOST_CHECK_CLOSE(5., (accumulators::mean_of_variates<int, tag::covariate1>(acc1)), 1e-5);
        BOOST_CHECK_CLOSE(1., mean(acc2), 1e-5);
        BOOST_CHECK_CLOSE(5., (accumulators::mean_of_variates<int, tag::covariate1>(acc2)), 1e-5);
        boost::archive::text_oarchive oa(ss);
        acc1.serialize(oa, 0);
        acc2.serialize(oa, 0);
    }
    mean_t acc1;
    immediate_mean_t acc2;
    boost::archive::text_iarchive ia(ss);
    acc1.serialize(ia, 0);
    acc2.serialize(ia, 0);
    BOOST_CHECK_CLOSE(1., mean(acc1), 1e-5);
    BOOST_CHECK_CLOSE(5., (accumulators::mean_of_variates<int, tag::covariate1>(acc2)), 1e-5);
}

///////////////////////////////////////////////////////////////////////////////
// init_unit_test_suite
//
test_suite* init_unit_test_suite( int argc, char* argv[] )
{
    test_suite *test = BOOST_TEST_SUITE("mean test");

    test->add(BOOST_TEST_CASE(&test_stat));
    test->add(BOOST_TEST_CASE(&test_persistency));

    return test;
}