src: Implement packet statistics collection

refs: #2955, #2956

Change-Id: I57476a63562dbd378e566ad0280c89ccc5883e3e
diff --git a/src/hello-protocol.cpp b/src/hello-protocol.cpp
index 4a995de..2e17e16 100644
--- a/src/hello-protocol.cpp
+++ b/src/hello-protocol.cpp
@@ -19,9 +19,9 @@
  *
  **/
 
+#include "hello-protocol.hpp"
 #include "nlsr.hpp"
 #include "lsdb.hpp"
-#include "hello-protocol.hpp"
 #include "utility/name-helper.hpp"
 #include "logger.hpp"
 
@@ -47,6 +47,9 @@
                                                  this, _1),
                                        std::bind(&HelloProtocol::processInterestTimedOut,
                                                  this, _1));
+
+  // increment SENT_HELLO_INTEREST
+  hpIncrementSignal(Statistics::PacketType::SENT_HELLO_INTEREST);
 }
 
 void
@@ -85,12 +88,17 @@
 {
   // interest name: /<neighbor>/NLSR/INFO/<router>
   const ndn::Name interestName = interest.getName();
+
+  // increment RCV_HELLO_INTEREST
+  hpIncrementSignal(Statistics::PacketType::RCV_HELLO_INTEREST);
+
   _LOG_DEBUG("Interest Received for Name: " << interestName);
   if (interestName.get(-2).toUri() != INFO_COMPONENT) {
     _LOG_DEBUG("INFO_COMPONENT not found or interestName: " << interestName
                << " does not match expression");
     return;
   }
+
   ndn::Name neighbor;
   neighbor.wireDecode(interestName.get(-1).blockFromValue());
   _LOG_DEBUG("Neighbor: " << neighbor);
@@ -103,6 +111,8 @@
     m_nlsr.getKeyChain().sign(*data, m_nlsr.getDefaultCertName());
     _LOG_DEBUG("Sending out data for name: " << interest.getName());
     m_nlsr.getNlsrFace().put(*data);
+    // increment SENT_HELLO_DATA
+    hpIncrementSignal(Statistics::PacketType::SENT_HELLO_DATA);
 
     auto adjacent = m_nlsr.getAdjacencyList().findAdjacent(neighbor);
     // If this neighbor was previously inactive, send our own hello interest, too
@@ -209,6 +219,8 @@
       }
     }
   }
+  // increment RCV_HELLO_DATA
+  hpIncrementSignal(Statistics::PacketType::RCV_HELLO_DATA);
 }
 
   // Simply logs a debug message that the content could not be
diff --git a/src/hello-protocol.hpp b/src/hello-protocol.hpp
index 808b843..dbc12d0 100644
--- a/src/hello-protocol.hpp
+++ b/src/hello-protocol.hpp
@@ -22,8 +22,10 @@
 #ifndef NLSR_HELLO_PROTOCOL_HPP
 #define NLSR_HELLO_PROTOCOL_HPP
 
+#include "statistics.hpp"
 #include "test-access-control.hpp"
 
+#include <ndn-cxx/util/signal.hpp>
 #include <ndn-cxx/face.hpp>
 #include <ndn-cxx/mgmt/nfd/control-parameters.hpp>
 #include <ndn-cxx/mgmt/nfd/control-response.hpp>
@@ -93,6 +95,8 @@
   void
   processInterest(const ndn::Name& name, const ndn::Interest& interest);
 
+  ndn::util::signal::Signal<HelloProtocol, Statistics::PacketType> hpIncrementSignal;
+
 private:
   void
   processInterestTimedOut(const ndn::Interest& interest);
diff --git a/src/lsdb.cpp b/src/lsdb.cpp
index b71b48e..5cfc783 100644
--- a/src/lsdb.cpp
+++ b/src/lsdb.cpp
@@ -29,8 +29,6 @@
 #include <ndn-cxx/security/signing-helpers.hpp>
 #include <ndn-cxx/util/segment-fetcher.hpp>
 
-#include <string>
-
 namespace nlsr {
 
 INIT_LOGGER("Lsdb");
@@ -961,6 +959,9 @@
 Lsdb::expressInterest(const ndn::Name& interestName, uint32_t timeoutCount,
                       steady_clock::TimePoint deadline)
 {
+  // increment SENT_LSA_INTEREST
+  lsaIncrementSignal(Statistics::PacketType::SENT_LSA_INTEREST);
+
   if (deadline == DEFAULT_LSA_RETRIEVAL_DEADLINE) {
     deadline = steady_clock::now() + ndn::time::seconds(static_cast<int>(LSA_REFRESH_TIME_MAX));
   }
@@ -991,11 +992,28 @@
                                    std::bind(&Lsdb::afterFetchLsa, this, _1, interestName),
                                    std::bind(&Lsdb::onFetchLsaError, this, _1, _2, interestName,
                                              timeoutCount, deadline, lsaName, seqNo));
+  // increment a specific SENT_LSA_INTEREST
+  std::string typeLSA = interestName[-2].toUri();
+  if (typeLSA == AdjLsa::TYPE_STRING) {
+    lsaIncrementSignal(Statistics::PacketType::SENT_ADJ_LSA_INTEREST);
+  }
+  else if (typeLSA == CoordinateLsa::TYPE_STRING) {
+    lsaIncrementSignal(Statistics::PacketType::SENT_COORD_LSA_INTEREST);
+  }
+  else if (typeLSA == NameLsa::TYPE_STRING) {
+    lsaIncrementSignal(Statistics::PacketType::SENT_NAME_LSA_INTEREST);
+  }
+  else {
+    _LOG_ERROR("typeLSA " + typeLSA + " not recognized; failed Statistics::PacketType conversion");
+  }
 }
 
 void
 Lsdb::processInterest(const ndn::Name& name, const ndn::Interest& interest)
 {
+  // increment RCV_LSA_INTEREST
+  lsaIncrementSignal(Statistics::PacketType::RCV_LSA_INTEREST);
+
   const ndn::Name& interestName(interest.getName());
   _LOG_DEBUG("Interest received for LSA: " << interestName);
 
@@ -1014,7 +1032,6 @@
 
     std::string interestedLsType = interestName[-2].toUri();
 
-    // Passes the Interest off to the appropriate subprocessor
     if (interestedLsType == NameLsa::TYPE_STRING) {
       processInterestForNameLsa(interest, originRouter.append(interestedLsType), seqNo);
     }
@@ -1027,6 +1044,7 @@
     else {
       _LOG_WARN("Received unrecognized LSA type: " << interestedLsType);
     }
+    lsaIncrementSignal(Statistics::PacketType::SENT_LSA_DATA);
   }
 }
 
@@ -1056,13 +1074,16 @@
                                 const ndn::Name& lsaKey,
                                 uint64_t seqNo)
 {
-
+  // increment RCV_NAME_LSA_INTEREST
+  lsaIncrementSignal(Statistics::PacketType::RCV_NAME_LSA_INTEREST);
   _LOG_DEBUG("nameLsa interest " << interest << " received");
   NameLsa*  nameLsa = m_nlsr.getLsdb().findNameLsa(lsaKey);
   if (nameLsa != 0) {
     if (nameLsa->getLsSeqNo() == seqNo) {
       std::string content = nameLsa->getData();
       putLsaData(interest,content);
+      // increment SENT_NAME_LSA_DATA
+      lsaIncrementSignal(Statistics::PacketType::SENT_NAME_LSA_DATA);
     }
     else {
       _LOG_TRACE("SeqNo for nameLsa does not match");
@@ -1087,12 +1108,16 @@
     _LOG_ERROR("Received interest for an adjacency LSA when hyperbolic routing is enabled");
   }
 
+  // increment RCV_ADJ_LSA_INTEREST
+  lsaIncrementSignal(Statistics::PacketType::RCV_ADJ_LSA_INTEREST);
   _LOG_DEBUG("AdjLsa interest " << interest << " received");
   AdjLsa* adjLsa = m_nlsr.getLsdb().findAdjLsa(lsaKey);
   if (adjLsa != 0) {
     if (adjLsa->getLsSeqNo() == seqNo) {
       std::string content = adjLsa->getData();
       putLsaData(interest,content);
+      // increment SENT_ADJ_LSA_DATA
+      lsaIncrementSignal(Statistics::PacketType::SENT_ADJ_LSA_DATA);
     }
     else {
       _LOG_TRACE("SeqNo for AdjLsa does not match");
@@ -1117,12 +1142,16 @@
     _LOG_ERROR("Received Interest for a coordinate LSA when link-state routing is enabled");
   }
 
+  // increment RCV_COORD_LSA_INTEREST
+  lsaIncrementSignal(Statistics::PacketType::RCV_COORD_LSA_INTEREST);
   _LOG_DEBUG("CoordinateLsa interest " << interest << " received");
   CoordinateLsa* corLsa = m_nlsr.getLsdb().findCoordinateLsa(lsaKey);
   if (corLsa != 0) {
     if (corLsa->getLsSeqNo() == seqNo) {
       std::string content = corLsa->getData();
       putLsaData(interest,content);
+      // increment SENT_COORD_LSA_DATA
+      lsaIncrementSignal(Statistics::PacketType::SENT_COORD_LSA_DATA);
     }
     else {
       _LOG_TRACE("SeqNo for CoordinateLsa does not match");
@@ -1166,6 +1195,9 @@
     else {
       _LOG_WARN("Received unrecognized LSA Type: " << interestedLsType);
     }
+
+    // increment RCV_LSA_DATA
+    lsaIncrementSignal(Statistics::PacketType::RCV_LSA_DATA);
   }
 }
 
@@ -1173,6 +1205,8 @@
 Lsdb::processContentNameLsa(const ndn::Name& lsaKey,
                             uint64_t lsSeqNo, std::string& dataContent)
 {
+  // increment RCV_NAME_LSA_DATA
+  lsaIncrementSignal(Statistics::PacketType::RCV_NAME_LSA_DATA);
   if (isNameLsaNew(lsaKey, lsSeqNo)) {
     NameLsa nameLsa;
     if (nameLsa.initializeFromContent(dataContent)) {
@@ -1188,6 +1222,8 @@
 Lsdb::processContentAdjacencyLsa(const ndn::Name& lsaKey,
                                  uint64_t lsSeqNo, std::string& dataContent)
 {
+  // increment RCV_ADJ_LSA_DATA
+  lsaIncrementSignal(Statistics::PacketType::RCV_ADJ_LSA_DATA);
   if (isAdjLsaNew(lsaKey, lsSeqNo)) {
     AdjLsa adjLsa;
     if (adjLsa.initializeFromContent(dataContent)) {
@@ -1203,6 +1239,8 @@
 Lsdb::processContentCoordinateLsa(const ndn::Name& lsaKey,
                                   uint64_t lsSeqNo, std::string& dataContent)
 {
+  // increment RCV_COORD_LSA_DATA
+  lsaIncrementSignal(Statistics::PacketType::RCV_COORD_LSA_DATA);
   if (isCoordinateLsaNew(lsaKey, lsSeqNo)) {
     CoordinateLsa corLsa;
     if (corLsa.initializeFromContent(dataContent)) {
diff --git a/src/lsdb.hpp b/src/lsdb.hpp
index 2b182bd..bb0b9d8 100644
--- a/src/lsdb.hpp
+++ b/src/lsdb.hpp
@@ -27,6 +27,14 @@
 #include "sequencing-manager.hpp"
 #include "test-access-control.hpp"
 #include "communication/sync-logic-handler.hpp"
+#include "statistics.hpp"
+
+#include <ndn-cxx/security/key-chain.hpp>
+#include <ndn-cxx/util/signal.hpp>
+#include <ndn-cxx/util/time.hpp>
+
+#include <utility>
+#include <boost/cstdint.hpp>
 
 #include <utility>
 #include <boost/cstdint.hpp>
@@ -374,6 +382,8 @@
 public:
   static const ndn::Name::Component NAME_COMPONENT;
 
+  ndn::util::signal::Signal<Lsdb, Statistics::PacketType> lsaIncrementSignal;
+
 private:
   Nlsr& m_nlsr;
   ndn::Scheduler& m_scheduler;
diff --git a/src/nlsr.cpp b/src/nlsr.cpp
index 55d426a..70648e3 100644
--- a/src/nlsr.cpp
+++ b/src/nlsr.cpp
@@ -81,6 +81,7 @@
   , m_nfdRibCommandProcessor(m_localhostDispatcher,
                              m_namePrefixList,
                              m_nlsrLsdb)
+  , m_statsCollector(m_nlsrLsdb, m_helloProtocol)
   , m_faceMonitor(m_nlsrFace)
   , m_firstHelloInterval(FIRST_HELLO_INTERVAL_DEFAULT)
 {
diff --git a/src/nlsr.hpp b/src/nlsr.hpp
index 1a3432a..54af8b4 100644
--- a/src/nlsr.hpp
+++ b/src/nlsr.hpp
@@ -38,6 +38,7 @@
 #include "update/prefix-update-processor.hpp"
 #include "update/nfd-rib-command-processor.hpp"
 #include "utility/name-helper.hpp"
+#include "stats-collector.hpp"
 
 #include <stdexcept>
 
@@ -376,6 +377,11 @@
   canonizeNeighborUris(std::list<Adjacent>::iterator currentNeighbor,
                        std::function<void(std::list<Adjacent>::iterator)> then);
 
+  StatsCollector&
+  getStatsCollector()
+  {
+    return m_statsCollector;
+  }
 
 PUBLIC_WITH_TESTS_ELSE_PRIVATE:
   void
@@ -470,6 +476,7 @@
   ndn::Name m_defaultCertName;
   update::PrefixUpdateProcessor m_prefixUpdateProcessor;
   update::NfdRibCommandProcessor m_nfdRibCommandProcessor;
+  StatsCollector m_statsCollector;
 
   ndn::nfd::FaceMonitor m_faceMonitor;
 
diff --git a/src/statistics.cpp b/src/statistics.cpp
new file mode 100644
index 0000000..f5f5516
--- /dev/null
+++ b/src/statistics.cpp
@@ -0,0 +1,101 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014-2017,  The University of Memphis,
+ *                           Regents of the University of California,
+ *                           Arizona Board of Regents.
+ *
+ * This file is part of NLSR (Named-data Link State Routing).
+ * See AUTHORS.md for complete list of NLSR authors and contributors.
+ *
+ * NLSR is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ *
+ * NLSR 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 GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * NLSR, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+#include "statistics.hpp"
+#include "nlsr.hpp"
+#include "utility/name-helper.hpp"
+
+namespace nlsr {
+
+size_t
+Statistics::get(PacketType type) const
+{
+  std::map<PacketType,int>::const_iterator it = m_packetCounter.find(type);
+  if(it != m_packetCounter.end())
+  {
+    return it->second;
+  }
+  else
+  {
+    return 0;
+  }
+}
+
+void
+Statistics::increment(PacketType type)
+{
+  m_packetCounter[type]++;
+}
+
+void
+Statistics::resetAll()
+{
+  for (auto&& it : m_packetCounter )
+  {
+    it.second = 0;
+  }
+}
+
+std::ostream&
+operator<<(std::ostream& os, const Statistics& stats)
+{
+  using PacketType = Statistics::PacketType;
+
+  os << "++++++++++++++++++++++++++++++++++++++++\n"
+     << "+                                      +\n"
+     << "+              Statistics              +\n"
+     << "+                                      +\n"
+     << "++++++++++++++++++++++++++++++++++++++++\n"
+     << "HELLO PROTOCOL\n"
+     << "    Sent Hello Interests: "              << stats.get(PacketType::SENT_HELLO_INTEREST) << "\n"
+     << "    Sent Hello Data: "                   << stats.get(PacketType::SENT_HELLO_DATA) << "\n"
+     << "\n"
+     << "    Received Hello Interests: "          << stats.get(PacketType::RCV_HELLO_INTEREST) << "\n"
+     << "    Received Hello Data: "               << stats.get(PacketType::RCV_HELLO_DATA) << "\n"
+     << "\n"
+     << "LSDB\n"
+     << "    Total Sent LSA Interests: "          << stats.get(PacketType::SENT_LSA_INTEREST) << "\n"
+     << "    Total Received LSA Interests: "      << stats.get(PacketType::RCV_LSA_INTEREST) << "\n"
+     << "\n"
+     << "    Total Sent LSA Data: "               << stats.get(PacketType::SENT_LSA_DATA) << "\n"
+     << "    Total Received LSA Data: "           << stats.get(PacketType::RCV_LSA_DATA) << "\n"
+     << "\n"
+     << "    Sent Adjacency LSA Interests: "      << stats.get(PacketType::SENT_ADJ_LSA_INTEREST) << "\n"
+     << "    Sent Coordinate LSA Interests: "     << stats.get(PacketType::SENT_COORD_LSA_INTEREST) << "\n"
+     << "    Sent Name LSA Interests: "           << stats.get(PacketType::SENT_NAME_LSA_INTEREST) << "\n"
+     << "\n"
+     << "    Received Adjacency LSA Interests: "  << stats.get(PacketType::RCV_ADJ_LSA_INTEREST) << "\n"
+     << "    Received Coordinate LSA Interests: " << stats.get(PacketType::RCV_COORD_LSA_INTEREST) << "\n"
+     << "    Received Name LSA Interests: "       << stats.get(PacketType::RCV_NAME_LSA_INTEREST) << "\n"
+     << "\n"
+     << "    Sent Adjacency LSA Data: "           << stats.get(PacketType::SENT_ADJ_LSA_DATA) << "\n"
+     << "    Sent Coordinate LSA Data: "          << stats.get(PacketType::SENT_COORD_LSA_DATA) << "\n"
+     << "    Sent Name LSA Data: "                << stats.get(PacketType::SENT_NAME_LSA_DATA) << "\n"
+     << "\n"
+     << "    Received Adjacency LSA Data: "       << stats.get(PacketType::RCV_ADJ_LSA_DATA) << "\n"
+     << "    Received Coordinate LSA Data: "      << stats.get(PacketType::RCV_COORD_LSA_DATA) << "\n"
+     << "    Received Name LSA Data: "            << stats.get(PacketType::RCV_NAME_LSA_DATA) << "\n"
+     << "++++++++++++++++++++++++++++++++++++++++\n";
+
+  return os;
+}
+
+} // namespace nlsr
diff --git a/src/statistics.hpp b/src/statistics.hpp
new file mode 100644
index 0000000..435938f
--- /dev/null
+++ b/src/statistics.hpp
@@ -0,0 +1,80 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014-2017,  The University of Memphis,
+ *                           Regents of the University of California,
+ *                           Arizona Board of Regents.
+ *
+ * This file is part of NLSR (Named-data Link State Routing).
+ * See AUTHORS.md for complete list of NLSR authors and contributors.
+ *
+ * NLSR is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ *
+ * NLSR 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 GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * NLSR, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+#ifndef NLSR_STATISTICS_HPP
+#define NLSR_STATISTICS_HPP
+
+#include <map>
+
+namespace nlsr {
+
+class Statistics
+{
+
+public:
+  enum class PacketType {
+    SENT_HELLO_INTEREST,
+    SENT_HELLO_DATA,
+    RCV_HELLO_INTEREST,
+    RCV_HELLO_DATA,
+    SENT_LSA_INTEREST,
+    SENT_ADJ_LSA_INTEREST,
+    SENT_COORD_LSA_INTEREST,
+    SENT_NAME_LSA_INTEREST,
+    SENT_LSA_DATA,
+    SENT_ADJ_LSA_DATA,
+    SENT_COORD_LSA_DATA,
+    SENT_NAME_LSA_DATA,
+    RCV_LSA_INTEREST,
+    RCV_ADJ_LSA_INTEREST,
+    RCV_COORD_LSA_INTEREST,
+    RCV_NAME_LSA_INTEREST,
+    RCV_LSA_DATA,
+    RCV_ADJ_LSA_DATA,
+    RCV_COORD_LSA_DATA,
+    RCV_NAME_LSA_DATA
+  };
+
+  size_t
+  get(PacketType) const;
+
+  void
+  resetAll();
+
+  void
+  increment(PacketType);
+
+  const std::map<PacketType,int>&
+  getCounter() const
+  {
+    return m_packetCounter;
+  }
+
+private:
+  std::map<PacketType,int> m_packetCounter;
+};
+
+std::ostream&
+operator<<(std::ostream&, const Statistics& stats);
+
+} // namespace nlsr
+
+#endif // NLSR_STATISTICS_HPP
diff --git a/src/stats-collector.cpp b/src/stats-collector.cpp
new file mode 100644
index 0000000..cb179ec
--- /dev/null
+++ b/src/stats-collector.cpp
@@ -0,0 +1,52 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014-2017,  The University of Memphis,
+ *                           Regents of the University of California
+ *
+ * This file is part of NLSR (Named-data Link State Routing).
+ * See AUTHORS.md for complete list of NLSR authors and contributors.
+ *
+ * NLSR is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ *
+ * NLSR 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 GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * NLSR, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+#include "stats-collector.hpp"
+#include "logger.hpp"
+#include <fstream>
+
+namespace nlsr {
+
+StatsCollector::StatsCollector(Lsdb& lsdb, HelloProtocol& hp)
+  : m_lsdb(lsdb)
+  , m_hp(hp)
+{
+  m_lsaIncrementConn =
+  this->m_lsdb.lsaIncrementSignal.connect(std::bind(&StatsCollector::statsIncrement,
+                                                    this, _1));
+
+  m_helloIncrementConn =
+  this->m_hp.hpIncrementSignal.connect(std::bind(&StatsCollector::statsIncrement,
+                                                 this, _1));
+}
+
+StatsCollector::~StatsCollector()
+{
+  m_lsaIncrementConn.disconnect();
+  m_helloIncrementConn.disconnect();
+}
+
+void
+StatsCollector::statsIncrement(Statistics::PacketType pType)
+{
+  m_stats.increment(pType);
+}
+
+} // namespace nlsr
diff --git a/src/stats-collector.hpp b/src/stats-collector.hpp
new file mode 100644
index 0000000..a2be959
--- /dev/null
+++ b/src/stats-collector.hpp
@@ -0,0 +1,72 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014-2017,  The University of Memphis,
+ *                           Regents of the University of California
+ *
+ * This file is part of NLSR (Named-data Link State Routing).
+ * See AUTHORS.md for complete list of NLSR authors and contributors.
+ *
+ * NLSR is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ *
+ * NLSR 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 GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * NLSR, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+#ifndef NLSR_STATS_COLLECTOR_HPP
+#define NLSR_STATS_COLLECTOR_HPP
+
+#include "statistics.hpp"
+#include "lsdb.hpp"
+#include "hello-protocol.hpp"
+#include <ndn-cxx/util/signal.hpp>
+
+namespace nlsr {
+
+// brief: a class designed to handle statistical signals in nlsr
+
+class StatsCollector
+{
+public:
+
+  StatsCollector(Lsdb& lsdb, HelloProtocol& hp);
+
+  ~StatsCollector();
+
+  Statistics&
+  getStatistics()
+  {
+    return m_stats;
+  }
+
+private:
+
+  /*!
+   * \brief: increments a Statistics::PacketType
+   *
+   * \param: pType is an enum value corresponding to a Statistics::PacketType
+   *
+   * This takes in a Statistics::PacketType emitted by a signal and increments
+   * the value in m_stats.
+   */
+  void
+  statsIncrement(Statistics::PacketType pType);
+
+private:
+
+  Lsdb& m_lsdb;
+  HelloProtocol& m_hp;
+  Statistics m_stats;
+
+  ndn::util::signal::ScopedConnection m_lsaIncrementConn;
+  ndn::util::signal::ScopedConnection m_helloIncrementConn;
+};
+
+} // namespace nlsr
+
+#endif // NLSR_STATS_COLLECTOR_HPP
diff --git a/tests/test-statistics.cpp b/tests/test-statistics.cpp
new file mode 100644
index 0000000..bcfc29c
--- /dev/null
+++ b/tests/test-statistics.cpp
@@ -0,0 +1,330 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014-2017,  The University of Memphis,
+ *                           Regents of the University of California,
+ *                           Arizona Board of Regents.
+ *
+ * This file is part of NLSR (Named-data Link State Routing).
+ * See AUTHORS.md for complete list of NLSR authors and contributors.
+ *
+ * NLSR is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ *
+ * NLSR 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 GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * NLSR, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+#include "statistics.hpp"
+#include "test-common.hpp"
+#include "hello-protocol.hpp"
+#include "lsdb.hpp"
+#include "nlsr.hpp"
+
+#include <ndn-cxx/util/dummy-client-face.hpp>
+
+namespace nlsr {
+namespace test {
+
+class StatisticsFixture : public BaseFixture
+{
+public:
+  StatisticsFixture()
+    : face(std::make_shared<ndn::util::DummyClientFace>(g_ioService))
+    , nlsr(g_ioService, g_scheduler, std::ref(*face), g_keyChain)
+    , lsdb(nlsr.getLsdb())
+    , hello(nlsr.m_helloProtocol)
+    , conf(nlsr.getConfParameter())
+    , collector(nlsr.getStatsCollector())
+  {
+    conf.setNetwork("/ndn");
+    conf.setSiteName("/site");
+    conf.setRouterName("/%C1.router/this-router");
+    conf.buildRouterPrefix();
+
+    nlsr.initialize();
+
+    face->processEvents(ndn::time::milliseconds(1));
+    face->sentInterests.clear();
+  }
+
+  /*!
+   * \brief Checks if lsa interest was received and data for interest was sent
+   *
+   * \param interestPrefix is an interest name prefix
+   * \param lsaType indicates whether the lsa is a name, adjacency, or coordinate
+   * \param seqNo sequence number that will be appended to an interest name
+   * \param receivedInterestType is the specific Statisitcs::PacketType interest that is received
+   * \param sentDataType is the Statistics::PacketType data being sent upon interest process
+   *
+   * This is a general function that can be used for all three types of lsa. Calling processInterest()
+   * from lsdb will cause the statsCollector to increment the incoming interest type and increment the
+   * outgoing data type.
+   */
+  void
+  receiveInterestAndCheckSentStats(const std::string& interestPrefix,
+                                   const std::string& lsaType,
+                                   uint32_t seqNo,
+                                   Statistics::PacketType receivedInterestType,
+                                   Statistics::PacketType sentDataType)
+  {
+    size_t rcvBefore = collector.getStatistics().get(receivedInterestType);
+    size_t sentBefore = collector.getStatistics().get(sentDataType);
+
+    ndn::Name interestName = ndn::Name(ndn::Name(interestPrefix + lsaType).appendNumber(seqNo));
+    lsdb.processInterest(ndn::Name(), ndn::Interest(interestName));
+    face->processEvents(ndn::time::milliseconds(1));
+
+    BOOST_CHECK_EQUAL(collector.getStatistics().get(receivedInterestType), rcvBefore + 1);
+    BOOST_CHECK_EQUAL(collector.getStatistics().get(sentDataType), sentBefore + 1);
+  }
+
+  /*!
+   * \brief Checks if statistics update after an lsa interest is sent
+   *
+   * \param prefix is an interest prefix
+   * \param lsaType indicates whether the lsa is a name, adjacency, or coordinate
+   * \param seqNo is the sequence number
+   * \param statsType is a statistical PacketType
+   *
+   * The function is called to initiate an expressInterest call in lsdb and to check if the
+   * expected statistical packetType was incremented.
+   */
+  void
+  sendInterestAndCheckStats(const std::string& prefix,
+                            const std::string& lsaType,
+                            uint32_t seqNo,
+                            Statistics::PacketType statsType)
+  {
+    size_t sentBefore = collector.getStatistics().get(statsType);
+
+    lsdb.expressInterest(ndn::Name(prefix + lsaType).appendNumber(seqNo), 0,
+                         ndn::time::steady_clock::TimePoint::min());
+    face->processEvents(ndn::time::milliseconds(1));
+
+    BOOST_CHECK_EQUAL(collector.getStatistics().get(statsType), sentBefore + 1);
+  }
+
+public:
+  std::shared_ptr<ndn::util::DummyClientFace> face;
+  Nlsr nlsr;
+
+  Lsdb& lsdb;
+  HelloProtocol& hello;
+  ConfParameter& conf;
+  StatsCollector& collector;
+};
+
+BOOST_FIXTURE_TEST_SUITE(TestStatistics, StatisticsFixture)
+
+
+// A statistical PacketType is directly incremented (without signals).
+BOOST_AUTO_TEST_CASE(StatsIncrement)
+{
+  Statistics stats;
+  BOOST_CHECK_EQUAL(stats.get(Statistics::PacketType::SENT_HELLO_INTEREST), 0);
+  stats.increment(Statistics::PacketType::SENT_HELLO_INTEREST);
+  BOOST_CHECK_EQUAL(stats.get(Statistics::PacketType::SENT_HELLO_INTEREST), 1);
+}
+
+/*
+ * After a PacketType has been incremented, the resetAll() function is called, which sets all
+ * statistical packetType counts to 0
+ */
+BOOST_AUTO_TEST_CASE(StatsReset)
+{
+  Statistics stats;
+  stats.increment(Statistics::PacketType::SENT_HELLO_INTEREST);
+  stats.resetAll();
+  BOOST_CHECK_EQUAL(stats.get(Statistics::PacketType::SENT_HELLO_INTEREST), 0);
+}
+
+
+/*
+ * This tests hello interests and hello data statistical collection by constructing an adjacency lsa
+ * and calling functions that trigger the sending and receiving hello of interests/data.
+ */
+BOOST_AUTO_TEST_CASE(SendHelloInterest)
+{
+  nlsr.initialize();
+
+  face->processEvents(ndn::time::milliseconds(1));
+  face->sentInterests.clear();
+
+  Adjacent other("/ndn/router/other", ndn::util::FaceUri("udp4://other"), 25, Adjacent::STATUS_INACTIVE, 0, 0);
+
+  // This router's Adjacency LSA
+  nlsr.getAdjacencyList().insert(other);
+
+  ndn::Name name(conf.getRouterPrefix());
+  name.append("NLSR");
+  name.append("INFO");
+  name.append(other.getName().wireEncode());
+
+  hello.expressInterest(name, 1);
+  face->processEvents(ndn::time::milliseconds(1));
+
+  BOOST_CHECK_EQUAL(collector.getStatistics().get(Statistics::PacketType::SENT_HELLO_INTEREST), 1);
+
+  ndn::Interest interest(name);
+  hello.processInterest(ndn::Name(), interest);
+
+  face->processEvents(ndn::time::milliseconds(1));
+
+  BOOST_CHECK_EQUAL(collector.getStatistics().get(Statistics::PacketType::RCV_HELLO_INTEREST), 1);
+  BOOST_CHECK_EQUAL(collector.getStatistics().get(Statistics::PacketType::SENT_HELLO_DATA), 1);
+
+  // Receive Hello Data
+  ndn::Name dataName = other.getName();
+  dataName.append("NLSR");
+  dataName.append("INFO");
+  dataName.append(conf.getRouterPrefix().wireEncode());
+
+  std::shared_ptr<ndn::Data> data = std::make_shared<ndn::Data>(dataName);
+  hello.onContentValidated(data);
+
+  BOOST_CHECK_EQUAL(collector.getStatistics().get(Statistics::PacketType::RCV_HELLO_DATA), 1);
+}
+
+/*
+ * An interest is sent for each lsa type (name, adjacency, coordinate). The respective statistics are
+ * totaled and checked.
+ */
+BOOST_AUTO_TEST_CASE(LsdbSendLsaInterest)
+{
+  const std::string interestPrefix("/ndn/NLSR/LSA/site/%C1.Router/router/");
+  uint32_t seqNo = 1;
+
+  // Adjacency LSA
+  sendInterestAndCheckStats(interestPrefix, AdjLsa::TYPE_STRING, seqNo, Statistics::PacketType::SENT_ADJ_LSA_INTEREST);
+
+  // Coordinate LSA
+  sendInterestAndCheckStats(interestPrefix, CoordinateLsa::TYPE_STRING, seqNo, Statistics::PacketType::SENT_COORD_LSA_INTEREST);
+
+  // Name LSA
+  sendInterestAndCheckStats(interestPrefix, NameLsa::TYPE_STRING, seqNo, Statistics::PacketType::SENT_NAME_LSA_INTEREST);
+
+  // 3 total lsa interests were sent
+  BOOST_CHECK_EQUAL(collector.getStatistics().get(Statistics::PacketType::SENT_LSA_INTEREST), 3);
+}
+
+/*
+ * Tests the statistics collected upon processing incoming lsa interests and respective outgoing data.
+ * This process will trigger both an increment for received lsa interest and sent lsa data.
+ *
+ * /sa receiveInterestAndCheckSentStats
+ */
+BOOST_AUTO_TEST_CASE(LsdbReceiveInterestSendData)
+{
+  std::string routerName("/ndn/site/%C1.Router/router");
+  ndn::time::system_clock::TimePoint MAX_TIME = ndn::time::system_clock::TimePoint::max();
+  uint32_t seqNo = 1;
+
+  // Adjacency LSA
+  Adjacent adjacency("adjacency");
+  adjacency.setStatus(Adjacent::STATUS_ACTIVE);
+
+  AdjacencyList adjacencies;
+  adjacencies.insert(adjacency);
+
+  AdjLsa adjLsa(routerName, seqNo, MAX_TIME, 1, adjacencies);
+  lsdb.installAdjLsa(adjLsa);
+
+  const std::string interestPrefix("/ndn/NLSR/LSA/site/%C1.Router/router/");
+
+  // Receive Adjacency LSA Interest
+  receiveInterestAndCheckSentStats(interestPrefix,
+                                   AdjLsa::TYPE_STRING,
+                                   seqNo,
+                                   Statistics::PacketType::RCV_ADJ_LSA_INTEREST,
+                                   Statistics::PacketType::SENT_ADJ_LSA_DATA);
+
+  // Name LSA
+  NamePrefixList prefixes;
+  prefixes.insert("/ndn/name");
+
+  NameLsa nameLsa(routerName, seqNo, MAX_TIME, prefixes);
+  lsdb.installNameLsa(nameLsa);
+
+  // Receive Name LSA Interest
+  receiveInterestAndCheckSentStats(interestPrefix,
+                                   NameLsa::TYPE_STRING,
+                                   seqNo,
+                                   Statistics::PacketType::RCV_NAME_LSA_INTEREST,
+                                   Statistics::PacketType::SENT_NAME_LSA_DATA);
+
+  // Coordinate LSA
+  std::vector<double> angles = {20.0, 30.0};
+  CoordinateLsa coordLsa(routerName, seqNo, MAX_TIME, 2.5, angles);
+  lsdb.installCoordinateLsa(coordLsa);
+
+  // Receive Adjacency LSA Interest
+  receiveInterestAndCheckSentStats(interestPrefix,
+                                   CoordinateLsa::TYPE_STRING,
+                                   seqNo,
+                                   Statistics::PacketType::RCV_COORD_LSA_INTEREST,
+                                   Statistics::PacketType::SENT_COORD_LSA_DATA);
+
+  // 3 different lsa type interests should be received
+  BOOST_CHECK_EQUAL(collector.getStatistics().get(Statistics::PacketType::RCV_LSA_INTEREST), 3);
+
+  // data should have been sent 3x, once per lsa type
+  BOOST_CHECK_EQUAL(collector.getStatistics().get(Statistics::PacketType::SENT_LSA_DATA), 3);
+}
+
+/*
+ * Data for each lsa type (name, adjacency, coordinate) is sent to the lsdb and statistics are
+ * checked to verify the respective statistical PacketType has been received.
+ */
+BOOST_AUTO_TEST_CASE(LsdbReceiveData)
+{
+  ndn::Name routerName("/ndn/cs/%C1.Router/router1");
+  uint32_t seqNo = 1;
+  ndn::time::system_clock::TimePoint MAX_TIME = ndn::time::system_clock::TimePoint::max();
+
+  // adjacency lsa
+  ndn::Name adjInterest("/ndn/NLSR/LSA/cs/%C1.Router/router1/adjacency/");
+  adjInterest.appendNumber(seqNo);
+  AdjLsa aLsa(routerName, seqNo, MAX_TIME, 1, nlsr.getAdjacencyList());
+  lsdb.installAdjLsa(aLsa);
+
+  const ndn::ConstBufferPtr aBuffer = std::make_shared<ndn::Buffer>(aLsa.getData().c_str(),
+                                                                    aLsa.getData().size());
+  lsdb.afterFetchLsa(aBuffer, adjInterest);
+  BOOST_CHECK_EQUAL(collector.getStatistics().get(Statistics::PacketType::RCV_ADJ_LSA_DATA), 1);
+
+  // coordinate lsa
+  ndn::Name coordInterest("/ndn/NLSR/LSA/cs/%C1.Router/router1/coordinate/");
+  coordInterest.appendNumber(seqNo);
+  std::vector<double> angles = {20.0, 30.0};
+  CoordinateLsa cLsa(routerName, seqNo, MAX_TIME, 2.5, angles);
+  lsdb.installCoordinateLsa(cLsa);
+
+  const ndn::ConstBufferPtr cBuffer = std::make_shared<ndn::Buffer>(cLsa.getData().c_str(),
+                                                                   cLsa.getData().size());
+  lsdb.afterFetchLsa(cBuffer, coordInterest);
+  BOOST_CHECK_EQUAL(collector.getStatistics().get(Statistics::PacketType::RCV_COORD_LSA_DATA), 1);
+
+  // name lsa
+  ndn::Name interestName("/ndn/NLSR/LSA/cs/%C1.Router/router1/name/");
+  interestName.appendNumber(seqNo);
+  NameLsa nLsa(routerName, seqNo, MAX_TIME, nlsr.getNamePrefixList());
+  lsdb.installNameLsa(nLsa);
+
+  const ndn::ConstBufferPtr nBuffer = std::make_shared<ndn::Buffer>(nLsa.getData().c_str(),
+                                                                   nLsa.getData().size());
+  lsdb.afterFetchLsa(nBuffer, interestName);
+  BOOST_CHECK_EQUAL(collector.getStatistics().get(Statistics::PacketType::RCV_NAME_LSA_DATA), 1);
+
+  // 3 lsa data types should be received
+  BOOST_CHECK_EQUAL(collector.getStatistics().get(Statistics::PacketType::RCV_LSA_DATA), 3);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace test
+} // namespace nlsr