stats_mgr_unittest.cc 23.8 KB
Newer Older
1
// Copyright (C) 2012-2016 Internet Systems Consortium, Inc. ("ISC")
2
//
3 4 5
// 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/.
6

7 8
#include <config.h>

9 10 11 12
#include <boost/shared_ptr.hpp>

#include <exceptions/exceptions.h>
#include <dhcp/dhcp4.h>
13
#include <dhcp/dhcp6.h>
14 15
#include <dhcp/pkt4.h>
#include <dhcp/pkt6.h>
16
#include "../stats_mgr.h"
17 18 19

#include <gtest/gtest.h>

20
#include <boost/date_time/posix_time/posix_time.hpp>
21 22
#include <boost/scoped_ptr.hpp>

23 24 25 26 27 28 29 30 31 32 33 34
using namespace std;
using namespace isc;
using namespace isc::dhcp;
using namespace isc::perfdhcp;

namespace {

typedef StatsMgr<dhcp::Pkt4> StatsMgr4;
typedef StatsMgr<dhcp::Pkt6> StatsMgr6;

const uint32_t common_transid = 123;

35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
/// @brief Number of packets to be used for testing packets collecting.
const size_t TEST_COLLECTED_PKT_NUM = 10;

/// @brief DHCPv4 packet with modifiable internal values.
///
/// Currently the only additional modifiable value is a packet
/// timestamp.
class Pkt4Modifiable : public Pkt4 {
public:

    /// @brief Constructor.
    ///
    /// @param msg_type DHCPv4 message type.
    /// @param transid Transaction id.
    Pkt4Modifiable(const uint8_t msg_type, const uint32_t transid)
        : Pkt4(msg_type, transid) {
    }

    /// @brief Modifies packet timestamp.
    ///
    /// @param delta Number of seconds to be added to the current
    /// packet time. If this number is negative, the new timestamp
    /// will point to earlier time than the original timestamp.
    void modifyTimestamp(const long delta) {
        timestamp_ += boost::posix_time::seconds(delta);
    }
};

/// @brief Pointer to the Pkt4Modifiable.
typedef boost::shared_ptr<Pkt4Modifiable> Pkt4ModifiablePtr;

66 67 68 69
class StatsMgrTest : public ::testing::Test {
public:
    StatsMgrTest() {
    }
70

71 72 73 74 75 76 77
    /// \brief Create DHCPv4 packet.
    ///
    /// Method creates DHCPv4 packet and updates its timestamp.
    ///
    /// \param msg_type DHCPv4 message type.
    /// \param transid transaction id for the packet.
    /// \return DHCPv4 packet.
78 79 80
    Pkt4Modifiable* createPacket4(const uint8_t msg_type,
                                  const uint32_t transid) {
        Pkt4Modifiable* pkt = new Pkt4Modifiable(msg_type, transid);
81 82 83
        // Packet timestamp is normally updated by interface
        // manager on packets reception or send. Unit tests
        // do not use interface manager so we need to do it
84
        // ourselves.
85
        pkt->updateTimestamp();
86
        return (pkt);
87
    }
88

89 90 91 92 93 94 95
    /// \brief Create DHCPv6 packet.
    ///
    /// Method creates DHCPv6 packet and updates its timestamp.
    ///
    /// \param msg_type DHCPv6 message type.
    /// \param transid transaction id.
    /// \return DHCPv6 packet.
96 97 98 99 100 101
    Pkt6* createPacket6(const uint8_t msg_type,
                        const uint32_t transid) {
        Pkt6* pkt = new Pkt6(msg_type, transid);
        // Packet timestamp is normally updated by interface
        // manager on packets reception or send. Unit tests
        // do not use interface manager so we need to do it
102
        // ourselves.
103 104 105 106
        pkt->updateTimestamp();
        return pkt;
    }

107 108 109 110
    /// \brief Pass multiple DHCPv6 packets to Statistics Manager.
    ///
    /// Method simulates sending or receiving  multiple DHCPv6 packets.
    ///
111 112 113 114
    /// \note The xchg_type parameter is passed as non-const value to avoid
    /// false cppcheck errors which expect enum value being passed by reference.
    /// This error is not reported when non-const enum is passed by value.
    ///
115 116 117 118 119 120
    /// \param stats_mgr Statistics Manager instance to be used.
    /// \param xchg_type packet exchange types.
    /// \param packet_type DHCPv6 packet type.
    /// \param num_packets packets to be passed to Statistics Manager.
    /// \param receive simulated packets are received (if true)
    /// or sent (if false)
121
    void passMultiplePackets6(const boost::shared_ptr<StatsMgr6> stats_mgr,
122
                              StatsMgr6::ExchangeType xchg_type,
123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
                              const uint8_t packet_type,
                              const int num_packets,
                              const bool receive = false) {
        for (int i = 0; i < num_packets; ++i) {
            boost::shared_ptr<Pkt6>
                packet(createPacket6(packet_type, i));

            if (receive) {
                ASSERT_NO_THROW(
                    stats_mgr->passRcvdPacket(xchg_type, packet);
                );
            } else {
                ASSERT_NO_THROW(
                    stats_mgr->passSentPacket(xchg_type, packet)
                );
            }
        }
    }

142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184
    /// \brief Simulate DHCPv4 DISCOVER-OFFER with delay.
    ///
    /// Method simulates DHCPv4 DISCOVER-OFFER exchange. The OFFER packet
    /// creation is delayed by the specified number of seconds. This imposes
    /// different packet timestamps and affects delay counters in Statistics
    /// Manager.
    ///
    /// \param stats_mgr Statistics Manager instance.
    /// \param delay delay in seconds between DISCOVER and OFFER packets.
    void passDOPacketsWithDelay(const boost::shared_ptr<StatsMgr4> stats_mgr,
                                unsigned int delay,
                                uint32_t transid) {
        boost::shared_ptr<Pkt4> sent_packet(createPacket4(DHCPDISCOVER,
                                                      transid));
        ASSERT_NO_THROW(
            stats_mgr->passSentPacket(StatsMgr4::XCHG_DO, sent_packet)
        );

        // There is way to differentiate timstamps of two packets other than
        // sleep for before we create another packet. Packet is using current
        // time to update its timestamp.
        // Sleeping for X seconds will guarantee that delay between packets
        // will be greater than 1 second. Note that posix time value is
        // transformed to double value and it makes it hard to determine
        // actual value to expect.
        std::cout << "Sleeping for " << delay << "s to test packet delays"
                  << std::endl;
        sleep(delay);

        boost::shared_ptr<Pkt4> rcvd_packet(createPacket4(DHCPOFFER,
                                                      transid));
        ASSERT_NO_THROW(
            stats_mgr->passRcvdPacket(StatsMgr4::XCHG_DO, rcvd_packet);
        );

        // Calculate period between packets.
        boost::posix_time::ptime sent_time = sent_packet->getTimestamp();
        boost::posix_time::ptime rcvd_time = rcvd_packet->getTimestamp();

        ASSERT_FALSE(sent_time.is_not_a_date_time());
        ASSERT_FALSE(rcvd_time.is_not_a_date_time());
    }

185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213
    /// @brief This test verifies that timed out packets are collected.
    ///
    /// @param transid_index Index in the table of transaction ids which
    /// points to the transaction id to be selected for the DHCPOFFER.
    void testSendReceiveCollected(const size_t transid_index) {
        boost::scoped_ptr<StatsMgr4> stats_mgr(new StatsMgr4());
        // The second parameter indicates that transactions older than
        // 2 seconds should be removed and respective packets collected.
        stats_mgr->addExchangeStats(StatsMgr4::XCHG_DO, 2);

        // Transaction ids of packets to be sent. All transaction ids
        // belong to the same bucket (match the transid & 1023 hashing
        // function).
        uint32_t transid[TEST_COLLECTED_PKT_NUM] =
            { 1, 1025, 2049, 3073, 4097, 5121, 6145, 7169, 8193, 9217 };

        // Simulate sending a number of packets.
        for (unsigned int i = 0; i < TEST_COLLECTED_PKT_NUM; ++i) {
            Pkt4ModifiablePtr sent_packet(createPacket4(DHCPDISCOVER,
                                                    transid[i]));
            // For packets with even index of the transaction id we set
            // the packet timestamp to 10s in the past. When DHCPOFFER
            // is processed, the packets with timestamps older than
            // 2s should be collected.
            if (i % 2 == 0) {
                sent_packet->modifyTimestamp(-10);
            }
            ASSERT_NO_THROW(
                stats_mgr->passSentPacket(StatsMgr4::XCHG_DO, sent_packet)
214
            )  << "failure for transaction id " << transid[i];
215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233
        }

        // Create a server response for one of the packets sent.
        Pkt4ModifiablePtr rcvd_packet(createPacket4(DHCPOFFER,
                                                    transid[transid_index]));
        ASSERT_NO_THROW(
            stats_mgr->passRcvdPacket(StatsMgr4::XCHG_DO, rcvd_packet);
        );

        // There is exactly one case (transaction id) for which perfdhcp
        // will find a packet using ordered lookup. In this case, no
        // packets will be collected. Otherwise, for any unordered lookup
        // all packets from a bucket should be collected.
        if (stats_mgr->getUnorderedLookups(StatsMgr4::XCHG_DO) > 0) {
            // All packets in the bucket having even transaction id
            // indexes should be removed.
            EXPECT_EQ(TEST_COLLECTED_PKT_NUM / 2,
                      stats_mgr->getCollectedNum(StatsMgr4::XCHG_DO));
        }
234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261

        // Make sure that we can still use the StatsMgr. It is possible
        // that the pointer to 'next sent' packet was invalidated
        // during packet removal.
        for (unsigned int i = 0; i < TEST_COLLECTED_PKT_NUM; ++i) {
            // Increase transaction ids by 1 so as they don't duplicate
            // with transaction ids of already sent packets.
            Pkt4ModifiablePtr sent_packet(createPacket4(DHCPDISCOVER,
                                                    transid[i] + 1));
            Pkt4ModifiablePtr rcvd_packet(createPacket4(DHCPOFFER,
                                                        transid[i] + 1));
            ASSERT_NO_THROW(
                stats_mgr->passSentPacket(StatsMgr4::XCHG_DO, sent_packet)
            ) << "failure for transaction id " << transid[i];

            ASSERT_NO_THROW(
                stats_mgr->passRcvdPacket(StatsMgr4::XCHG_DO, rcvd_packet);
            ) << "failure for transaction id " << transid[i];
        }

        // We should have processed TEST_COLLECTED_PKT_NUM but it is possible
        // that one of them we couldn't match (orphan packet), because
        // the matched packet had to be collected because of the transaction
        // timeout. Therefore, we have to count both received packets and
        // orhpans.
        EXPECT_EQ(TEST_COLLECTED_PKT_NUM + 1,
                  stats_mgr->getRcvdPacketsNum(StatsMgr4::XCHG_DO) +
                  stats_mgr->getOrphans(StatsMgr4::XCHG_DO));
262
    }
263 264 265 266
};

TEST_F(StatsMgrTest, Constructor) {
    boost::scoped_ptr<StatsMgr4> stats_mgr(new StatsMgr4());
267
    stats_mgr->addExchangeStats(StatsMgr4::XCHG_DO);
268
    EXPECT_DOUBLE_EQ(
269 270 271
        std::numeric_limits<double>::max(),
        stats_mgr->getMinDelay(StatsMgr4::XCHG_DO)
    );
272
    EXPECT_DOUBLE_EQ(0, stats_mgr->getMaxDelay(StatsMgr4::XCHG_DO));
273 274 275 276 277
    EXPECT_EQ(0, stats_mgr->getOrphans(StatsMgr4::XCHG_DO));
    EXPECT_EQ(0, stats_mgr->getOrderedLookups(StatsMgr4::XCHG_DO));
    EXPECT_EQ(0, stats_mgr->getUnorderedLookups(StatsMgr4::XCHG_DO));
    EXPECT_EQ(0, stats_mgr->getSentPacketsNum(StatsMgr4::XCHG_DO));
    EXPECT_EQ(0, stats_mgr->getRcvdPacketsNum(StatsMgr4::XCHG_DO));
Marcin Siodelski's avatar
Marcin Siodelski committed
278
    EXPECT_EQ(0, stats_mgr->getCollectedNum(StatsMgr4::XCHG_DO));
279

280 281 282 283 284
    EXPECT_THROW(stats_mgr->getAvgDelay(StatsMgr4::XCHG_DO), InvalidOperation);
    EXPECT_THROW(stats_mgr->getStdDevDelay(StatsMgr4::XCHG_DO),
                 InvalidOperation);
    EXPECT_THROW(stats_mgr->getAvgUnorderedLookupSetSize(StatsMgr4::XCHG_DO),
                 InvalidOperation);
285 286
}

287
TEST_F(StatsMgrTest, Exchange) {
288
    boost::scoped_ptr<StatsMgr4> stats_mgr(new StatsMgr4());
289 290 291 292
    boost::shared_ptr<Pkt4> sent_packet(createPacket4(DHCPDISCOVER,
                                                      common_transid));
    boost::shared_ptr<Pkt4> rcvd_packet(createPacket4(DHCPOFFER,
                                                      common_transid));
293 294
    // This is expected to throw because XCHG_DO was not yet
    // added to Stats Manager for tracking.
295 296
    ASSERT_FALSE(stats_mgr->hasExchangeStats(StatsMgr4::XCHG_DO));
    ASSERT_FALSE(stats_mgr->hasExchangeStats(StatsMgr4::XCHG_RA));
297 298 299 300 301 302 303 304 305
    EXPECT_THROW(
        stats_mgr->passSentPacket(StatsMgr4::XCHG_DO, sent_packet),
        BadValue
    );
    EXPECT_THROW(
        stats_mgr->passRcvdPacket(StatsMgr4::XCHG_DO, rcvd_packet),
        BadValue
    );

306

307
    // Adding DISCOVER-OFFER exchanges to be tracked by Stats Manager.
308
    stats_mgr->addExchangeStats(StatsMgr4::XCHG_DO);
309 310
    ASSERT_TRUE(stats_mgr->hasExchangeStats(StatsMgr4::XCHG_DO));
    ASSERT_FALSE(stats_mgr->hasExchangeStats(StatsMgr4::XCHG_RA));
311 312
    // The following two attempts are expected to throw because
    // invalid exchange types are passed (XCHG_RA instead of XCHG_DO)
313 314 315 316 317 318 319 320
    EXPECT_THROW(
        stats_mgr->passSentPacket(StatsMgr4::XCHG_RA, sent_packet),
        BadValue
    );
    EXPECT_THROW(
        stats_mgr->passRcvdPacket(StatsMgr4::XCHG_RA, rcvd_packet),
        BadValue
    );
321

322 323
    // The following two attempts are expected to run fine because
    // right exchange type is specified.
324 325 326 327 328 329
    EXPECT_NO_THROW(
        stats_mgr->passSentPacket(StatsMgr4::XCHG_DO, sent_packet)
    );
    EXPECT_NO_THROW(
        stats_mgr->passRcvdPacket(StatsMgr4::XCHG_DO, rcvd_packet)
    );
330 331
}

332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349
TEST_F(StatsMgrTest, MultipleExchanges) {
    boost::shared_ptr<StatsMgr6> stats_mgr(new StatsMgr6());
    stats_mgr->addExchangeStats(StatsMgr6::XCHG_SA);
    stats_mgr->addExchangeStats(StatsMgr6::XCHG_RR);

    // Simulate sending number of solicit packets.
    const int solicit_packets_num = 10;
    passMultiplePackets6(stats_mgr, StatsMgr6::XCHG_SA, DHCPV6_SOLICIT,
                         solicit_packets_num);

    // Simulate sending number of request packets. It is important that
    // number of request packets is different then number of solicit
    // packets. We can now check if right number packets went to
    // the right exchange type group.
    const int request_packets_num = 5;
    passMultiplePackets6(stats_mgr, StatsMgr6::XCHG_RR, DHCPV6_REQUEST,
                         request_packets_num);

350
    // Check if all packets are successfully passed to packet lists.
351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371
    EXPECT_EQ(solicit_packets_num,
              stats_mgr->getSentPacketsNum(StatsMgr6::XCHG_SA));
    EXPECT_EQ(request_packets_num,
              stats_mgr->getSentPacketsNum(StatsMgr6::XCHG_RR));

    // Simulate reception of multiple packets for both SOLICIT-ADVERTISE
    // and REQUEST-REPLY exchanges. Assume no packet drops.
    const bool receive_packets = true;
    passMultiplePackets6(stats_mgr, StatsMgr6::XCHG_SA, DHCPV6_ADVERTISE,
                         solicit_packets_num, receive_packets);

    passMultiplePackets6(stats_mgr, StatsMgr6::XCHG_RR, DHCPV6_REPLY,
                         request_packets_num, receive_packets);

    // Verify that all received packets are counted.
    EXPECT_EQ(solicit_packets_num,
              stats_mgr->getRcvdPacketsNum(StatsMgr6::XCHG_SA));
    EXPECT_EQ(request_packets_num,
              stats_mgr->getRcvdPacketsNum(StatsMgr6::XCHG_RR));
}

372 373 374
TEST_F(StatsMgrTest, ExchangeToString) {
    // Test DHCPv4 specific exchange names.
    EXPECT_EQ("DISCOVER-OFFER",
375 376
              StatsMgr4::exchangeToString(StatsMgr4::XCHG_DO));
    EXPECT_EQ("REQUEST-ACK", StatsMgr4::exchangeToString(StatsMgr4::XCHG_RA));
377 378 379
    EXPECT_EQ("REQUEST-ACK (renewal)",
              StatsMgr4::exchangeToString(StatsMgr4::XCHG_RNA));

380 381 382

    // Test DHCPv6 specific exchange names.
    EXPECT_EQ("SOLICIT-ADVERTISE",
383 384 385 386
              StatsMgr6::exchangeToString(StatsMgr6::XCHG_SA));
    EXPECT_EQ("REQUEST-REPLY", StatsMgr6::exchangeToString(StatsMgr6::XCHG_RR));
    EXPECT_EQ("RENEW-REPLY", StatsMgr6::exchangeToString(StatsMgr6::XCHG_RN));
    EXPECT_EQ("RELEASE-REPLY", StatsMgr6::exchangeToString(StatsMgr6::XCHG_RL));
387 388 389

}

390 391
TEST_F(StatsMgrTest, SendReceiveSimple) {
    boost::scoped_ptr<StatsMgr4> stats_mgr(new StatsMgr4());
392 393 394 395 396
    boost::shared_ptr<Pkt4> sent_packet(createPacket4(DHCPDISCOVER,
                                                      common_transid));
    boost::shared_ptr<Pkt4> rcvd_packet(createPacket4(DHCPOFFER,
                                                      common_transid));
    stats_mgr->addExchangeStats(StatsMgr4::XCHG_DO);
397
    // The following attempt is expected to pass because the right
398
    // exchange type is used.
399 400 401
    ASSERT_NO_THROW(
        stats_mgr->passSentPacket(StatsMgr4::XCHG_DO, sent_packet)
    );
402 403 404 405 406
    // It is ok, to pass to received packets here. First one will
    // be matched with sent packet. The latter one will not be
    // matched with sent packet but orphans counter will simply
    // increase.
    ASSERT_NO_THROW(
407 408
        stats_mgr->passRcvdPacket(StatsMgr4::XCHG_DO, rcvd_packet)
    );
409
    ASSERT_NO_THROW(
410 411
        stats_mgr->passRcvdPacket(StatsMgr4::XCHG_DO, rcvd_packet)
    );
412
    EXPECT_EQ(1, stats_mgr->getOrphans(StatsMgr4::XCHG_DO));
413 414
}

415 416 417 418 419
TEST_F(StatsMgrTest, SendReceiveUnordered) {
    const int packets_num = 10;
    boost::scoped_ptr<StatsMgr4> stats_mgr(new StatsMgr4());
    stats_mgr->addExchangeStats(StatsMgr4::XCHG_DO);

420 421 422
    // Transaction ids of 10 packets to be sent and received.
    uint32_t transid[packets_num] =
        { 1, 1024, 2, 1025, 3, 1026, 4, 1027, 5, 1028 };
423 424 425 426 427 428 429 430
    for (int i = 0; i < packets_num; ++i) {
        boost::shared_ptr<Pkt4> sent_packet(createPacket4(DHCPDISCOVER,
                                                          transid[i]));
        ASSERT_NO_THROW(
            stats_mgr->passSentPacket(StatsMgr4::XCHG_DO, sent_packet)
        );
    }

431 432
    // We are simulating that received packets are coming in reverse order:
    // 1028, 5, 1027 ....
433
    for (int i = 0; i < packets_num; ++i) {
434 435 436
        boost::shared_ptr<Pkt4>
            rcvd_packet(createPacket4(DHCPDISCOVER,
                                      transid[packets_num - 1 - i]));
437 438 439 440
        ASSERT_NO_THROW(
            stats_mgr->passRcvdPacket(StatsMgr4::XCHG_DO, rcvd_packet);
        );
    }
441
    // All packets are expected to match (we did not drop any)
442
    EXPECT_EQ(0, stats_mgr->getOrphans(StatsMgr4::XCHG_DO));
443 444 445 446 447 448
    // Most of the time we have to do unordered lookups except for the last
    // one. Packets are removed from the sent list every time we have a match
    // so eventually we come up with the single packet that caching iterator
    // is pointing to. This is counted as ordered lookup.
    EXPECT_EQ(1, stats_mgr->getOrderedLookups(StatsMgr4::XCHG_DO));
    EXPECT_EQ(9, stats_mgr->getUnorderedLookups(StatsMgr4::XCHG_DO));
449 450
}

451 452 453 454 455 456 457 458
TEST_F(StatsMgrTest, SendReceiveCollectedHighTransid) {
    // Check that the packet collection mechanism works fine
    // for any packet returned by the server.
    for (unsigned int i = 0; i < TEST_COLLECTED_PKT_NUM; ++i) {
        testSendReceiveCollected(i);
    }
}

459 460 461
TEST_F(StatsMgrTest, Orphans) {
    const int packets_num = 6;
    boost::scoped_ptr<StatsMgr4> stats_mgr(new StatsMgr4());
462
    stats_mgr->addExchangeStats(StatsMgr4::XCHG_DO);
463

464
    // We skip every second packet to simulate drops.
465 466 467 468 469 470
    for (int i = 0; i < packets_num; i += 2) {
        boost::shared_ptr<Pkt4> sent_packet(createPacket4(DHCPDISCOVER, i));
        ASSERT_NO_THROW(
            stats_mgr->passSentPacket(StatsMgr4::XCHG_DO, sent_packet)
        );
    }
471
    // We pass all received packets.
472 473 474 475 476 477
    for (int i = 0; i < packets_num; ++i) {
        boost::shared_ptr<Pkt4> rcvd_packet(createPacket4(DHCPOFFER, i));
        ASSERT_NO_THROW(
            stats_mgr->passRcvdPacket(StatsMgr4::XCHG_DO, rcvd_packet);
        );
    }
478 479
    // The half of received packets are expected not to have matching
    // sent packet.
480
    EXPECT_EQ(packets_num / 2, stats_mgr->getOrphans(StatsMgr4::XCHG_DO));
481 482
}

483 484
TEST_F(StatsMgrTest, Delays) {

485
    boost::shared_ptr<StatsMgr4> stats_mgr(new StatsMgr4());
Marcin Siodelski's avatar
Marcin Siodelski committed
486
    stats_mgr->addExchangeStats(StatsMgr4::XCHG_DO, 5);
487

488 489 490
    // Send DISCOVER, wait 2s and receive OFFER. This will affect
    // counters in Stats Manager.
    passDOPacketsWithDelay(stats_mgr, 2, common_transid);
491 492 493 494 495 496 497

    // Initially min delay is equal to MAX_DOUBLE. After first packets
    // are passed, it is expected to set to actual value.
    EXPECT_LT(stats_mgr->getMinDelay(StatsMgr4::XCHG_DO),
              std::numeric_limits<double>::max());
    EXPECT_GT(stats_mgr->getMinDelay(StatsMgr4::XCHG_DO), 1);

498
    // Max delay is supposed to the same value as minimum
499 500 501 502
    // or maximum delay.
    EXPECT_GT(stats_mgr->getMaxDelay(StatsMgr4::XCHG_DO), 1);

    // Delay sums are now the same as minimum or maximum delay.
503 504 505 506 507 508 509 510 511
    EXPECT_GT(stats_mgr->getAvgDelay(StatsMgr4::XCHG_DO), 1);

    // Simulate another DISCOVER-OFFER exchange with delay between
    // sent and received packets. Delay is now shorter than earlier
    // so standard deviation of delay will now increase.
    const unsigned int delay2 = 1;
    passDOPacketsWithDelay(stats_mgr, delay2, common_transid + 1);
    // Standard deviation is expected to be non-zero.
    EXPECT_GT(stats_mgr->getStdDevDelay(StatsMgr4::XCHG_DO), 0);
512 513
}

514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529
TEST_F(StatsMgrTest, CustomCounters) {
    boost::scoped_ptr<StatsMgr4> stats_mgr(new StatsMgr4());

    // Specify counter keys and names.
    const std::string too_short_key("tooshort");
    const std::string too_short_name("Too short packets");
    const std::string too_late_key("toolate");
    const std::string too_late_name("Packets sent too late");

    // Add two custom counters.
    stats_mgr->addCustomCounter(too_short_key, too_short_name);
    stats_mgr->addCustomCounter(too_late_key, too_late_name);

    // Increment one of the counters 10 times.
    const uint64_t tooshort_num = 10;
    for (uint64_t i = 0; i < tooshort_num; ++i) {
530
        stats_mgr->incrementCounter(too_short_key);
531 532 533 534 535
    }

    // Increment another counter by 5 times.
    const uint64_t toolate_num = 5;
    for (uint64_t i = 0; i < toolate_num; ++i) {
536
        stats_mgr->incrementCounter(too_late_key);
537 538 539 540 541 542 543 544 545 546 547 548 549
    }

    // Check counter's current value and name.
    StatsMgr4::CustomCounterPtr tooshort_counter =
        stats_mgr->getCounter(too_short_key);
    EXPECT_EQ(too_short_name, tooshort_counter->getName());
    EXPECT_EQ(tooshort_num, tooshort_counter->getValue());

    // Check counter's current value and name.
    StatsMgr4::CustomCounterPtr toolate_counter =
        stats_mgr->getCounter(too_late_key);
    EXPECT_EQ(too_late_name, toolate_counter->getName());
    EXPECT_EQ(toolate_num, toolate_counter->getValue());
550

551 552
}

553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588
TEST_F(StatsMgrTest, PrintStats) {
    std::cout << "This unit test is checking statistics printing "
              << "capabilities. It is expected that some counters "
              << "will be printed during this test. It may also "
              << "cause spurious errors." << std::endl;
    boost::shared_ptr<StatsMgr6> stats_mgr(new StatsMgr6());
    stats_mgr->addExchangeStats(StatsMgr6::XCHG_SA);

    // Simulate sending and receiving one packet. Otherwise printing
    // functions will complain about lack of packets.
    const int packets_num = 1;
    passMultiplePackets6(stats_mgr, StatsMgr6::XCHG_SA, DHCPV6_SOLICIT,
                         packets_num);
    passMultiplePackets6(stats_mgr, StatsMgr6::XCHG_SA, DHCPV6_ADVERTISE,
                         packets_num, true);

    // This function will print statistics even if packets are not
    // archived because it relies on counters. There is at least one
    // exchange needed to count the average delay and std deviation.
    EXPECT_NO_THROW(stats_mgr->printStats());

    // Printing timestamps is expected to fail because by default we
    // disable packets archiving mode. Without packets we can't get
    // timestamps.
    EXPECT_THROW(stats_mgr->printTimestamps(), isc::InvalidOperation);

    // Now, we create another statistics manager instance and enable
    // packets archiving mode.
    const bool archive_packets = true;
    boost::shared_ptr<StatsMgr6> stats_mgr2(new StatsMgr6(archive_packets));
    stats_mgr2->addExchangeStats(StatsMgr6::XCHG_SA);

    // Timestamps should now get printed because packets have been preserved.
    EXPECT_NO_THROW(stats_mgr2->printTimestamps());
}

589

590
}