tests+consumer+docs: Test case for AppDelayTracer and related bugfixes

Change-Id: I44a154f53350e140303795a9a8f824f913cc9a4c
Refs: #2764
diff --git a/apps/ndn-consumer.cpp b/apps/ndn-consumer.cpp
index a1ad032..7350b0f 100644
--- a/apps/ndn-consumer.cpp
+++ b/apps/ndn-consumer.cpp
@@ -221,9 +221,9 @@
   uint32_t seq = data->getName().at(-1).toSequenceNumber();
   NS_LOG_INFO("< DATA for " << seq);
 
-  int hopCount = -1;
+  int hopCount = 0;
   auto ns3PacketTag = data->getTag<Ns3PacketTag>();
-  if (ns3PacketTag != nullptr) {
+  if (ns3PacketTag != nullptr) { // e.g., packet came from local node's cache
     FwHopCountTag hopCountTag;
     if (ns3PacketTag->getPacket()->PeekPacketTag(hopCountTag)) {
       hopCount = hopCountTag.Get();
diff --git a/docs/source/metric.rst b/docs/source/metric.rst
index 2476d43..167b622 100644
--- a/docs/source/metric.rst
+++ b/docs/source/metric.rst
@@ -341,8 +341,11 @@
     +-----------------+---------------------------------------------------------------------+
     | ``RetxCount``   | number of Interest retransmissions (for LastDelay always equal to 1)|
     +-----------------+---------------------------------------------------------------------+
-    | ``HopCount``    | the number of hops that the retrieved Data packet traveled on the   |
-    |                 | way back from producer application or cache.                        |
+    | ``HopCount``    | the number of network hops that the retrieved Data packet traveled  |
+    |                 | on the way back from producer application or cache.                 |
+    |                 |                                                                     |
+    |                 | Note that semantics of ``HopCount`` field has changed compared to   |
+    |                 | ndnSIM 1.0.                                                         |
     +-----------------+---------------------------------------------------------------------+
 
 .. _app delay trace helper example:
diff --git a/tests/unit-tests/utils/tracers/ndn-app-delay-tracer.t.cpp b/tests/unit-tests/utils/tracers/ndn-app-delay-tracer.t.cpp
new file mode 100644
index 0000000..64e4aa9
--- /dev/null
+++ b/tests/unit-tests/utils/tracers/ndn-app-delay-tracer.t.cpp
@@ -0,0 +1,163 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2011-2015  Regents of the University of California.
+ *
+ * This file is part of ndnSIM. See AUTHORS for complete list of ndnSIM authors and
+ * contributors.
+ *
+ * ndnSIM 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.
+ *
+ * ndnSIM 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
+ * ndnSIM, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+#include "utils/tracers/ndn-app-delay-tracer.hpp"
+
+#include <boost/filesystem.hpp>
+#include <boost/test/output_test_stream.hpp>
+
+#include "../../tests-common.hpp"
+
+namespace ns3 {
+namespace ndn {
+
+const boost::filesystem::path TEST_TRACE = boost::filesystem::path(TEST_CONFIG_PATH) / "trace.txt";
+
+class AppDelayTracerFixture : public ScenarioHelperWithCleanupFixture
+{
+public:
+  AppDelayTracerFixture()
+  {
+    boost::filesystem::create_directories(TEST_CONFIG_PATH);
+
+    // setting default parameters for PointToPoint links and channels
+    Config::SetDefault("ns3::PointToPointNetDevice::DataRate", StringValue("10Mbps"));
+    Config::SetDefault("ns3::PointToPointChannel::Delay", StringValue("10ms"));
+    Config::SetDefault("ns3::DropTailQueue::MaxPackets", StringValue("20"));
+
+    createTopology({
+        {"1", "2"},
+        {"2", "3"}
+      });
+
+    addRoutes({
+        {"1", "2", "/prefix", 1},
+        {"2", "3", "/prefix", 1}
+      });
+
+    addApps({
+        {"1", "ns3::ndn::ConsumerCbr",
+            {{"Prefix", "/prefix"}, {"Frequency", "1"}},
+            "0s", "0.9s"}, // send just one packet
+        // Second consumer will capture effect of the bug #2764
+        {"2", "ns3::ndn::ConsumerCbr",
+            {{"Prefix", "/prefix"}, {"Frequency", "1"}},
+            "2s", "100s"},
+        {"3", "ns3::ndn::Producer",
+            {{"Prefix", "/prefix"}, {"PayloadSize", "1024"}},
+            "0s", "100s"}
+      });
+  }
+
+  ~AppDelayTracerFixture()
+  {
+    boost::filesystem::remove(TEST_TRACE);
+    AppDelayTracer::Destroy(); // additional cleanup
+  }
+};
+
+BOOST_FIXTURE_TEST_SUITE(UtilsTracersNdnAppDelayTracer, AppDelayTracerFixture)
+
+BOOST_AUTO_TEST_CASE(InstallAll)
+{
+  AppDelayTracer::InstallAll(TEST_TRACE.string());
+
+  Simulator::Stop(Seconds(4));
+  Simulator::Run();
+
+  AppDelayTracer::Destroy(); // to force log to be written
+
+  std::ifstream t(TEST_TRACE.string().c_str());
+  std::stringstream buffer;
+  buffer << t.rdbuf();
+
+  BOOST_CHECK_EQUAL(buffer.str(),
+    "Time	Node	AppId	SeqNo	Type	DelayS	DelayUS	RetxCount	HopCount\n"
+    "0.0417424	1	257	0	LastDelay	0.0417424	41742.4	1	2\n"
+    "0.0417424	1	257	0	FullDelay	0.0417424	41742.4	1	2\n"
+    "2	2	258	0	LastDelay	0	0	1	0\n"
+    "2	2	258	0	FullDelay	0	0	1	0\n"
+    "3.02087	2	258	1	LastDelay	0.0208712	20871.2	1	1\n"
+    "3.02087	2	258	1	FullDelay	0.0208712	20871.2	1	1\n");
+}
+
+BOOST_AUTO_TEST_CASE(InstallNodeContainer)
+{
+  NodeContainer nodes;
+  nodes.Add(getNode("1"));
+
+  AppDelayTracer::Install(nodes, TEST_TRACE.string());
+
+  Simulator::Stop(Seconds(4));
+  Simulator::Run();
+
+  AppDelayTracer::Destroy(); // to force log to be written
+
+  std::ifstream t(TEST_TRACE.string().c_str());
+  std::stringstream buffer;
+  buffer << t.rdbuf();
+
+  BOOST_CHECK_EQUAL(buffer.str(),
+    "Time	Node	AppId	SeqNo	Type	DelayS	DelayUS	RetxCount	HopCount\n"
+    "0.0417424	1	257	0	LastDelay	0.0417424	41742.4	1	2\n"
+    "0.0417424	1	257	0	FullDelay	0.0417424	41742.4	1	2\n");
+}
+
+BOOST_AUTO_TEST_CASE(InstallNode)
+{
+  AppDelayTracer::Install(getNode("2"), TEST_TRACE.string());
+
+  Simulator::Stop(Seconds(4));
+  Simulator::Run();
+
+  AppDelayTracer::Destroy(); // to force log to be written
+
+  std::ifstream t(TEST_TRACE.string().c_str());
+  std::stringstream buffer;
+  buffer << t.rdbuf();
+
+  BOOST_CHECK_EQUAL(buffer.str(),
+    "Time	Node	AppId	SeqNo	Type	DelayS	DelayUS	RetxCount	HopCount\n"
+    "2	2	258	0	LastDelay	0	0	1	0\n"
+    "2	2	258	0	FullDelay	0	0	1	0\n"
+    "3.02087	2	258	1	LastDelay	0.0208712	20871.2	1	1\n"
+    "3.02087	2	258	1	FullDelay	0.0208712	20871.2	1	1\n");
+}
+
+BOOST_AUTO_TEST_CASE(InstallNodeDumpStream)
+{
+  auto output = make_shared<boost::test_tools::output_test_stream>();
+  Ptr<AppDelayTracer> tracer = AppDelayTracer::Install(getNode("2"), output);
+
+  Simulator::Stop(Seconds(4));
+  Simulator::Run();
+
+  tracer = nullptr; // destroy tracer
+
+  BOOST_CHECK(output->is_equal(
+    "2	2	258	0	LastDelay	0	0	1	0\n"
+    "2	2	258	0	FullDelay	0	0	1	0\n"
+    "3.02087	2	258	1	LastDelay	0.0208712	20871.2	1	1\n"
+    "3.02087	2	258	1	FullDelay	0.0208712	20871.2	1	1\n"));
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace ndn
+} // namespace ns3
diff --git a/utils/tracers/ndn-app-delay-tracer.hpp b/utils/tracers/ndn-app-delay-tracer.hpp
index d58f517..6e02f93 100644
--- a/utils/tracers/ndn-app-delay-tracer.hpp
+++ b/utils/tracers/ndn-app-delay-tracer.hpp
@@ -51,10 +51,6 @@
    *
    * @param file File to which traces will be written.  If filename is -, then std::out is used
    *
-   * @returns a tuple of reference to output stream and list of tracers. !!! Attention !!! This
-   *tuple needs to be preserved
-   *          for the lifetime of simulation, otherwise SEGFAULTs are inevitable
-   *
    */
   static void
   InstallAll(const std::string& file);
@@ -65,10 +61,6 @@
    * @param nodes Nodes on which to install tracer
    * @param file File to which traces will be written.  If filename is -, then std::out is used
    *
-   * @returns a tuple of reference to output stream and list of tracers. !!! Attention !!! This
-   *tuple needs to be preserved
-   *          for the lifetime of simulation, otherwise SEGFAULTs are inevitable
-   *
    */
   static void
   Install(const NodeContainer& nodes, const std::string& file);
@@ -79,12 +71,7 @@
    * @param nodes Nodes on which to install tracer
    * @param file File to which traces will be written.  If filename is -, then std::out is used
    * @param averagingPeriod How often data will be written into the trace file (default, every half
-   *second)
-   *
-   * @returns a tuple of reference to output stream and list of tracers. !!! Attention !!! This
-   *tuple needs to be preserved
-   *          for the lifetime of simulation, otherwise SEGFAULTs are inevitable
-   *
+   *        second)
    */
   static void
   Install(Ptr<Node> node, const std::string& file);
@@ -95,11 +82,11 @@
    * @param nodes Nodes on which to install tracer
    * @param outputStream Smart pointer to a stream
    * @param averagingPeriod How often data will be written into the trace file (default, every half
-   *second)
+   *        second)
    *
-   * @returns a tuple of reference to output stream and list of tracers. !!! Attention !!! This
-   *tuple needs to be preserved
-   *          for the lifetime of simulation, otherwise SEGFAULTs are inevitable
+   * @returns a tuple of reference to output stream and list of tracers.
+   *          !!! Attention !!! This tuple needs to be preserved for the lifetime of simulation,
+   *          otherwise SEGFAULTs are inevitable
    */
   static Ptr<AppDelayTracer>
   Install(Ptr<Node> node, shared_ptr<std::ostream> outputStream);