fw: NccStrategy proper detection for new PIT entry

A PIT entry during straggler timer should be treated as new PIT entry
for strategy purposes.

refs #1971

Change-Id: I1156630ac0635f8e311a4f18400c5d7b535e3f20
diff --git a/daemon/fw/ncc-strategy.cpp b/daemon/fw/ncc-strategy.cpp
index bb93bd5..749e97c 100644
--- a/daemon/fw/ncc-strategy.cpp
+++ b/daemon/fw/ncc-strategy.cpp
@@ -59,11 +59,10 @@
 
   shared_ptr<PitEntryInfo> pitEntryInfo =
     pitEntry->getOrCreateStrategyInfo<PitEntryInfo>();
-  bool isNewInterest = pitEntryInfo->isNewInterest;
-  if (!isNewInterest) {
+  bool isNewPitEntry = !pitEntry->hasUnexpiredOutRecords();
+  if (!isNewPitEntry) {
     return;
   }
-  pitEntryInfo->isNewInterest = false;
 
   shared_ptr<MeasurementsEntryInfo> measurementsEntryInfo =
     this->getMeasurementsEntryInfo(pitEntry);
@@ -305,11 +304,6 @@
   this->bestFace.reset();
 }
 
-NccStrategy::PitEntryInfo::PitEntryInfo()
-  : isNewInterest(true)
-{
-}
-
 NccStrategy::PitEntryInfo::~PitEntryInfo()
 {
   scheduler::cancel(this->bestFaceTimeout);
diff --git a/daemon/fw/ncc-strategy.hpp b/daemon/fw/ncc-strategy.hpp
index 0f5ddac..fa27cd9 100644
--- a/daemon/fw/ncc-strategy.hpp
+++ b/daemon/fw/ncc-strategy.hpp
@@ -93,13 +93,10 @@
   class PitEntryInfo : public StrategyInfo
   {
   public:
-    PitEntryInfo();
-
     virtual
     ~PitEntryInfo();
 
   public:
-    bool isNewInterest;
     /// timer that expires when best face does not respond within predicted time
     EventId bestFaceTimeout;
     /// timer for propagating to another face
diff --git a/tests/daemon/fw/ncc-strategy.cpp b/tests/daemon/fw/ncc-strategy.cpp
index ef89bb1..7d82b5f 100644
--- a/tests/daemon/fw/ncc-strategy.cpp
+++ b/tests/daemon/fw/ncc-strategy.cpp
@@ -221,6 +221,56 @@
   BOOST_CHECK_EQUAL(strategy->m_sendInterestHistory[2].get<1>(), face1);
 }
 
+BOOST_AUTO_TEST_CASE(Bug1971)
+{
+  LimitedIo limitedIo;
+  Forwarder forwarder;
+  typedef StrategyTester<fw::NccStrategy> NccStrategyTester;
+  shared_ptr<NccStrategyTester> strategy = make_shared<NccStrategyTester>(ref(forwarder));
+  strategy->onAction += bind(&LimitedIo::afterOp, &limitedIo);
+
+  shared_ptr<DummyFace> face1 = make_shared<DummyFace>();
+  shared_ptr<DummyFace> face2 = make_shared<DummyFace>();
+  forwarder.addFace(face1);
+  forwarder.addFace(face2);
+
+  Fib& fib = forwarder.getFib();
+  shared_ptr<fib::Entry> fibEntry = fib.insert(Name()).first;
+  fibEntry->addNextHop(face2, 10);
+
+  StrategyChoice& strategyChoice = forwarder.getStrategyChoice();
+  strategyChoice.install(strategy);
+  strategyChoice.insert(Name(), strategy->getName());
+
+  Pit& pit = forwarder.getPit();
+
+  // first Interest: strategy forwards to face2
+  shared_ptr<Interest> interest1 = makeInterest("ndn:/M4mBXCsd");
+  interest1->setInterestLifetime(time::milliseconds(2000));
+  shared_ptr<pit::Entry> pitEntry1 = pit.insert(*interest1).first;
+
+  pitEntry1->insertOrUpdateInRecord(face1, *interest1);
+  strategy->afterReceiveInterest(*face1, *interest1, fibEntry, pitEntry1);
+  limitedIo.run(1 - strategy->m_sendInterestHistory.size(), time::milliseconds(2000));
+  BOOST_REQUIRE_EQUAL(strategy->m_sendInterestHistory.size(), 1);
+  BOOST_CHECK_EQUAL(strategy->m_sendInterestHistory[0].get<1>(), face2);
+
+  // face2 responds
+  shared_ptr<Data> data1 = makeData("ndn:/M4mBXCsd");
+  data1->setFreshnessPeriod(time::milliseconds(5));
+  strategy->beforeSatisfyInterest(pitEntry1, *face2, *data1);
+  pitEntry1->deleteOutRecord(face2);
+  pitEntry1->deleteInRecords();
+  limitedIo.run(LimitedIo::UNLIMITED_OPS, time::milliseconds(10));
+
+  // similar Interest: strategy should still forward it
+  pitEntry1->insertOrUpdateInRecord(face1, *interest1);
+  strategy->afterReceiveInterest(*face1, *interest1, fibEntry, pitEntry1);
+  limitedIo.run(2 - strategy->m_sendInterestHistory.size(), time::milliseconds(2000));
+  BOOST_REQUIRE_EQUAL(strategy->m_sendInterestHistory.size(), 2);
+  BOOST_CHECK_EQUAL(strategy->m_sendInterestHistory[1].get<1>(), face2);
+}
+
 BOOST_AUTO_TEST_SUITE_END()
 
 } // namespace tests