fw: handle dropped packets in strategies

refs #5128

Change-Id: Ic7f7e61b2dde66004d0291bb41e008961fc7b252
diff --git a/tests/daemon/fw/forwarder.t.cpp b/tests/daemon/fw/forwarder.t.cpp
index 5439492..77db2c8 100644
--- a/tests/daemon/fw/forwarder.t.cpp
+++ b/tests/daemon/fw/forwarder.t.cpp
@@ -161,16 +161,20 @@
 
   Pit& pit = forwarder.getPit();
   auto interestA1 = makeInterest("/A", false, nullopt, 8378);
-  shared_ptr<pit::Entry> pitA = pit.insert(*interestA1).first;
+  auto pitA = pit.insert(*interestA1).first;
   pitA->insertOrUpdateInRecord(*face1, *interestA1);
 
   auto interestA2 = makeInterest("/A", false, nullopt, 1698);
-  forwarder.onOutgoingInterest(pitA, *face2, *interestA2);
-
-  auto outA2 = pitA->getOutRecord(*face2);
-  BOOST_REQUIRE(outA2 != pitA->out_end());
+  auto outA2 = forwarder.onOutgoingInterest(pitA, *face2, *interestA2);
+  BOOST_REQUIRE(outA2 != nullptr);
   BOOST_CHECK_EQUAL(outA2->getLastNonce(), 1698);
 
+  // This packet will be dropped because HopLimit=0
+  auto interestA3 = makeInterest("/A", false, nullopt, 9876);
+  interestA3->setHopLimit(0);
+  auto outA3 = forwarder.onOutgoingInterest(pitA, *face2, *interestA3);
+  BOOST_CHECK(outA3 == nullptr);
+
   BOOST_REQUIRE_EQUAL(face2->sentInterests.size(), 1);
   BOOST_CHECK_EQUAL(face2->sentInterests.back().getNonce(), 1698);
 }
@@ -339,8 +343,8 @@
   auto face1 = addFace();
   auto face2 = addFace();
 
-  DummyStrategy& strategyA = choose<DummyStrategy>(forwarder, "/", DummyStrategy::getStrategyName());
-  DummyStrategy& strategyB = choose<DummyStrategy>(forwarder, "/B", DummyStrategy::getStrategyName());
+  auto& strategyA = choose<DummyStrategy>(forwarder, "/", DummyStrategy::getStrategyName());
+  auto& strategyB = choose<DummyStrategy>(forwarder, "/B", DummyStrategy::getStrategyName());
 
   auto interest1 = makeInterest("/A/1");
   strategyA.afterReceiveInterest_count = 0;
@@ -381,14 +385,14 @@
 
   Pit& pit = forwarder.getPit();
   auto interestD = makeInterest("/A/B/C/D");
-  shared_ptr<pit::Entry> pitD = pit.insert(*interestD).first;
+  auto pitD = pit.insert(*interestD).first;
   pitD->insertOrUpdateInRecord(*face1, *interestD);
   auto interestA = makeInterest("/A", true);
-  shared_ptr<pit::Entry> pitA = pit.insert(*interestA).first;
+  auto pitA = pit.insert(*interestA).first;
   pitA->insertOrUpdateInRecord(*face2, *interestA);
   pitA->insertOrUpdateInRecord(*face3, *interestA);
   auto interestC = makeInterest("/A/B/C", true);
-  shared_ptr<pit::Entry> pitC = pit.insert(*interestC).first;
+  auto pitC = pit.insert(*interestC).first;
   pitC->insertOrUpdateInRecord(*face3, *interestC);
   pitC->insertOrUpdateInRecord(*face4, *interestC);
 
@@ -406,6 +410,36 @@
   BOOST_CHECK_EQUAL(forwarder.getCounters().nUnsolicitedData, 0);
 }
 
+BOOST_AUTO_TEST_CASE(OutgoingData)
+{
+  auto face1 = addFace("dummy://", "dummy://", ndn::nfd::FACE_SCOPE_LOCAL);
+  auto face2 = addFace("dummy://", "dummy://", ndn::nfd::FACE_SCOPE_NON_LOCAL);
+  auto face3 = addFace();
+  face3->setId(face::INVALID_FACEID);
+
+  auto data = makeData("/QkzAWU6K");
+  auto localData = makeData("/localhost/YH8bqnbv");
+
+  face1->sentData.clear();
+  BOOST_CHECK(forwarder.onOutgoingData(*data, *face1));
+  BOOST_REQUIRE_EQUAL(face1->sentData.size(), 1);
+  BOOST_CHECK_EQUAL(face1->sentData.back().getName(), data->getName());
+
+  // scope control
+  face1->sentData.clear();
+  face2->sentData.clear();
+  BOOST_CHECK(!forwarder.onOutgoingData(*localData, *face2));
+  BOOST_CHECK_EQUAL(face2->sentData.size(), 0);
+  BOOST_CHECK(forwarder.onOutgoingData(*localData, *face1));
+  BOOST_REQUIRE_EQUAL(face1->sentData.size(), 1);
+  BOOST_CHECK_EQUAL(face1->sentData.back().getName(), localData->getName());
+
+  // face with invalid ID
+  face3->sentData.clear();
+  BOOST_CHECK(!forwarder.onOutgoingData(*data, *face3));
+  BOOST_CHECK_EQUAL(face3->sentData.size(), 0);
+}
+
 BOOST_AUTO_TEST_CASE(IncomingNack)
 {
   auto face1 = addFace();
@@ -415,27 +449,27 @@
                        ndn::nfd::FACE_PERSISTENCY_PERSISTENT,
                        ndn::nfd::LINK_TYPE_MULTI_ACCESS);
 
-  DummyStrategy& strategyA = choose<DummyStrategy>(forwarder, "/", DummyStrategy::getStrategyName());
-  DummyStrategy& strategyB = choose<DummyStrategy>(forwarder, "/B", DummyStrategy::getStrategyName());
+  auto& strategyA = choose<DummyStrategy>(forwarder, "/", DummyStrategy::getStrategyName());
+  auto& strategyB = choose<DummyStrategy>(forwarder, "/B", DummyStrategy::getStrategyName());
 
   Pit& pit = forwarder.getPit();
 
   // dispatch to the correct strategy
   auto interest1 = makeInterest("/A/AYJqayrzF", false, nullopt, 562);
-  shared_ptr<pit::Entry> pit1 = pit.insert(*interest1).first;
+  auto pit1 = pit.insert(*interest1).first;
   pit1->insertOrUpdateOutRecord(*face1, *interest1);
   auto interest2 = makeInterest("/B/EVyP73ru", false, nullopt, 221);
-  shared_ptr<pit::Entry> pit2 = pit.insert(*interest2).first;
+  auto pit2 = pit.insert(*interest2).first;
   pit2->insertOrUpdateOutRecord(*face1, *interest2);
 
-  lp::Nack nack1 = makeNack(*interest1, lp::NackReason::CONGESTION);
+  auto nack1 = makeNack(*interest1, lp::NackReason::CONGESTION);
   strategyA.afterReceiveNack_count = 0;
   strategyB.afterReceiveNack_count = 0;
   forwarder.onIncomingNack(FaceEndpoint(*face1, 0), nack1);
   BOOST_CHECK_EQUAL(strategyA.afterReceiveNack_count, 1);
   BOOST_CHECK_EQUAL(strategyB.afterReceiveNack_count, 0);
 
-  lp::Nack nack2 = makeNack(*interest2, lp::NackReason::CONGESTION);
+  auto nack2 = makeNack(*interest2, lp::NackReason::CONGESTION);
   strategyA.afterReceiveNack_count = 0;
   strategyB.afterReceiveNack_count = 0;
   forwarder.onIncomingNack(FaceEndpoint(*face1, 0), nack2);
@@ -449,7 +483,7 @@
   BOOST_CHECK_EQUAL(outRecord1->getIncomingNack()->getReason(), lp::NackReason::CONGESTION);
 
   // drop if no PIT entry
-  lp::Nack nack3 = makeNack(*makeInterest("/yEcw5HhdM", false, nullopt, 243), lp::NackReason::CONGESTION);
+  auto nack3 = makeNack(*makeInterest("/yEcw5HhdM", false, nullopt, 243), lp::NackReason::CONGESTION);
   strategyA.afterReceiveNack_count = 0;
   strategyB.afterReceiveNack_count = 0;
   forwarder.onIncomingNack(FaceEndpoint(*face1, 0), nack3);
@@ -458,10 +492,10 @@
 
   // drop if no out-record
   auto interest4 = makeInterest("/Etab4KpY", false, nullopt, 157);
-  shared_ptr<pit::Entry> pit4 = pit.insert(*interest4).first;
+  auto pit4 = pit.insert(*interest4).first;
   pit4->insertOrUpdateOutRecord(*face1, *interest4);
 
-  lp::Nack nack4a = makeNack(*interest4, lp::NackReason::CONGESTION);
+  auto nack4a = makeNack(*interest4, lp::NackReason::CONGESTION);
   strategyA.afterReceiveNack_count = 0;
   strategyB.afterReceiveNack_count = 0;
   forwarder.onIncomingNack(FaceEndpoint(*face2, 0), nack4a);
@@ -469,7 +503,7 @@
   BOOST_CHECK_EQUAL(strategyB.afterReceiveNack_count, 0);
 
   // drop if Nonce does not match out-record
-  lp::Nack nack4b = makeNack(*makeInterest("/Etab4KpY", false, nullopt, 294), lp::NackReason::CONGESTION);
+  auto nack4b = makeNack(*makeInterest("/Etab4KpY", false, nullopt, 294), lp::NackReason::CONGESTION);
   strategyA.afterReceiveNack_count = 0;
   strategyB.afterReceiveNack_count = 0;
   forwarder.onIncomingNack(FaceEndpoint(*face1, 0), nack4b);
@@ -493,6 +527,8 @@
                        ndn::nfd::FACE_SCOPE_NON_LOCAL,
                        ndn::nfd::FACE_PERSISTENCY_PERSISTENT,
                        ndn::nfd::LINK_TYPE_MULTI_ACCESS);
+  auto face4 = addFace();
+  face4->setId(face::INVALID_FACEID);
 
   Pit& pit = forwarder.getPit();
 
@@ -501,39 +537,41 @@
 
   // don't send Nack if there's no in-record
   auto interest1 = makeInterest("/fM5IVEtC", false, nullopt, 719);
-  shared_ptr<pit::Entry> pit1 = pit.insert(*interest1).first;
+  auto pit1 = pit.insert(*interest1).first;
   pit1->insertOrUpdateInRecord(*face1, *interest1);
 
   face2->sentNacks.clear();
-  forwarder.onOutgoingNack(pit1, *face2, nackHeader);
+  BOOST_CHECK(!forwarder.onOutgoingNack(pit1, *face2, nackHeader));
   BOOST_CHECK_EQUAL(face2->sentNacks.size(), 0);
 
   // send Nack with correct Nonce
   auto interest2a = makeInterest("/Vi8tRm9MG3", false, nullopt, 152);
-  shared_ptr<pit::Entry> pit2 = pit.insert(*interest2a).first;
+  auto pit2 = pit.insert(*interest2a).first;
   pit2->insertOrUpdateInRecord(*face1, *interest2a);
   auto interest2b = makeInterest("/Vi8tRm9MG3", false, nullopt, 808);
   pit2->insertOrUpdateInRecord(*face2, *interest2b);
-
   face1->sentNacks.clear();
-  forwarder.onOutgoingNack(pit2, *face1, nackHeader);
+  face2->sentNacks.clear();
+
+  BOOST_CHECK(forwarder.onOutgoingNack(pit2, *face1, nackHeader));
   BOOST_REQUIRE_EQUAL(face1->sentNacks.size(), 1);
   BOOST_CHECK_EQUAL(face1->sentNacks.back().getReason(), lp::NackReason::CONGESTION);
   BOOST_CHECK_EQUAL(face1->sentNacks.back().getInterest().getNonce(), 152);
+  BOOST_CHECK_EQUAL(face2->sentNacks.size(), 0);
 
-  // erase in-record
+  // in-record is erased
   auto inRecord2a = pit2->getInRecord(*face1);
   BOOST_CHECK(inRecord2a == pit2->in_end());
 
   // send Nack with correct Nonce
-  face2->sentNacks.clear();
-  forwarder.onOutgoingNack(pit2, *face2, nackHeader);
+  BOOST_CHECK(forwarder.onOutgoingNack(pit2, *face2, nackHeader));
+  BOOST_CHECK_EQUAL(face1->sentNacks.size(), 1);
   BOOST_REQUIRE_EQUAL(face2->sentNacks.size(), 1);
   BOOST_CHECK_EQUAL(face2->sentNacks.back().getReason(), lp::NackReason::CONGESTION);
   BOOST_CHECK_EQUAL(face2->sentNacks.back().getInterest().getNonce(), 808);
 
-  // erase in-record
-  auto inRecord2b = pit2->getInRecord(*face1);
+  // in-record is erased
+  auto inRecord2b = pit2->getInRecord(*face2);
   BOOST_CHECK(inRecord2b == pit2->in_end());
 
   // don't send Nack to multi-access face
@@ -541,8 +579,16 @@
   pit2->insertOrUpdateInRecord(*face3, *interest2c);
 
   face3->sentNacks.clear();
-  forwarder.onOutgoingNack(pit1, *face3, nackHeader);
+  BOOST_CHECK(!forwarder.onOutgoingNack(pit2, *face3, nackHeader));
   BOOST_CHECK_EQUAL(face3->sentNacks.size(), 0);
+
+  // don't send Nack to face with invalid ID
+  auto interest1b = makeInterest("/fM5IVEtC", false, nullopt, 553);
+  pit1->insertOrUpdateInRecord(*face4, *interest1b);
+
+  face4->sentNacks.clear();
+  BOOST_CHECK(!forwarder.onOutgoingNack(pit1, *face4, nackHeader));
+  BOOST_CHECK_EQUAL(face4->sentNacks.size(), 0);
 }
 
 BOOST_AUTO_TEST_CASE(InterestLoopNack)
diff --git a/tests/daemon/fw/strategy-tester.hpp b/tests/daemon/fw/strategy-tester.hpp
index 17806ec..cc40905 100644
--- a/tests/daemon/fw/strategy-tester.hpp
+++ b/tests/daemon/fw/strategy-tester.hpp
@@ -107,13 +107,15 @@
   }
 
 protected:
-  void
+  pit::OutRecord*
   sendInterest(const shared_ptr<pit::Entry>& pitEntry, Face& egress,
                const Interest& interest) override
   {
     sendInterestHistory.push_back({pitEntry->getInterest(), egress.getId(), interest});
-    pitEntry->insertOrUpdateOutRecord(egress, interest);
+    auto it = pitEntry->insertOrUpdateOutRecord(egress, interest);
+    BOOST_ASSERT(it != pitEntry->out_end());
     afterAction();
+    return &*it;
   }
 
   void
@@ -123,13 +125,14 @@
     afterAction();
   }
 
-  void
+  bool
   sendNack(const shared_ptr<pit::Entry>& pitEntry, Face& egress,
            const lp::NackHeader& header) override
   {
     sendNackHistory.push_back({pitEntry->getInterest(), egress.getId(), header});
     pitEntry->deleteInRecord(egress);
     afterAction();
+    return true;
   }
 
 public:
diff --git a/tests/daemon/fw/strategy.t.cpp b/tests/daemon/fw/strategy.t.cpp
index 8b27986..6f8e09b 100644
--- a/tests/daemon/fw/strategy.t.cpp
+++ b/tests/daemon/fw/strategy.t.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2014-2019,  Regents of the University of California,
+ * Copyright (c) 2014-2020,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -43,7 +43,7 @@
 BOOST_AUTO_TEST_SUITE(Fw)
 BOOST_FIXTURE_TEST_SUITE(TestStrategy, GlobalIoFixture)
 
-// Strategy registry is tested in table/strategy-choice.t.cpp and strategy-instantiation.t.cpp
+// Strategy registry is tested in strategy-choice.t.cpp and strategy-instantiation.t.cpp
 
 class FaceTableAccessTestStrategy : public DummyStrategy
 {
@@ -103,8 +103,6 @@
   BOOST_CHECK((strategy.removedFaces == std::vector<FaceId>{id2, id1}));
 }
 
-// LookupFib is tested in Fw/TestLinkForwarding test suite.
-
 BOOST_AUTO_TEST_SUITE_END() // TestStrategy
 BOOST_AUTO_TEST_SUITE_END() // Fw