ndnping: recognize and trace Nack

Change-Id: If47877892c75ae0849375f36430a66e02fb7a608
refs: #3335
diff --git a/tests/ping/client/ping.t.cpp b/tests/ping/client/ping.t.cpp
index 9f9eeb9..e39c65a 100644
--- a/tests/ping/client/ping.t.cpp
+++ b/tests/ping/client/ping.t.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014-2015,  Arizona Board of Regents.
+ * Copyright (c) 2014-2016,  Arizona Board of Regents.
  *
  * This file is part of ndn-tools (Named Data Networking Essential Tools).
  * See AUTHORS.md for complete list of ndn-tools authors and contributors.
@@ -31,86 +31,64 @@
 
 BOOST_AUTO_TEST_SUITE(PingClientPing)
 
-class SequenceNumberIncrementFixture : public UnitTestTimeFixture
+BOOST_FIXTURE_TEST_CASE(Basic, UnitTestTimeFixture)
 {
-protected:
-  SequenceNumberIncrementFixture()
-    : face(util::makeDummyClientFace(io, {false, true}))
-    , pingOptions(makeOptions())
-    , ping(*face, pingOptions)
-    , numPings(0)
-    , lastPingSeq(pingOptions.startSeq)
-  {
-    ping.afterResponse.connect(bind(&SequenceNumberIncrementFixture::onResponse, this, _1));
-    ping.afterTimeout.connect(bind(&SequenceNumberIncrementFixture::onTimeout, this, _1));
-  }
-
-public:
-  void
-  onResponse(uint64_t seq)
-  {
-    numPings++;
-    lastPingSeq = seq;
-    if (numPings == maxPings) {
-      face->shutdown();
-      io.stop();
-    }
-  }
-
-  void
-  onTimeout(uint64_t seq)
-  {
-    numPings++;
-    lastPingSeq = seq;
-    if (numPings == maxPings) {
-      face->shutdown();
-      io.stop();
-    }
-  }
-
-private:
-  static Options
-  makeOptions()
-  {
-    Options opt;
-    opt.prefix = "ndn:/test-prefix";
-    opt.shouldAllowStaleData = false;
-    opt.shouldGenerateRandomSeq = false;
-    opt.shouldPrintTimestamp = false;
-    opt.nPings = 4;
-    opt.interval = time::milliseconds(100);
-    opt.timeout = time::milliseconds(1000);
-    opt.startSeq = 1000;
-    return opt;
-  }
-
-protected:
-  boost::asio::io_service io;
-  shared_ptr<util::DummyClientFace> face;
   Options pingOptions;
-  Ping ping;
-  uint32_t numPings;
-  uint32_t maxPings;
-  uint64_t lastPingSeq;
-  KeyChain keyChain;
-};
+  pingOptions.prefix = "ndn:/test-prefix";
+  pingOptions.shouldAllowStaleData = false;
+  pingOptions.shouldGenerateRandomSeq = false;
+  pingOptions.shouldPrintTimestamp = false;
+  pingOptions.nPings = 4;
+  pingOptions.interval = time::milliseconds(100);
+  pingOptions.timeout = time::milliseconds(2000);
+  pingOptions.startSeq = 1000;
 
-BOOST_FIXTURE_TEST_CASE(SequenceNumberIncrement, SequenceNumberIncrementFixture)
-{
-  maxPings = 4;
+  boost::asio::io_service io;
+  util::DummyClientFace face(io, {true, true});
+  Ping ping(face, pingOptions);
+
+  int nFinishSignals = 0;
+  std::vector<uint64_t> dataSeqs;
+  std::vector<uint64_t> nackSeqs;
+  std::vector<uint64_t> timeoutSeqs;
+
+  ping.afterData.connect(bind([&] (uint64_t seq) { dataSeqs.push_back(seq); }, _1));
+  ping.afterNack.connect(bind([&] (uint64_t seq) { nackSeqs.push_back(seq); }, _1));
+  ping.afterTimeout.connect(bind([&] (uint64_t seq) { timeoutSeqs.push_back(seq); }, _1));
+  ping.afterFinish.connect(bind([&] {
+    BOOST_REQUIRE_EQUAL(dataSeqs.size(), 2);
+    BOOST_REQUIRE_EQUAL(nackSeqs.size(), 1);
+    BOOST_REQUIRE_EQUAL(timeoutSeqs.size(), 1);
+
+    BOOST_CHECK_EQUAL(dataSeqs[0], 1000);
+    BOOST_CHECK_EQUAL(nackSeqs[0], 1001);
+    BOOST_CHECK_EQUAL(dataSeqs[1], 1002);
+    BOOST_CHECK_EQUAL(timeoutSeqs[0], 1003);
+
+    nFinishSignals++;
+  }));
+
   ping.start();
 
-  this->advanceClocks(io, time::milliseconds(1), 400);
+  this->advanceClocks(io, time::milliseconds(1), 500);
+  BOOST_REQUIRE_EQUAL(face.sentInterests.size(), 4);
 
-  face->receive(*makeData("ndn:/test-prefix/ping/1000"));
-  face->receive(*makeData("ndn:/test-prefix/ping/1001"));
-  face->receive(*makeData("ndn:/test-prefix/ping/1002"));
-  face->receive(*makeData("ndn:/test-prefix/ping/1003"));
+  face.receive(*makeData("ndn:/test-prefix/ping/1000"));
 
-  io.run();
+  lp::Nack nack(face.sentInterests[1]);
+  nack.setReason(lp::NackReason::DUPLICATE);
+  face.receive(nack);
 
-  BOOST_REQUIRE_EQUAL(1003, lastPingSeq);
-  BOOST_REQUIRE_EQUAL(4, numPings);
+  face.receive(*makeData("ndn:/test-prefix/ping/1002"));
+
+  this->advanceClocks(io, time::milliseconds(100), 20);
+
+  // ndn:/test-prefix/ping/1003 is unanswered and will timeout
+
+  BOOST_CHECK_EQUAL(nFinishSignals, 1);
+
+  face.shutdown();
+  io.stop();
 }
 
 BOOST_AUTO_TEST_SUITE_END()
diff --git a/tests/ping/client/statistics-collector.t.cpp b/tests/ping/client/statistics-collector.t.cpp
index 5dcf941..a1cc177 100644
--- a/tests/ping/client/statistics-collector.t.cpp
+++ b/tests/ping/client/statistics-collector.t.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014-2015,  Arizona Board of Regents.
+ * Copyright (c) 2014-2016,  Arizona Board of Regents.
  *
  * This file is part of ndn-tools (Named Data Networking Essential Tools).
  * See AUTHORS.md for complete list of ndn-tools authors and contributors.
@@ -67,7 +67,7 @@
 
 BOOST_AUTO_TEST_CASE(Resp50msResp50ms)
 {
-  sc.recordResponse(time::milliseconds(50));
+  sc.recordData(time::milliseconds(50));
 
   Statistics stats1 = sc.computeStatistics();
   BOOST_CHECK_EQUAL(stats1.prefix, pingOptions.prefix);
@@ -80,7 +80,7 @@
   BOOST_CHECK_CLOSE(stats1.avgRtt, 50.0, 0.001);
   BOOST_CHECK_CLOSE(stats1.stdDevRtt, 0.0, 0.001);
 
-  sc.recordResponse(time::milliseconds(50));
+  sc.recordData(time::milliseconds(50));
 
   Statistics stats2 = sc.computeStatistics();
   BOOST_CHECK_EQUAL(stats2.prefix, pingOptions.prefix);
@@ -96,8 +96,8 @@
 
 BOOST_AUTO_TEST_CASE(Resp50msResp100ms)
 {
-  sc.recordResponse(time::milliseconds(50));
-  sc.recordResponse(time::milliseconds(100));
+  sc.recordData(time::milliseconds(50));
+  sc.recordData(time::milliseconds(100));
 
   Statistics stats = sc.computeStatistics();
   BOOST_CHECK_EQUAL(stats.prefix, pingOptions.prefix);
@@ -125,7 +125,7 @@
 
 BOOST_AUTO_TEST_CASE(Resp50msLoss)
 {
-  sc.recordResponse(time::milliseconds(50));
+  sc.recordData(time::milliseconds(50));
   sc.recordTimeout();
 
   Statistics stats = sc.computeStatistics();
@@ -140,6 +140,51 @@
   BOOST_CHECK_CLOSE(stats.stdDevRtt, 0.0, 0.001);
 }
 
+BOOST_AUTO_TEST_CASE(NackNack)
+{
+  sc.recordNack();
+  sc.recordNack();
+
+  Statistics stats = sc.computeStatistics();
+  BOOST_CHECK_EQUAL(stats.prefix, pingOptions.prefix);
+  BOOST_CHECK_EQUAL(stats.nSent, 2);
+  BOOST_CHECK_EQUAL(stats.nNacked, 2);
+  BOOST_CHECK_EQUAL(stats.nReceived, 0);
+  BOOST_CHECK_CLOSE(stats.packetNackedRate, 1.0, 0.001);
+}
+
+BOOST_AUTO_TEST_CASE(Resp50msNack)
+{
+  sc.recordData(time::milliseconds(50));
+  sc.recordNack();
+
+  Statistics stats = sc.computeStatistics();
+  BOOST_CHECK_EQUAL(stats.prefix, pingOptions.prefix);
+  BOOST_CHECK_EQUAL(stats.nSent, 2);
+  BOOST_CHECK_EQUAL(stats.nReceived, 1);
+  BOOST_CHECK_CLOSE(stats.minRtt, 50.0, 0.001);
+  BOOST_CHECK_CLOSE(stats.maxRtt, 50.0, 0.001);
+  BOOST_CHECK_CLOSE(stats.sumRtt, 50.0, 0.001);
+  BOOST_CHECK_CLOSE(stats.avgRtt, 50.0, 0.001);
+  BOOST_CHECK_CLOSE(stats.stdDevRtt, 0.0, 0.001);
+  BOOST_CHECK_EQUAL(stats.nNacked, 1);
+  BOOST_CHECK_CLOSE(stats.packetNackedRate, 0.5, 0.001);
+}
+
+BOOST_AUTO_TEST_CASE(NackLoss)
+{
+  sc.recordNack();
+  sc.recordTimeout();
+
+  Statistics stats = sc.computeStatistics();
+  BOOST_CHECK_EQUAL(stats.prefix, pingOptions.prefix);
+  BOOST_CHECK_EQUAL(stats.nSent, 2);
+  BOOST_CHECK_EQUAL(stats.nReceived, 0);
+  BOOST_CHECK_EQUAL(stats.nNacked, 1);
+  BOOST_CHECK_CLOSE(stats.packetNackedRate, 0.5, 0.001);
+  BOOST_CHECK_CLOSE(stats.packetLossRate, 0.5, 0.001);
+}
+
 BOOST_AUTO_TEST_SUITE_END()
 
 } // namespace tests
diff --git a/tests/ping/integrated.t.cpp b/tests/ping/integrated.t.cpp
index b8e2164..aec1896 100644
--- a/tests/ping/integrated.t.cpp
+++ b/tests/ping/integrated.t.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014-2015,  Arizona Board of Regents.
+ * Copyright (c) 2014-2016,  Arizona Board of Regents.
  *
  * This file is part of ndn-tools (Named Data Networking Essential Tools).
  * See AUTHORS.md for complete list of ndn-tools authors and contributors.
@@ -36,7 +36,6 @@
   PingIntegratedFixture()
     : serverFace(util::makeDummyClientFace(io, {false, true}))
     , clientFace(util::makeDummyClientFace(io, {false, true}))
-    , numResponses(0)
     , wantLoss(false)
   {
     serverFace->onSendInterest.connect([this] (const Interest& interest) {
@@ -53,24 +52,11 @@
     });
   }
 
-  void onTimeout(uint64_t seq)
+  void onFinish()
   {
-    numResponses++;
-    if (numResponses == maxResponses) {
-      serverFace->shutdown();
-      clientFace->shutdown();
-      io.stop();
-    }
-  }
-
-  void onData(uint64_t seq)
-  {
-    numResponses++;
-    if (numResponses == maxResponses) {
-      serverFace->shutdown();
-      clientFace->shutdown();
-      io.stop();
-    }
+    serverFace->shutdown();
+    clientFace->shutdown();
+    io.stop();
   }
 
 public:
@@ -79,8 +65,6 @@
   shared_ptr<util::DummyClientFace> clientFace;
   std::unique_ptr<server::PingServer> server;
   std::unique_ptr<client::Ping> client;
-  int maxResponses;
-  int numResponses;
   bool wantLoss;
 };
 
@@ -108,9 +92,7 @@
   clientOpts.timeout = time::milliseconds(2000);
   clientOpts.startSeq = 1000;
   client.reset(new client::Ping(*clientFace, clientOpts));
-  client->afterResponse.connect(bind(&PingIntegratedFixture::onData, this, _1));
-  client->afterTimeout.connect(bind(&PingIntegratedFixture::onTimeout, this, _1));
-  maxResponses = 4;
+  client->afterFinish.connect(bind(&PingIntegratedFixture::onFinish, this));
   client->start();
 
   this->advanceClocks(io, time::milliseconds(1), 400);
@@ -143,10 +125,7 @@
   clientOpts.timeout = time::milliseconds(500);
   clientOpts.startSeq = 1000;
   client.reset(new client::Ping(*clientFace, clientOpts));
-  numResponses = 0;
-  maxResponses = 4;
-  client->afterResponse.connect(bind(&PingIntegratedFixture::onData, this, _1));
-  client->afterTimeout.connect(bind(&PingIntegratedFixture::onTimeout, this, _1));
+  client->afterFinish.connect(bind(&PingIntegratedFixture::onFinish, this));
   client->start();
 
   this->advanceClocks(io, time::milliseconds(1), 1000);