Browse Source

[1958] Provided access to all basic counters and created corresponding unit tests.

Marcin Siodelski 12 years ago
parent
commit
5d2116c14a
2 changed files with 394 additions and 51 deletions
  1. 211 43
      tests/tools/perfdhcp/stats_mgr.h
  2. 183 8
      tests/tools/perfdhcp/tests/stats_mgr_unittest.cc

+ 211 - 43
tests/tools/perfdhcp/stats_mgr.h

@@ -85,7 +85,9 @@ public:
                 boost::multi_index::sequenced<>,
                 boost::multi_index::hashed_non_unique<
                         boost::multi_index::global_fun<
-                            boost::shared_ptr<T>, uint32_t, &ExchangeStats::transid_hash
+                            boost::shared_ptr<T>,
+                            uint32_t,
+                            &ExchangeStats::transid_hash
                         >
                 >
             >
@@ -111,7 +113,9 @@ public:
             square_sum_delay_(0.),
             ordered_lookups_(0),
             unordered_lookup_size_sum_(0),
-            unordered_lookups_(0) {
+            unordered_lookups_(0),
+            sent_packets_num_(0),
+            rcvd_packets_num_(0) {
             sent_packets_cache_ = sent_packets_.begin();
         }
 
@@ -121,6 +125,7 @@ public:
         ///
         /// \param packet packet object to be added.
         void appendSent(const boost::shared_ptr<T> packet) {
+            ++sent_packets_num_;
             sent_packets_.template get<0>().push_back(packet);
         }
 
@@ -130,9 +135,55 @@ public:
         ///
         /// \param packet packet object to be added.
         void appendRcvd(const boost::shared_ptr<T> packet) {
+            ++rcvd_packets_num_;
             rcvd_packets_.template get<0>().push_back(packet);
         }
 
+        ///  \brief Update delay counters.
+        ///
+        /// Method updates delay counters based on timestamps of
+        /// sent and received packets.
+        ///
+        /// \param sent_packet sent packet
+        /// \param rcvd_packet received packet
+        /// \throw isc::Unexpected if failed to calculate timestamps
+        void updateDelays(const boost::shared_ptr<T> sent_packet,
+                          const boost::shared_ptr<T> rcvd_packet) {
+            boost::posix_time::ptime sent_time = sent_packet->getTimestamp();
+            boost::posix_time::ptime rcvd_time = rcvd_packet->getTimestamp();
+
+            if (sent_time.is_not_a_date_time() ||
+                rcvd_time.is_not_a_date_time()) {
+                isc_throw(Unexpected,
+                          "Timestamp must be set for sent and "
+                          "received packet to measure RTT");
+            }
+            boost::posix_time::time_period period(sent_time, rcvd_time);
+            // We don't bother calculating deltas in nanoseconds. It is much
+            // more convenient to use seconds instead because we are going to
+            // sum them up.
+            double delta =
+                static_cast<double>(period.length().total_nanoseconds()) / 1e9;
+
+            if (delta < 0) {
+                isc_throw(Unexpected, "Sent packet's timestamp must not be "
+                          "greater than received packet's timestamp");
+            }
+
+            // Record the minimum delay between sent and received packets.
+            if (delta < min_delay_) {
+                min_delay_ = delta;
+            }
+            // Record the maximum delay between sent and received packets.
+            if (delta > max_delay_) {
+                max_delay_ = delta;
+            }
+            // Update delay sum and square sum. That will be used to calculate
+            // mean delays.
+            sum_delay_ += delta;
+            square_sum_delay_ += delta * delta;
+        }
+
         /// \brief Find packet on the list of sent packets.
         ///
         /// Method finds packet with specified transaction id on the list
@@ -149,21 +200,54 @@ public:
         /// not found
         boost::shared_ptr<T> findSent(const uint32_t transid) {
             if (sent_packets_.size() == 0) {
+                // List of sent packets is empty so there is no sense
+                // to continue looking fo the packet. It also means
+                // that the received packet we got has no corresponding
+                // sent packet so orphans counter has to be updated.
                 ++orphans_;
                 return boost::shared_ptr<T>();
             } else if (sent_packets_cache_ == sent_packets_.end()) {
+                // Even if there are still many unmatched packets on the
+                // list we might hit the end of it because of unordered
+                // lookups. The next logical step is to reset cache.
                 sent_packets_cache_ = sent_packets_.begin();
             }
 
+            // With this variable we will be signalling success or failure
+            // to find the packet.
             bool packet_found = false;
+            // Most likely responses are sent from the server in the same
+            // order as client's requests to the server. We are caching
+            // next sent packet and first try to match with it the next
+            // incoming packet. We are successful if there is no
+            // packet drop or out of order packets sent. This is actually
+            // the fastest way to look for packets.
             if ((*sent_packets_cache_)->getTransid() == transid) {
                 ++ordered_lookups_;
                 packet_found = true;
             } else {
+                // If we are here, it means that we were unable to match the
+                // next incoming packet with next sent packet so we need to
+                // take a little more expensive approach to look packets using
+                // alternative index (transaction id & 1023).
                 PktListTransidIndex& idx = sent_packets_.template get<1>();
+                // Packets are grouped using trasaction id masking with value
+                // of 1023. For instance, packets with transaction id equal to
+                // 1, 1024 ... will belong to the same group (a.k.a. bucket).
+                // When using alternative index we don't find the packet but
+                // bucket of packets and need to iterate through the bucket
+                // to find the one that has desired transaction id.
                 std::pair<PktListTransidIterator,PktListTransidIterator> p =
                     idx.equal_range(transid & 1023);
+                // We want to keep statistics of unordered lookups to make
+                // sure that there is a right balance before number of
+                // unordered lookups and ordered lookups. If number of unordered
+                // lookups is high it may mean that many packets are lost or
+                // sent out of order.
                 ++unordered_lookups_;
+                // We also want to keep the mean value of the bucket. The lower
+                // bucket size the better. If bucket sizes appear to big we
+                // might want to increase number of buckets.
                 unordered_lookup_size_sum_ += std::distance(p.first, p.second);
                 for (PktListTransidIterator it = p.first; it != p.second;
                      ++it) {
@@ -177,53 +261,20 @@ public:
             }
 
             if (!packet_found) {
+                // If we are here, it means that both ordered lookup and
+                // unordered lookup failed. Searched packet is not on the list.
                 ++orphans_;
                 return boost::shared_ptr<T>();
             }
 
             boost::shared_ptr<T> sent_packet(*sent_packets_cache_);
-            ++sent_packets_cache_;
+            // If packet was found, we assume it will be never searched
+            // again. We want to delete this packet from the list to
+            // improve performance of future searches.
+            sent_packets_cache_ = eraseSent(sent_packets_cache_);
             return sent_packet;
         }
 
-        ///  \brief Update delay counters.
-        ///
-        /// Method updates delay counters based on timestamps of
-        /// sent and received packets.
-        ///
-        /// \param sent_packet sent packet
-        /// \param rcvd_packet received packet
-        /// \throw isc::Unexpected if failed to calculate timestamps
-        void updateDelays(const boost::shared_ptr<T> sent_packet,
-                          const boost::shared_ptr<T> rcvd_packet) {
-            boost::posix_time::ptime sent_time = sent_packet->getTimestamp();
-            boost::posix_time::ptime rcvd_time = rcvd_packet->getTimestamp();
-
-            if (sent_time.is_not_a_date_time() ||
-                rcvd_time.is_not_a_date_time()) {
-                isc_throw(Unexpected,
-                          "Timestamp must be set for sent and "
-                          "received packet to measure RTT");
-            }
-            boost::posix_time::time_period period(sent_time, rcvd_time);
-            double delta =
-                static_cast<double>(period.length().total_nanoseconds()) / 1e9;
-
-            if (delta < 0) {
-                isc_throw(Unexpected, "Sent packet's timestamp must not be "
-                          "greater than received packet's timestamp");
-            }
-
-            if (delta < min_delay_) {
-                min_delay_ = delta;
-            }
-            if (delta > max_delay_) {
-                max_delay_ = delta;
-            }
-            sum_delay_ += delta;
-            square_sum_delay_ += delta * delta;
-        }
-
         /// \brief Return minumum delay between sent and received packet.
         ///
         /// Method returns minimum delay between sent and received packet.
@@ -271,6 +322,9 @@ public:
         ///
         /// \return average unordered lookup set size.
         double getAvgUnorderedLookupSetSize() const {
+            if (unordered_lookups_ == 0) {
+                return 0.;
+            }
             return static_cast<double>(unordered_lookup_size_sum_) /
                 static_cast<double>(unordered_lookups_);
         }
@@ -285,7 +339,6 @@ public:
         /// \return number of unordered lookups.
         uint64_t getUnorderedLookups() const { return unordered_lookups_; }
 
-
         /// \brief Return number of ordered sent packets lookups
         ///
         /// Method returns number of ordered sent packet lookups.
@@ -296,6 +349,25 @@ public:
         ///
         /// \return number of ordered lookups.
         uint64_t getOrderedLookups() const { return ordered_lookups_; }
+
+        /// \brief Return total number of sent packets
+        ///
+        /// Method returns total number of sent packets.
+        ///
+        /// \return number of sent packets.
+        uint64_t getSentPacketsNum() const {
+            return sent_packets_num_;
+        }
+
+        /// \brief Return total number of received packets
+        ///
+        /// Method returns total number of received packets.
+        ///
+        /// \return number of received packets.
+        uint64_t getRcvdPacketsNum() const {
+            return rcvd_packets_num_;
+        }
+
     private:
 
         /// \brief Private default constructor.
@@ -304,6 +376,18 @@ public:
         /// class to specify exchange type explicitely.
         ExchangeStats();
 
+
+        /// \brief Erase packet from the list of sent packets.
+        ///
+        /// Method erases packet from the list of sent packets.
+        ///
+        /// \param it iterator pointing to packet to be erased.
+        /// \return iterator pointing to packet following erased
+        /// packet or sent_packets_.end() if packet not found.
+         PktListIterator eraseSent(const PktListIterator it) {
+            return sent_packets_.template get<0>().erase(it);
+        }
+
         ExchangeType xchg_type_;             ///< Packet exchange type.
         PktList sent_packets_;               ///< List of sent packets.
 
@@ -337,6 +421,8 @@ public:
         uint64_t ordered_lookups_;     ///< Number of ordered sent packets
                                        ///< lookups.
 
+        uint64_t sent_packets_num_;    ///< Total number of sent packets.
+        uint64_t rcvd_packets_num_;    ///< Total number of received packets.
     };
 
     /// Pointer to ExchangeStats.
@@ -400,6 +486,59 @@ public:
         }
     }
 
+    /// \brief Return minumum delay between sent and received packet.
+    ///
+    /// Method returns minimum delay between sent and received packet
+    /// for specified exchange type.
+    ///
+    /// \param xchg_type exchange type.
+    /// \throw isc::BadValue if invalid exchange type specified.
+    /// \return minimum delay between packets.
+    double getMinDelay(const ExchangeType xchg_type) const {
+        ExchangeStatsPtr xchg_stats = getExchangeStats(xchg_type);
+        return xchg_stats->getMinDelay();
+    }
+
+    /// \brief Return maxmimum delay between sent and received packet.
+    ///
+    /// Method returns maximum delay between sent and received packet
+    /// for specified exchange type.
+    ///
+    /// \param xchg_type exchange type.
+    /// \throw isc::BadValue if invalid exchange type specified.
+    /// \return maximum delay between packets.
+    double getMaxDelay(const ExchangeType xchg_type) const {
+        ExchangeStatsPtr xchg_stats = getExchangeStats(xchg_type);
+        return xchg_stats->getMaxDelay();
+    }
+
+    /// \brief Return sum of delays between sent and received packets.
+    ///
+    /// Method returns sum of delays between sent and received packets
+    /// for specified exchange type.
+    ///
+    /// \param xchg_type exchange type.
+    /// \throw isc::BadValue if invalid exchange type specified.
+    /// \return sum of delays between sent and received packets.
+    double getSumDelay(const ExchangeType xchg_type) const {
+        ExchangeStatsPtr xchg_stats = getExchangeStats(xchg_type);
+        return xchg_stats->getSumDelay();
+    }
+
+    /// \brief Return square sum of delays between sent and received
+    /// packets.
+    ///
+    /// Method returns square sum of delays between sent and received
+    /// packets for specified exchange type.
+    ///
+    /// \param xchg_type exchange type.
+    /// \throw isc::BadValue if invalid exchange type specified.
+    /// \return square sum of delays between sent and received packets.
+    double getSquareSumDelay(const ExchangeType xchg_type) const {
+        ExchangeStatsPtr xchg_stats = getExchangeStats(xchg_type);
+        return xchg_stats->getSquareSumDelay();
+    }
+
     /// \brief Return number of orphant packets.
     ///
     /// Method returns number of orphant packets for specified
@@ -420,6 +559,7 @@ public:
     /// unordered packet lookup using transaction id.
     ///
     /// \param xchg_type exchange type.
+    /// \throw isc::BadValue if invalid exchange type specified.
     /// \return average unordered lookup set size.
     double getAvgUnorderedLookupSetSize(const ExchangeType xchg_type) const {
         ExchangeStatsPtr xchg_stats = getExchangeStats(xchg_type);
@@ -433,6 +573,8 @@ public:
     /// out of order by server - transaction id of received
     /// packet does not match transaction id of next sent packet.
     ///
+    /// \param xchg_type exchange type.
+    /// \throw isc::BadValue if invalid exchange type specified.
     /// \return number of unordered lookups.
     uint64_t getUnorderedLookups(const ExchangeType xchg_type) const {
         ExchangeStatsPtr xchg_stats = getExchangeStats(xchg_type);
@@ -447,14 +589,40 @@ public:
     /// If packets are skipped or received out of order, lookup
     /// function will use unordered lookup (with hash table).
     ///
+    /// \param xchg_type exchange type.
+    /// \throw isc::BadValue if invalid exchange type specified.
     /// \return number of ordered lookups.
     uint64_t getOrderedLookups(const ExchangeType xchg_type) const {
         ExchangeStatsPtr xchg_stats = getExchangeStats(xchg_type);
         return xchg_stats->getOrderedLookups();
     }
 
-private:
+    /// \brief Return total number of sent packets
+    ///
+    /// Method returns total number of sent packets for specified
+    /// exchange type.
+    ///
+    /// \param xchg_type exchange type.
+    /// \throw isc::BadValue if invalid exchange type specified.
+    /// \return number of sent packets.
+    uint64_t getSentPacketsNum(const ExchangeType xchg_type) const {
+        ExchangeStatsPtr xchg_stats = getExchangeStats(xchg_type);
+        return xchg_stats->getSentPacketsNum();
+    }
 
+    /// \brief Return total number of received packets
+    ///
+    /// Method returns total number of received packets for specified
+    /// exchange type.
+    ///
+    /// \param xchg_type exchange type.
+    /// \throw isc::BadValue if invalid exchange type specified.
+    /// \return number of received packets.
+    uint64_t getRcvdPacketsNum(const ExchangeType xchg_type) const {
+        ExchangeStatsPtr xchg_stats = getExchangeStats(xchg_type);
+        return xchg_stats->getRcvdPacketsNum();
+    }
+private:
     /// \brief Return exchange stats object for given exchange type
     ///
     /// Method returns exchange stats object for given exchange type.

+ 183 - 8
tests/tools/perfdhcp/tests/stats_mgr_unittest.cc

@@ -16,6 +16,7 @@
 
 #include <exceptions/exceptions.h>
 #include <dhcp/dhcp4.h>
+#include <dhcp/dhcp6.h>
 #include <dhcp/pkt4.h>
 #include <dhcp/pkt6.h>
 
@@ -43,21 +44,79 @@ public:
     Pkt4* createPacket4(const uint8_t msg_type,
                         const uint32_t transid) {
         Pkt4* pkt = new Pkt4(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
+        // ourselfs.
         pkt->updateTimestamp();
         return pkt;
     }
+
+    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
+        // ourselfs.
+        pkt->updateTimestamp();
+        return pkt;
+    }
+
+    void passMultiplePackets6(const boost::shared_ptr<StatsMgr6> stats_mgr,
+                              const StatsMgr6::ExchangeType xchg_type,
+                              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)
+                );
+            }
+        }
+    }
+
 };
 
 TEST_F(StatsMgrTest, Constructor) {
     boost::scoped_ptr<StatsMgr4> stats_mgr(new StatsMgr4());
+    stats_mgr->addExchangeStats(StatsMgr4::XCHG_DO);
+    EXPECT_EQ(
+        std::numeric_limits<double>::max(),
+        stats_mgr->getMinDelay(StatsMgr4::XCHG_DO)
+    );
+    EXPECT_EQ(0, stats_mgr->getMaxDelay(StatsMgr4::XCHG_DO));
+    EXPECT_EQ(0, stats_mgr->getSumDelay(StatsMgr4::XCHG_DO));
+    EXPECT_EQ(0, stats_mgr->getOrphans(StatsMgr4::XCHG_DO));
+    EXPECT_EQ(0, stats_mgr->getSquareSumDelay(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));
+
+    double avg_size = 0.;
+    ASSERT_NO_THROW(
+        avg_size = stats_mgr->getAvgUnorderedLookupSetSize(StatsMgr4::XCHG_DO);
+    );
+    EXPECT_EQ(0., avg_size);
 }
 
-TEST_F(StatsMgrTest, Exchanges) {
+TEST_F(StatsMgrTest, Exchange) {
     boost::scoped_ptr<StatsMgr4> stats_mgr(new StatsMgr4());
     boost::shared_ptr<Pkt4> sent_packet(createPacket4(DHCPDISCOVER,
                                                       common_transid));
     boost::shared_ptr<Pkt4> rcvd_packet(createPacket4(DHCPOFFER,
                                                       common_transid));
+    // This is expected to throw because XCHG_DO was not yet
+    // added to Stats Manager for tracking.
     EXPECT_THROW(
         stats_mgr->passSentPacket(StatsMgr4::XCHG_DO, sent_packet),
         BadValue
@@ -67,7 +126,10 @@ TEST_F(StatsMgrTest, Exchanges) {
         BadValue
     );
 
+    // Adding DISCOVER-OFFER exchanges to be tracked by Stats Manager.
     stats_mgr->addExchangeStats(StatsMgr4::XCHG_DO);
+    // The following two attempts are expected to throw because
+    // invalid exchange types are passed (XCHG_RA instead of XCHG_DO)
     EXPECT_THROW(
         stats_mgr->passSentPacket(StatsMgr4::XCHG_RA, sent_packet),
         BadValue
@@ -77,6 +139,8 @@ TEST_F(StatsMgrTest, Exchanges) {
         BadValue
     );
 
+    // The following two attempts are expected to run fine because
+    // right exchange type is specified.
     EXPECT_NO_THROW(
         stats_mgr->passSentPacket(StatsMgr4::XCHG_DO, sent_packet)
     );
@@ -85,6 +149,46 @@ TEST_F(StatsMgrTest, Exchanges) {
     );
 }
 
+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);
+
+    // Check if all packets are successfuly passed to packet lists.
+    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));
+}
+
 TEST_F(StatsMgrTest, SendReceiveSimple) {
     boost::scoped_ptr<StatsMgr4> stats_mgr(new StatsMgr4());
     boost::shared_ptr<Pkt4> sent_packet(createPacket4(DHCPDISCOVER,
@@ -92,15 +196,22 @@ TEST_F(StatsMgrTest, SendReceiveSimple) {
     boost::shared_ptr<Pkt4> rcvd_packet(createPacket4(DHCPOFFER,
                                                       common_transid));
     stats_mgr->addExchangeStats(StatsMgr4::XCHG_DO);
+    // The following attempt is expected to pass becase the right
+    // exchange type is used.
     ASSERT_NO_THROW(
         stats_mgr->passSentPacket(StatsMgr4::XCHG_DO, sent_packet)
     );
-    EXPECT_NO_THROW(
+    // 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(
         stats_mgr->passRcvdPacket(StatsMgr4::XCHG_DO, rcvd_packet)
     );
-    EXPECT_NO_THROW(
+    ASSERT_NO_THROW(
         stats_mgr->passRcvdPacket(StatsMgr4::XCHG_DO, rcvd_packet)
     );
+    EXPECT_EQ(1, stats_mgr->getOrphans(StatsMgr4::XCHG_DO));
 }
 
 TEST_F(StatsMgrTest, SendReceiveUnordered) {
@@ -108,7 +219,9 @@ TEST_F(StatsMgrTest, SendReceiveUnordered) {
     boost::scoped_ptr<StatsMgr4> stats_mgr(new StatsMgr4());
     stats_mgr->addExchangeStats(StatsMgr4::XCHG_DO);
 
-    uint32_t transid[packets_num] = { 1, 1024, 2, 1025, 3, 1026, 4, 1027, 5, 1028 };
+    // 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 };
     for (int i = 0; i < packets_num; ++i) {
         boost::shared_ptr<Pkt4> sent_packet(createPacket4(DHCPDISCOVER,
                                                           transid[i]));
@@ -117,16 +230,24 @@ TEST_F(StatsMgrTest, SendReceiveUnordered) {
         );
     }
 
+    // We are simulating that received packets are coming in reverse order:
+    // 1028, 5, 1027 ....
     for (int i = 0; i < packets_num; ++i) {
-        boost::shared_ptr<Pkt4> rcvd_packet(createPacket4(DHCPDISCOVER,
-                                                          transid[packets_num - 1 - i]));
+        boost::shared_ptr<Pkt4>
+            rcvd_packet(createPacket4(DHCPDISCOVER,
+                                      transid[packets_num - 1 - i]));
         ASSERT_NO_THROW(
             stats_mgr->passRcvdPacket(StatsMgr4::XCHG_DO, rcvd_packet);
         );
     }
+    // All packets are expected to match (we did not drop any)
     EXPECT_EQ(0, stats_mgr->getOrphans(StatsMgr4::XCHG_DO));
-    EXPECT_EQ(10, stats_mgr->getUnorderedLookups(StatsMgr4::XCHG_DO));
-    std::cout << stats_mgr->getAvgUnorderedLookupSetSize(StatsMgr4::XCHG_DO) << std::endl;
+    // 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));
 }
 
 TEST_F(StatsMgrTest, Orphans) {
@@ -134,19 +255,73 @@ TEST_F(StatsMgrTest, Orphans) {
     boost::scoped_ptr<StatsMgr4> stats_mgr(new StatsMgr4());
     stats_mgr->addExchangeStats(StatsMgr4::XCHG_DO);
 
+    // We skip every second packet to simulate drops.
     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)
         );
     }
+    // We pass all received packets.
     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);
         );
     }
+    // The half of received packets are expected not to have matching
+    // sent packet.
     EXPECT_EQ(packets_num / 2, stats_mgr->getOrphans(StatsMgr4::XCHG_DO));
 }
 
+TEST_F(StatsMgrTest, Delays) {
+    boost::scoped_ptr<StatsMgr4> stats_mgr(new StatsMgr4());
+    stats_mgr->addExchangeStats(StatsMgr4::XCHG_DO);
+
+    boost::shared_ptr<Pkt4> sent_packet(createPacket4(DHCPDISCOVER,
+                                                      common_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 two 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 2 seconds to test packet delays" << std::endl;
+    sleep(2);
+
+    boost::shared_ptr<Pkt4> rcvd_packet(createPacket4(DHCPOFFER,
+                                                      common_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());
+
+    boost::posix_time::time_period period(sent_time, rcvd_time);
+
+    // 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);
+
+    // Max delay is supposed to the same value as mininimum
+    // or maximum delay.
+    EXPECT_GT(stats_mgr->getMaxDelay(StatsMgr4::XCHG_DO), 1);
+
+    // Delay sums are now the same as minimum or maximum delay.
+    EXPECT_GT(stats_mgr->getSumDelay(StatsMgr4::XCHG_DO), 1);
+    EXPECT_GT(stats_mgr->getSquareSumDelay(StatsMgr4::XCHG_DO), 1);
+}
+
 }