route: updates to name prefixes are more efficent.

With this change, only entries that have changed next-hops will
cause any change in the name prefix table.  Consequently, routing
table calculations no longer cause RIB registrations for unchanged
prefixes.  However, this is not a problem because FIB entries
refresh themselves.  This resulted in the removal of a test from the
TestNlsr suite.

refs: #2864

Change-Id: If28a04cb7bb47a3a6c32cd24578c68885d08d6b3
diff --git a/src/route/name-prefix-table-entry.cpp b/src/route/name-prefix-table-entry.cpp
index d9f8c65..298a2ef 100644
--- a/src/route/name-prefix-table-entry.cpp
+++ b/src/route/name-prefix-table-entry.cpp
@@ -50,6 +50,8 @@
 
   if (iterator != m_rteList.end()) {
     (*iterator)->decrementUseCount();
+    // Remove this NamePrefixEntry from the RoutingTablePoolEntry
+    (*iterator)->namePrefixTableEntries.erase(getNamePrefix());
     m_rteList.erase(iterator);
   }
   else {
diff --git a/src/route/name-prefix-table.cpp b/src/route/name-prefix-table.cpp
index 251bfc5..3b67bcb 100644
--- a/src/route/name-prefix-table.cpp
+++ b/src/route/name-prefix-table.cpp
@@ -34,9 +34,9 @@
 INIT_LOGGER("NamePrefixTable");
 
 bool
-npteCompare(NamePrefixTableEntry& npte, const ndn::Name& name)
+npteCompare(std::shared_ptr<NamePrefixTableEntry>& npte, const ndn::Name& name)
 {
-  return npte.getNamePrefix() == name;
+  return npte->getNamePrefix() == name;
 }
 
 void
@@ -44,12 +44,15 @@
 {
 
   // Check if the advertised name prefix is in the table already.
-  NptEntryList::iterator nameItr = std::find(m_table.begin(),
-                                             m_table.end(),
-                                             name);
+  NptEntryList::iterator nameItr =
+    std::find_if(m_table.begin(),
+                 m_table.end(),
+                 [&] (const std::shared_ptr<NamePrefixTableEntry>& entry) {
+                   return name == entry->getNamePrefix();
+                 });
 
   // Attempt to find a routing table pool entry (RTPE) we can use.
-  RtpEntryMap::iterator rtpeItr = m_rtpool.find(destRouter);
+  RoutingTableEntryPool::iterator rtpeItr = m_rtpool.find(destRouter);
 
   // These declarations just to make the compiler happy...
   RoutingTablePoolEntry rtpe;
@@ -78,19 +81,19 @@
     rtpePtr = (*rtpeItr).second;
   }
 
+  std::shared_ptr<NamePrefixTableEntry> npte;
   // Either we have to make a new NPT entry or there already was one.
   if (nameItr == m_table.end()) {
     _LOG_DEBUG("Adding origin: " << rtpePtr->getDestination()
                << " to a new name prefix: " << name);
-    NamePrefixTableEntry npte(name);
-    npte.addRoutingTableEntry(rtpePtr);
-    npte.generateNhlfromRteList();
+    npte = make_shared<NamePrefixTableEntry>(name);
+    npte->addRoutingTableEntry(rtpePtr);
+    npte->generateNhlfromRteList();
     m_table.push_back(npte);
-
     // If this entry has next hops, we need to inform the FIB
-    if (npte.getNexthopList().getSize() > 0) {
+    if (npte->getNexthopList().getSize() > 0) {
       _LOG_TRACE("Updating FIB with next hops for " << npte);
-      m_nlsr.getFib().update(name, npte.getNexthopList());
+      m_nlsr.getFib().update(name, npte->getNexthopList());
     }
     // The routing table may recalculate and add a routing table entry
     // with no next hops to replace an existing routing table entry. In
@@ -99,25 +102,29 @@
     // remain in the Name Prefix Table as a future routing table
     // calculation may add next hops.
     else {
-      _LOG_TRACE(npte << " has no next hops; removing from FIB");
+      _LOG_TRACE(*npte << " has no next hops; removing from FIB");
       m_nlsr.getFib().remove(name);
     }
   }
   else {
+    npte = *nameItr;
     _LOG_TRACE("Adding origin: " << rtpePtr->getDestination()
                << " to existing prefix: " << *nameItr);
-    nameItr->addRoutingTableEntry(rtpePtr);
-    nameItr->generateNhlfromRteList();
+    (*nameItr)->addRoutingTableEntry(rtpePtr);
+    (*nameItr)->generateNhlfromRteList();
 
-    if (nameItr->getNexthopList().getSize() > 0) {
+    if ((*nameItr)->getNexthopList().getSize() > 0) {
       _LOG_TRACE("Updating FIB with next hops for " << (*nameItr));
-      m_nlsr.getFib().update(name, nameItr->getNexthopList());
+      m_nlsr.getFib().update(name, (*nameItr)->getNexthopList());
     }
     else {
       _LOG_TRACE((*nameItr) << " has no next hops; removing from FIB");
       m_nlsr.getFib().remove(name);
     }
   }
+  // Add the reference to this NPT to the RTPE.
+  rtpePtr->namePrefixTableEntries.emplace(
+    std::make_pair(npte->getNamePrefix(), std::weak_ptr<NamePrefixTableEntry>(npte)));
 }
 
 void
@@ -126,7 +133,7 @@
   _LOG_DEBUG("Removing origin: " << destRouter << " from " << name);
 
   // Fetch an iterator to the appropriate pair object in the pool.
-  RtpEntryMap::iterator rtpeItr = m_rtpool.find(destRouter);
+  RoutingTableEntryPool::iterator rtpeItr = m_rtpool.find(destRouter);
 
   // Simple error checking to prevent any unusual behavior in the case
   // that we try to remove an entry that isn't there.
@@ -139,15 +146,18 @@
   std::shared_ptr<RoutingTablePoolEntry> rtpePtr = rtpeItr->second;
 
   // Ensure that the entry exists
-  NptEntryList::iterator nameItr = std::find_if(m_table.begin(), m_table.end(),
-                                                std::bind(&npteCompare, _1, name));
+  NptEntryList::iterator nameItr =
+    std::find_if(m_table.begin(), m_table.end(),
+                 [&] (const std::shared_ptr<NamePrefixTableEntry>& entry) {
+                   return entry->getNamePrefix() == name;
+                 });
   if (nameItr != m_table.end()) {
     _LOG_TRACE("Removing origin: " << rtpePtr->getDestination()
-               << " from prefix: " << *nameItr);
+               << " from prefix: " << **nameItr);
 
     // Rather than iterating through the whole list periodically, just
     // delete them here if they have no references.
-    if ((*nameItr).removeRoutingTableEntry(rtpePtr) == 0) {
+    if ((*nameItr)->removeRoutingTableEntry(rtpePtr) == 0) {
       deleteRtpeFromPool(rtpePtr);
     }
 
@@ -166,17 +176,17 @@
     //   Prefix Table. Once a new Name LSA advertises this prefix, a
     //   new entry for the prefix will be created.
     //
-    if ((*nameItr).getRteListSize() == 0) {
-      _LOG_TRACE(*nameItr << " has no routing table entries;"
+    if ((*nameItr)->getRteListSize() == 0) {
+      _LOG_TRACE(**nameItr << " has no routing table entries;"
                  << " removing from table and FIB");
       m_table.erase(nameItr);
       m_nlsr.getFib().remove(name);
     }
     else {
-      _LOG_TRACE(*nameItr << " has other routing table entries;"
+      _LOG_TRACE(**nameItr << " has other routing table entries;"
                  << " updating FIB with next hops");
-      (*nameItr).generateNhlfromRteList();
-      m_nlsr.getFib().update(name, (*nameItr).getNexthopList());
+      (*nameItr)->generateNhlfromRteList();
+      m_nlsr.getFib().update(name, (*nameItr)->getNexthopList());
     }
   }
   else {
@@ -190,24 +200,32 @@
 {
   _LOG_DEBUG("Updating table with newly calculated routes");
 
-  // Update each name prefix table entry in the NPT with the
-  // newly calculated next hops.
-  for (auto&& npte : m_table) {
-    // For each routing table pool entry in this NPT entry.
-    for (auto&& rtpe : npte.getRteList()) {
-      _LOG_TRACE("Updating next hops to origin: " << rtpe->getDestination()
-                 << " for prefix: " << npte);
-      RoutingTableEntry* rteCheck =
-        m_nlsr.getRoutingTable().findRoutingTableEntry(rtpe->getDestination());
+  // Iterate over each pool entry we have
+  for (auto&& poolEntryPair : m_rtpool) {
+    auto&& poolEntry = poolEntryPair.second;
+    RoutingTableEntry* sourceEntry =
+      m_nlsr.getRoutingTable().findRoutingTableEntry(poolEntry->getDestination());
+    // If this pool entry has a corresponding entry in the routing table now
+    if (sourceEntry != nullptr && poolEntry->getNexthopList() != sourceEntry->getNexthopList()) {
+      _LOG_DEBUG("Routing entry: " << poolEntry->getDestination() << " has changed next-hops.");
+      poolEntry->setNexthopList(sourceEntry->getNexthopList());
+      for (const auto& nameEntry : poolEntry->namePrefixTableEntries) {
+        auto nameEntryFullPtr = nameEntry.second.lock();
+        addEntry(nameEntryFullPtr->getNamePrefix(), poolEntry->getDestination());
+      }
+    }
+    else if (sourceEntry == nullptr) {
+      _LOG_DEBUG("Routing entry: " << poolEntry->getDestination() << " now has no next-hops.");
+      poolEntry->getNexthopList().reset();
+      for (const auto& nameEntry : poolEntry->namePrefixTableEntries) {
+        auto nameEntryFullPtr = nameEntry.second.lock();
+        addEntry(nameEntryFullPtr->getNamePrefix(), poolEntry->getDestination());
+      }
 
-      // If there is a routing table entry for this prefix, update the NPT with it.
-      if (rteCheck != nullptr) {
-        rtpe->setNexthopList(rteCheck->getNexthopList());
-      }
-      else {
-        rtpe->getNexthopList().reset();
-      }
-      addEntry(npte.getNamePrefix(), rtpe->getDestination());
+    }
+    else {
+      _LOG_TRACE("No change in routing entry:" << poolEntry->getDestination()
+                 << ", no action necessary.");
     }
   }
 }
@@ -218,13 +236,11 @@
 std::shared_ptr<RoutingTablePoolEntry>
 NamePrefixTable::addRtpeToPool(RoutingTablePoolEntry& rtpe)
 {
-  RtpEntryMap::iterator poolItr =
+  RoutingTableEntryPool::iterator poolItr =
     m_rtpool.insert(std::make_pair(rtpe.getDestination(),
                                    std::make_shared<RoutingTablePoolEntry>
                                    (rtpe)))
     .first;
-  //| There's gotta be a more efficient way to do this
-  //std::shared_ptr<RoutingTablePoolEntry> poolPtr = &(poolItr->second);
   return poolItr->second;
 }
 
@@ -255,8 +271,8 @@
 {
   os << "----------------NPT----------------------\n";
 
-  for (const NamePrefixTableEntry& entry : table) {
-    os << entry << std::endl;
+  for (const auto& entryPtr : table) {
+    os << *entryPtr << std::endl;
   }
 
   return os;
diff --git a/src/route/name-prefix-table.hpp b/src/route/name-prefix-table.hpp
index 3e91ea5..a4dabc4 100644
--- a/src/route/name-prefix-table.hpp
+++ b/src/route/name-prefix-table.hpp
@@ -36,12 +36,10 @@
 class NamePrefixTable
 {
 public:
-
-  typedef std::list<NamePrefixTableEntry> NptEntryList;
-  typedef NptEntryList::const_iterator const_iterator;
-
-  typedef std::unordered_map<ndn::Name, std::shared_ptr<RoutingTablePoolEntry>>
-           RtpEntryMap;
+  using RoutingTableEntryPool =
+    std::unordered_map<ndn::Name, std::shared_ptr<RoutingTablePoolEntry>>;
+  using NptEntryList = std::list<std::shared_ptr<NamePrefixTableEntry>>;
+  using const_iterator = NptEntryList::const_iterator;
 
   NamePrefixTable(Nlsr& nlsr)
     : m_nlsr(nlsr)
@@ -122,7 +120,8 @@
   end() const;
 
 PUBLIC_WITH_TESTS_ELSE_PRIVATE:
-  RtpEntryMap m_rtpool;
+  RoutingTableEntryPool m_rtpool;
+
   NptEntryList m_table;
 
 private:
diff --git a/src/route/nexthop-list.cpp b/src/route/nexthop-list.cpp
index 97a89b0..903ca48 100644
--- a/src/route/nexthop-list.cpp
+++ b/src/route/nexthop-list.cpp
@@ -18,6 +18,7 @@
  * You should have received a copy of the GNU General Public License along with
  * NLSR, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
  **/
+
 #include "nexthop-list.hpp"
 #include "common.hpp"
 #include "nexthop.hpp"
@@ -61,6 +62,12 @@
   return true;
 }
 
+bool
+operator!=(const NexthopList& lhs, const NexthopList& rhs)
+{
+  return !(lhs == rhs);
+}
+
 std::ostream&
 operator<<(std::ostream& os, const NexthopList& nhl)
 {
diff --git a/src/route/nexthop-list.hpp b/src/route/nexthop-list.hpp
index b35f3bf..1c35079 100644
--- a/src/route/nexthop-list.hpp
+++ b/src/route/nexthop-list.hpp
@@ -27,7 +27,6 @@
 #include <set>
 #include <iostream>
 #include <boost/cstdint.hpp>
-
 #include <ndn-cxx/face.hpp>
 
 namespace nlsr {
@@ -140,6 +139,9 @@
 bool
 operator==(const NexthopList& lhs, const NexthopList& rhs);
 
+bool
+operator!=(const NexthopList& lhs, const NexthopList& rhs);
+
 std::ostream&
 operator<<(std::ostream& os, const NexthopList& nhl);
 
diff --git a/src/route/routing-table-pool-entry.cpp b/src/route/routing-table-pool-entry.cpp
index 881b1e3..7e081ae 100644
--- a/src/route/routing-table-pool-entry.cpp
+++ b/src/route/routing-table-pool-entry.cpp
@@ -19,6 +19,7 @@
  **/
 
 #include "routing-table-pool-entry.hpp"
+#include "name-prefix-table-entry.hpp"
 
 namespace nlsr {
 
@@ -28,9 +29,13 @@
   os << "RoutingTablePoolEntry("
      << "Destination router: " << rtpe.getDestination()
      << "Next hop list: ";
-  for (auto && nh : rtpe.getNexthopList()) {
+  for (const auto& nh : rtpe.getNexthopList()) {
     os << nh;
   }
+  os << "NamePrefixTableEntries using this entry:";
+  for (const auto& entryPtr : rtpe.namePrefixTableEntries) {
+    os << entryPtr.first << ":";
+  }
 
   return os;
 }
diff --git a/src/route/routing-table-pool-entry.hpp b/src/route/routing-table-pool-entry.hpp
index ff84dfe..14e7c7d 100644
--- a/src/route/routing-table-pool-entry.hpp
+++ b/src/route/routing-table-pool-entry.hpp
@@ -27,6 +27,7 @@
 
 #include <iostream>
 #include <ndn-cxx/name.hpp>
+#include <unordered_map>
 
 namespace nlsr {
 
@@ -43,6 +44,8 @@
  * original entries, which provides a minimal memory solution.
  * \sa NamePrefixTable
  */
+class NamePrefixTableEntry;
+
 class RoutingTablePoolEntry : public RoutingTableEntry
 {
 public:
@@ -100,6 +103,10 @@
     m_nexthopList = nhl;
   }
 
+public:
+  std::unordered_map<ndn::Name, std::weak_ptr<NamePrefixTableEntry>>
+    namePrefixTableEntries;
+
 private:
   uint64_t m_useCount;
 
diff --git a/tests/test-name-prefix-table.cpp b/tests/test-name-prefix-table.cpp
index eca19f6..79cd651 100644
--- a/tests/test-name-prefix-table.cpp
+++ b/tests/test-name-prefix-table.cpp
@@ -19,9 +19,9 @@
  * NLSR, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
  **/
 
+#include "route/name-prefix-table.hpp"
 #include "nlsr.hpp"
 #include "test-common.hpp"
-#include "route/name-prefix-table.hpp"
 
 #include <ndn-cxx/util/dummy-client-face.hpp>
 
@@ -123,14 +123,14 @@
 
   // Each NPT entry should have a destination router
   it = npt.begin();
-  BOOST_REQUIRE_EQUAL(it->getNamePrefix(), buptRouterName);
-  BOOST_REQUIRE_EQUAL(it->getRteList().size(), 1);
-  BOOST_CHECK_EQUAL((*it->getRteList().begin())->getDestination(), buptRouterName);
+  BOOST_REQUIRE_EQUAL((*it)->getNamePrefix(), buptRouterName);
+  BOOST_REQUIRE_EQUAL((*it)->getRteList().size(), 1);
+  BOOST_CHECK_EQUAL((*(*it)->getRteList().begin())->getDestination(), buptRouterName);
 
   ++it;
-  BOOST_REQUIRE_EQUAL(it->getNamePrefix(), buptAdvertisedName);
-  BOOST_REQUIRE_EQUAL(it->getRteList().size(), 1);
-  BOOST_CHECK_EQUAL((*it->getRteList().begin())->getDestination(), buptRouterName);
+  BOOST_REQUIRE_EQUAL((*it)->getNamePrefix(), buptAdvertisedName);
+  BOOST_REQUIRE_EQUAL((*it)->getRteList().size(), 1);
+  BOOST_CHECK_EQUAL((*(*it)->getRteList().begin())->getDestination(), buptRouterName);
 }
 
 BOOST_FIXTURE_TEST_CASE(AddEntryToPool, NamePrefixTableFixture)
@@ -168,11 +168,13 @@
   npt.addEntry("/ndn/memphis/rtr2", "/ndn/memphis/rtr1");
 
   NamePrefixTable::NptEntryList::iterator nItr =
-    std::find(npt.m_table.begin(),
-              npt.m_table.end(),
-              npte1);
+    std::find_if(npt.m_table.begin(),
+                 npt.m_table.end(),
+                 [&] (const std::shared_ptr<NamePrefixTableEntry>& entry) {
+                   return entry->getNamePrefix() == npte1.getNamePrefix();
+                 });
 
-  std::list<std::shared_ptr<RoutingTablePoolEntry>> rtpeList = nItr->getRteList();
+  std::list<std::shared_ptr<RoutingTablePoolEntry>> rtpeList = (*nItr)->getRteList();
   std::list<std::shared_ptr<RoutingTablePoolEntry>>::iterator rItr =
     std::find(rtpeList.begin(),
               rtpeList.end(),
@@ -186,7 +188,7 @@
   RoutingTablePoolEntry rtpe1("/ndn/memphis/rtr1", 0);
 
   NamePrefixTableEntry npte1("/ndn/memphis/rtr2");
-  npt.m_table.push_back(npte1);
+  npt.m_table.push_back(make_shared<NamePrefixTableEntry>(npte1));
 
   npt.addEntry("/ndn/memphis/rtr2", "/ndn/memphis/rtr1");
   npt.addEntry("/ndn/memphis/rtr2", "/ndn/memphis/altrtr");
@@ -194,16 +196,126 @@
   npt.removeEntry("/ndn/memphis/rtr2", "/ndn/memphis/rtr1");
 
   NamePrefixTable::NptEntryList::iterator nItr =
-    std::find(npt.m_table.begin(),
-              npt.m_table.end(),
-              npte1);
+    std::find_if(npt.m_table.begin(),
+                 npt.m_table.end(),
+                 [&] (const std::shared_ptr<NamePrefixTableEntry>& entry) {
+                   return entry->getNamePrefix() == npte1.getNamePrefix();
+                 });
 
-  std::list<std::shared_ptr<RoutingTablePoolEntry>> rtpeList = nItr->getRteList();
+  std::list<std::shared_ptr<RoutingTablePoolEntry>> rtpeList = (*nItr)->getRteList();
 
   BOOST_CHECK_EQUAL(rtpeList.size(), 1);
   BOOST_CHECK_EQUAL(npt.m_rtpool.size(), 1);
 }
 
+BOOST_FIXTURE_TEST_CASE(AddNptEntryPtrToRoutingEntry, NamePrefixTableFixture)
+{
+  NamePrefixTable& npt = nlsr.getNamePrefixTable();
+  NamePrefixTableEntry npte1("/ndn/memphis/rtr2");
+  npt.m_table.push_back(make_shared<NamePrefixTableEntry>(npte1));
+
+  npt.addEntry("/ndn/memphis/rtr2", "/ndn/memphis/rtr1");
+
+  NamePrefixTable::NptEntryList::iterator nItr =
+    std::find_if(npt.m_table.begin(),
+                 npt.m_table.end(),
+                 [&] (const std::shared_ptr<NamePrefixTableEntry>& entry) {
+                   return entry->getNamePrefix() == npte1.getNamePrefix();
+                 });
+
+  std::list<std::shared_ptr<RoutingTablePoolEntry>> rtpeList = (*nItr)->getRteList();
+
+  BOOST_CHECK_EQUAL(rtpeList.size(), 1);
+
+  auto& namePrefixPtrs = rtpeList.front()->namePrefixTableEntries;
+
+  auto nptIterator = namePrefixPtrs.find(npte1.getNamePrefix());
+  BOOST_REQUIRE(nptIterator != namePrefixPtrs.end());
+  auto nptSharedPtr = nptIterator->second.lock();
+  BOOST_CHECK_EQUAL(*nptSharedPtr, npte1);
+}
+
+BOOST_FIXTURE_TEST_CASE(RemoveNptEntryPtrFromRoutingEntry, NamePrefixTableFixture)
+{
+  NamePrefixTable& npt = nlsr.getNamePrefixTable();
+  NamePrefixTableEntry npte1("/ndn/memphis/rtr1");
+  NamePrefixTableEntry npte2("/ndn/memphis/rtr2");
+  RoutingTableEntry rte1("/ndn/memphis/destination1");
+  npt.m_table.push_back(make_shared<NamePrefixTableEntry>(npte1));
+  npt.m_table.push_back(make_shared<NamePrefixTableEntry>(npte2));
+
+  npt.addEntry(npte1.getNamePrefix(), rte1.getDestination());
+  // We have to add two entries, otherwise the routing pool entry will be deleted.
+  npt.addEntry(npte2.getNamePrefix(), rte1.getDestination());
+  npt.removeEntry(npte2.getNamePrefix(), rte1.getDestination());
+
+  NamePrefixTable::NptEntryList::iterator nItr =
+    std::find_if(npt.m_table.begin(),
+                 npt.m_table.end(),
+                 [&] (const std::shared_ptr<NamePrefixTableEntry>& entry) {
+                   return entry->getNamePrefix() == npte1.getNamePrefix();
+                 });
+
+  std::list<std::shared_ptr<RoutingTablePoolEntry>> rtpeList = (*nItr)->getRteList();
+
+  BOOST_CHECK_EQUAL(rtpeList.size(), 1);
+
+  auto& namePrefixPtrs = rtpeList.front()->namePrefixTableEntries;
+
+  // We should have removed the second one
+  BOOST_CHECK_EQUAL(namePrefixPtrs.size(), 1);
+
+  auto nptIterator = namePrefixPtrs.find(npte1.getNamePrefix());
+
+  BOOST_REQUIRE(nptIterator != namePrefixPtrs.end());
+  auto nptSharedPtr = nptIterator->second.lock();
+  BOOST_CHECK_EQUAL(*nptSharedPtr, npte1);
+}
+
+BOOST_FIXTURE_TEST_CASE(RoutingTableUpdate, NamePrefixTableFixture)
+{
+  NamePrefixTable& namePrefixTable = nlsr.getNamePrefixTable();
+  RoutingTable& routingTable = nlsr.getRoutingTable();
+  const ndn::Name destination = ndn::Name{"/ndn/destination1"};
+  NextHop hop1{"upd4://10.0.0.1", 0};
+  NextHop hop2{"udp4://10.0.0.2", 1};
+  NextHop hop3{"udp4://10.0.0.3", 2};
+  const NamePrefixTableEntry entry1{"/ndn/router1"};
+  namePrefixTable.addEntry(entry1.getNamePrefix(), destination);
+
+  routingTable.addNextHop(destination, hop1);
+  routingTable.addNextHop(destination, hop2);
+
+  namePrefixTable.updateWithNewRoute();
+
+  // At this point the NamePrefixTableEntry should have two NextHops.
+  auto nameIterator = std::find_if(namePrefixTable.begin(), namePrefixTable.end(),
+                                   [&] (const std::shared_ptr<NamePrefixTableEntry>& entry) {
+                                     return entry1.getNamePrefix() == entry->getNamePrefix();
+                                   });
+  BOOST_REQUIRE(nameIterator != namePrefixTable.end());
+
+  auto iterator = namePrefixTable.m_rtpool.find(destination);
+  BOOST_REQUIRE(iterator != namePrefixTable.m_rtpool.end());
+  auto nextHops = (iterator->second)->getNexthopList();
+  BOOST_CHECK_EQUAL(nextHops.getSize(), 2);
+
+  // Add the other NextHop
+  routingTable.addNextHop(destination, hop3);
+  namePrefixTable.updateWithNewRoute();
+
+  // At this point the NamePrefixTableEntry should have three NextHops.
+  nameIterator = std::find_if(namePrefixTable.begin(), namePrefixTable.end(),
+                              [&] (const std::shared_ptr<NamePrefixTableEntry>& entry) {
+                                return entry1.getNamePrefix() == entry->getNamePrefix();
+                              });
+  BOOST_REQUIRE(nameIterator != namePrefixTable.end());
+  iterator = namePrefixTable.m_rtpool.find(destination);
+  BOOST_REQUIRE(iterator != namePrefixTable.m_rtpool.end());
+  nextHops = (iterator->second)->getNexthopList();
+  BOOST_CHECK_EQUAL(nextHops.getSize(), 3);
+}
+
 BOOST_AUTO_TEST_SUITE_END()
 
 } // namespace test
diff --git a/tests/test-nlsr.cpp b/tests/test-nlsr.cpp
index cba776b..502d5f6 100644
--- a/tests/test-nlsr.cpp
+++ b/tests/test-nlsr.cpp
@@ -22,6 +22,7 @@
 #include "nlsr.hpp"
 #include "test-common.hpp"
 #include "control-commands.hpp"
+#include "logger.hpp"
 
 #include <ndn-cxx/mgmt/nfd/face-event-notification.hpp>
 
@@ -315,118 +316,6 @@
   BOOST_CHECK(rtEntry == nullptr);
 }
 
-// Bug #2733
-// This test checks that when a face for an inactive node is destroyed, an
-// Adjacency LSA build does not postpone the LSA refresh and cause RIB
-// entries for other nodes' name prefixes to not be refreshed.
-//
-// This test is invalid when Issue #2732 is implemented since an Adjacency LSA
-// refresh will not cause RIB entries for other nodes' name prefixes to be refreshed.
-BOOST_FIXTURE_TEST_CASE(FaceDestroyEventInactive, UnitTestTimeFixture)
-{
-  std::shared_ptr<ndn::util::DummyClientFace> face = std::make_shared<ndn::util::DummyClientFace>(g_ioService);
-  Nlsr nlsr(g_ioService, g_scheduler, std::ref(*face), g_keyChain);
-  Lsdb& lsdb = nlsr.getLsdb();
-
-  // Simulate loading configuration file
-  ConfParameter& conf = nlsr.getConfParameter();
-  conf.setNetwork("/ndn");
-  conf.setSiteName("/site");
-  conf.setRouterName("/%C1.router/this-router");
-  conf.setFirstHelloInterval(0);
-  conf.setAdjLsaBuildInterval(0);
-  conf.setRoutingCalcInterval(0);
-
-  // Add neighbors
-  AdjacencyList& neighbors = nlsr.getAdjacencyList();
-
-  uint64_t destroyFaceId = 128;
-
-  // Create an inactive neighbor whose Face will be destroyed
-  Adjacent failNeighbor("/ndn/neighborA", ndn::util::FaceUri("udp4://10.0.0.1"), 10, Adjacent::STATUS_INACTIVE, 3,
-                        destroyFaceId);
-  neighbors.insert(failNeighbor);
-
-  // Create an additional active neighbor so an adjacency LSA can be built
-  Adjacent otherNeighbor("/ndn/neighborB", ndn::util::FaceUri("udp4://10.0.0.2"), 25, Adjacent::STATUS_ACTIVE, 0, 256);
-  neighbors.insert(otherNeighbor);
-
-  // Add a name for the neighbor to advertise
-  NamePrefixList nameList;
-  ndn::Name nameToAdvertise("/ndn/neighborB/name");
-  nameList.insert(nameToAdvertise);
-
-  NameLsa nameLsa("/ndn/neighborB", 25, ndn::time::system_clock::now(), nameList);
-  lsdb.installNameLsa(nameLsa);
-
-  nlsr.initialize();
-
-  // Simulate successful HELLO responses from neighbor B
-  lsdb.scheduleAdjLsaBuild();
-
-  // Set up adjacency LSAs
-  // This router
-  Adjacent thisRouter(conf.getRouterPrefix(), ndn::util::FaceUri("udp4://10.0.0.2"), 25, Adjacent::STATUS_ACTIVE, 0, 256);
-
-  AdjLsa ownAdjLsa(conf.getRouterPrefix(), 10, ndn::time::system_clock::now(), 1, neighbors);
-  lsdb.installAdjLsa(ownAdjLsa);
-
-  // Other ACTIVE router
-  AdjacencyList otherAdjacencies;
-  otherAdjacencies.insert(thisRouter);
-
-  AdjLsa otherAdjLsa("/ndn/neighborB", 10,
-                     ndn::time::system_clock::now() + ndn::time::seconds(3600), 1, otherAdjacencies);
-
-  lsdb.installAdjLsa(otherAdjLsa);
-
-  // Run the scheduler to build an adjacency LSA
-  this->advanceClocks(ndn::time::milliseconds(1));
-
-  ndn::Name key = ndn::Name(nlsr.getConfParameter().getRouterPrefix()).append(AdjLsa::TYPE_STRING);
-  AdjLsa* lsa = lsdb.findAdjLsa(key);
-  BOOST_REQUIRE(lsa != nullptr);
-
-  // Cancel previous LSA expiration event
-  g_scheduler.cancelEvent(lsa->getExpiringEventId());
-
-  // Set expiration time for own Adjacency LSA to earlier value for unit-test
-  //
-  // Expiration time is negative to offset the GRACE_PERIOD (10 seconds) automatically applied
-  // to expiration times
-  ndn::EventId id = lsdb.scheduleAdjLsaExpiration(key, lsa->getLsSeqNo(), -ndn::time::seconds(9));
-  lsa->setExpiringEventId(id);
-
-  // Generate a FaceEventDestroyed notification
-  ndn::nfd::FaceEventNotification event;
-  event.setKind(ndn::nfd::FACE_EVENT_DESTROYED)
-       .setFaceId(destroyFaceId);
-
-  std::shared_ptr<ndn::Data> data = std::make_shared<ndn::Data>("/localhost/nfd/faces/events/%FE%00");
-  data->setContent(event.wireEncode());
-  nlsr.getKeyChain().sign(*data);
-
-  // Receive the FaceEventDestroyed notification
-  face->receive(*data);
-
-  // Run the scheduler to expire the Adjacency LSA. The expiration should refresh the RIB
-  // entries associated with Neighbor B's advertised prefix.
-  face->sentInterests.clear();
-  this->advanceClocks(ndn::time::seconds(1));
-
-  // The Face should have two sent Interests: the face event and a RIB registration
-  BOOST_REQUIRE(face->sentInterests.size() > 0);
-  const ndn::Interest& interest = face->sentInterests.back();
-
-  ndn::nfd::ControlParameters parameters;
-  ndn::Name::Component verb;
-  BOOST_REQUIRE_NO_THROW(extractRibCommandParameters(interest,
-                                                     verb,
-                                                     parameters));
-  BOOST_CHECK_EQUAL(verb, ndn::Name::Component("register"));
-  BOOST_CHECK_EQUAL(parameters.getName(), nameToAdvertise);
-}
-
 BOOST_AUTO_TEST_CASE(GetCertificate)
 {
   // Create certificate