Add test nack scenario for best-route, and a framework to support tests tools

refs #3157

Change-Id: I12d0241bd45e1af6796765a03751e445a6a23525
diff --git a/install_helpers/install_tools.py b/install_helpers/install_tools.py
new file mode 100644
index 0000000..5af3396
--- /dev/null
+++ b/install_helpers/install_tools.py
@@ -0,0 +1,14 @@
+#!/usr/bin/python2
+import os
+
+# Install tools
+def run():
+    print "\nINSTALLING tools"
+    print "***********************"
+    helper_path = os.path.dirname(os.path.realpath(__file__))
+    cwd_path = os.getcwd()
+    os.chdir(helper_path)
+    os.chdir("tools")
+    os.system("make")
+    os.system("sudo make install")
+    os.chdir(cwd_path)
diff --git a/install_helpers/tools/Makefile b/install_helpers/tools/Makefile
new file mode 100644
index 0000000..fa5c8cb
--- /dev/null
+++ b/install_helpers/tools/Makefile
@@ -0,0 +1,20 @@
+CXX = g++
+CXXFLAGS = -std=c++11 -Wall -Werror `pkg-config --cflags libndn-cxx`
+LIBS = `pkg-config --libs libndn-cxx`
+DESTDIR ?= /usr/local
+
+PROGRAMS = test-nack-consumer
+
+all: $(PROGRAMS)
+
+%: %.cpp
+	$(CXX) $(CXXFLAGS) -o $@ $< $(LIBS)
+
+clean:
+	rm -f $(PROGRAMS)
+
+install: all
+	cp $(PROGRAMS) $(DESTDIR)/bin/
+
+uninstall:
+	cd $(DESTDIR)/bin && rm -f $(PROGRAMS)
diff --git a/install_helpers/tools/README b/install_helpers/tools/README
new file mode 100644
index 0000000..5db0657
--- /dev/null
+++ b/install_helpers/tools/README
@@ -0,0 +1,21 @@
+# ndn integration-tests
+
+This directory contains tools required by one or more integration tests.
+Tools are built automatically during the installation phase.
+
+## Instructions for adding a new tool:
+------------------------------------
+1. Keep each program to a single cpp file when feasible.
+2. In each program source code, insert a comment to indicate which test case(s) uses this program
+3. Add the tool name to the Makefile programs list.
+
+## Build Instructions
+
+To compile and install:
+
+    make
+    sudo make install
+
+To uninstall:
+
+    sudo make uninstall
\ No newline at end of file
diff --git a/install_helpers/tools/test-nack-consumer.cpp b/install_helpers/tools/test-nack-consumer.cpp
new file mode 100644
index 0000000..fb6cb96
--- /dev/null
+++ b/install_helpers/tools/test-nack-consumer.cpp
@@ -0,0 +1,225 @@
+/**
+ * This program is used by test_nack interation test.
+ * The program implements a simple consumer that sends one or
+ * two interests and prints the network response (timeout, data or nack).
+ * ndnpeek is not sufficient since it does not allow verifying
+ * the nonces as required by the test.
+ */
+
+#include <boost/asio.hpp>
+#include <ndn-cxx/face.hpp>
+
+namespace ndn {
+
+class TestNackConsumer : boost::noncopyable
+{
+public:
+  explicit
+  TestNackConsumer(const char* programName)
+    : m_programName(programName)
+    , m_face(m_ioService)
+    , m_interestLifetime(time::milliseconds(4000))
+    , m_secondInterestInterval(time::milliseconds(0))
+  {
+    m_nonce1 = static_cast<uint32_t>(std::rand());
+    m_nonce2 = static_cast<uint32_t>(std::rand());
+  }
+
+  void
+  usage() const
+  {
+    std::cout << "Usage:\n"
+              << "  " << m_programName
+              << "  -p <name> -w <wait_before_quit> [-r second_Interest_interval] \n"
+              << "\n"
+              << "  -p <name> Interest prefix\n"
+              << "  -w <wait_before_quit> - wait time(ms) before exit\n"
+              << "\n"
+              << "Options:\n"
+              << "  [-r second_Interest_interval] - set the time (ms) between first and second Interests\n"
+              << "  [-h]          - print this help text and exit\n";
+    exit(EXIT_FAILURE);
+  }
+
+  void setInterestName(const Name& prefix)
+  {
+    m_interestName = prefix;
+  }
+
+  void
+  setSecondInterestInterval(int interestInterval)
+  {
+    if (interestInterval <= 0)
+      usage();
+    m_secondInterestInterval = time::milliseconds(interestInterval);
+  }
+
+  void
+  setWaitBeforeQuit(int waitTime)
+  {
+    if (waitTime <= 0)
+      usage();
+    m_waitBeforeQuit = time::milliseconds(waitTime);
+  }
+
+  void
+  signalHandler()
+  {
+    m_face.shutdown();
+    m_ioService.stop();
+
+    exit(EXIT_FAILURE);
+  }
+
+  void
+  onData(const Interest& interest, const Data& data)
+  {
+    std::cout << "DATA received for: " << interest.getName() << std::endl;
+  }
+
+  void
+  onTimeout(const Interest& interest)
+  {
+    std::cout << "TIMEOUT received for: " << interest.getName() << std::endl;
+  }
+
+  void
+  onNack(const Interest& interest,
+         const lp::Nack& nack)
+  {
+    std::cout << "NACK received for: " << interest.getName()
+              << " Nonce: " << interest.getNonce()
+              << " Reason: " << nack.getReason()
+              << std::endl;
+  }
+  void
+  sendSecondInterest(boost::asio::deadline_timer* timer)
+  {
+    Interest interest(m_interestName);
+    interest.setInterestLifetime(m_interestLifetime);
+    interest.setNonce(m_nonce2);
+
+    std::cout << "Sending Interest 2: " << interest.getName()
+              << " Nonce: " << interest.getNonce()
+              << std::endl;
+
+    m_face.expressInterest(interest,
+                           bind(&TestNackConsumer::onData,
+                                this, _1, _2),
+                           bind(&TestNackConsumer::onNack,
+                                this, _1, _2),
+                           bind(&TestNackConsumer::onTimeout,
+                                this, _1));
+  }
+
+  void
+  exitProgram(boost::asio::deadline_timer* timer)
+  {
+    m_face.shutdown();
+    m_ioService.stop();
+  }
+
+  void
+  run()
+  {
+    boost::asio::signal_set signalSet(m_ioService, SIGINT, SIGTERM);
+    signalSet.async_wait(bind(&TestNackConsumer::signalHandler, this));
+
+    boost::asio::deadline_timer sendSecondInterestTimer(m_ioService,
+                                                        boost::posix_time::millisec(m_secondInterestInterval.count()));
+    boost::asio::deadline_timer exitTimer(m_ioService,
+                                          boost::posix_time::millisec(m_waitBeforeQuit.count()));
+
+    sendSecondInterestTimer.expires_at(sendSecondInterestTimer.expires_at() +
+                      boost::posix_time::millisec(m_secondInterestInterval.count()));
+    exitTimer.expires_at(exitTimer.expires_at() +
+                      boost::posix_time::millisec(m_waitBeforeQuit.count()));
+
+    try {
+      Interest interest(m_interestName);
+      interest.setInterestLifetime(m_interestLifetime);
+      interest.setNonce(m_nonce1);
+
+      std::cout << "Sending Interest 1: " << interest.getName()
+                << " Nonce: " << interest.getNonce()
+                << std::endl;
+
+      m_face.expressInterest(interest,
+                             bind(&TestNackConsumer::onData,
+                                  this, _1, _2),
+                             bind(&TestNackConsumer::onNack,
+                                  this, _1, _2),
+                             bind(&TestNackConsumer::onTimeout,
+                                  this, _1));
+
+      if (m_secondInterestInterval != time::milliseconds(0))
+      {
+        // Schedule second interest
+        sendSecondInterestTimer.async_wait(bind(&TestNackConsumer::sendSecondInterest,
+                                                this,
+                                                &sendSecondInterestTimer));
+      }
+
+      // Schedule exit
+      exitTimer.async_wait(bind(&TestNackConsumer::exitProgram, this, &exitTimer));
+      m_face.processEvents();
+    }
+    catch (const std::exception& exception) {
+      m_ioService.stop();
+    }
+  }
+
+private:
+  boost::asio::io_service m_ioService;
+  std::string m_programName;
+  Face m_face;
+  Name m_interestName;
+  time::milliseconds m_interestLifetime;
+  time::milliseconds m_secondInterestInterval;
+  time::milliseconds m_waitBeforeQuit;
+  uint32_t  m_nonce1;
+  uint32_t  m_nonce2;
+};
+
+} // namespace ndn
+
+int
+main(int argc, char* argv[])
+{
+  std::srand(std::time(nullptr));
+  int argNumber = 0;
+  ndn::TestNackConsumer testNackConsumer(argv[0]);
+
+  int option;
+  while ((option = getopt(argc, argv, "hp:r:w:")) != -1) {
+    switch (option) {
+      case 'h':
+        testNackConsumer.usage();
+        break;
+      case 'p':
+        testNackConsumer.setInterestName(ndn::Name(optarg));
+        ++argNumber;
+        break;
+      case 'r':
+        testNackConsumer.setSecondInterestInterval(atoi(optarg));
+        break;
+      case 'w':
+        testNackConsumer.setWaitBeforeQuit(atoi(optarg));
+        ++argNumber;
+        break;
+      default:
+        testNackConsumer.usage();
+        break;
+    }
+  }
+
+  argc -= optind;
+  argv += optind;
+
+  if (argc || argNumber < 2)
+    testNackConsumer.usage();
+
+  testNackConsumer.run();
+
+  return 0;
+}
\ No newline at end of file