face: accommodate empty InterestCallback/DataCallback/NackCallback

refs #3724

Change-Id: Iebfeae48d37d6dedbbdbca2a05730247807675d5
diff --git a/src/detail/interest-filter-record.hpp b/src/detail/interest-filter-record.hpp
index c2916f8..f771d16 100644
--- a/src/detail/interest-filter-record.hpp
+++ b/src/detail/interest-filter-record.hpp
@@ -67,11 +67,14 @@
 
   /**
    * @brief invokes the InterestCallback
+   * @note This method does nothing if the Interest callback is empty
    */
   void
   invokeInterestCallback(const Interest& interest) const
   {
-    m_interestCallback(m_filter, interest);
+    if (m_interestCallback != nullptr) {
+      m_interestCallback(m_filter, interest);
+    }
   }
 
 private:
diff --git a/src/detail/pending-interest.hpp b/src/detail/pending-interest.hpp
index f21fc2e..735db80 100644
--- a/src/detail/pending-interest.hpp
+++ b/src/detail/pending-interest.hpp
@@ -76,20 +76,26 @@
 
   /**
    * @brief invokes the Data callback
+   * @note This method does nothing if the Data callback is empty
    */
   void
   invokeDataCallback(const Data& data)
   {
-    m_dataCallback(*m_interest, data);
+    if (m_dataCallback != nullptr) {
+      m_dataCallback(*m_interest, data);
+    }
   }
 
   /**
    * @brief invokes the Nack callback
+   * @note This method does nothing if the Nack callback is empty
    */
   void
   invokeNackCallback(const lp::Nack& nack)
   {
-    m_nackCallback(*m_interest, nack);
+    if (m_nackCallback != nullptr) {
+      m_nackCallback(*m_interest, nack);
+    }
   }
 
   /**
diff --git a/tests/unit-tests/face.t.cpp b/tests/unit-tests/face.t.cpp
index 2ffe6d4..b0f78f9 100644
--- a/tests/unit-tests/face.t.cpp
+++ b/tests/unit-tests/face.t.cpp
@@ -94,6 +94,20 @@
   BOOST_CHECK_EQUAL(nTimeouts, 1);
 }
 
+BOOST_AUTO_TEST_CASE(ExpressInterestEmptyDataCallback)
+{
+  face.expressInterest(Interest("/Hello/World"),
+                       nullptr,
+                       bind([] { BOOST_FAIL("Unexpected Nack"); }),
+                       bind([] { BOOST_FAIL("Unexpected timeout"); }));
+  advanceClocks(time::milliseconds(1));
+
+  BOOST_CHECK_NO_THROW(do {
+    face.receive(*makeData("/Hello/World/a"));
+    advanceClocks(time::milliseconds(1));
+  } while (false));
+}
+
 // test case for deprecated expressInterest implementation
 BOOST_AUTO_TEST_CASE(DeprecatedExpressInterestData)
 {
@@ -158,6 +172,19 @@
   BOOST_CHECK_EQUAL(face.sentNacks.size(), 0);
 }
 
+BOOST_AUTO_TEST_CASE(ExpressInterestEmptyTimeoutCallback)
+{
+  face.expressInterest(Interest("/Hello/World", time::milliseconds(50)),
+                       bind([] { BOOST_FAIL("Unexpected Data"); }),
+                       bind([] { BOOST_FAIL("Unexpected Nack"); }),
+                       nullptr);
+  advanceClocks(time::milliseconds(40));
+
+  BOOST_CHECK_NO_THROW(do {
+    advanceClocks(time::milliseconds(6), 2);
+  } while (false));
+}
+
 // test case for deprecated expressInterest implementation
 BOOST_AUTO_TEST_CASE(DeprecatedExpressInterestTimeout)
 {
@@ -204,6 +231,20 @@
   BOOST_CHECK_EQUAL(face.sentInterests.size(), 1);
 }
 
+BOOST_AUTO_TEST_CASE(ExpressInterestEmptyNackCallback)
+{
+  face.expressInterest(Interest("/Hello/World"),
+                       bind([] { BOOST_FAIL("Unexpected Data"); }),
+                       nullptr,
+                       bind([] { BOOST_FAIL("Unexpected timeout"); }));
+  advanceClocks(time::milliseconds(1));
+
+  BOOST_CHECK_NO_THROW(do {
+    face.receive(makeNack(face.sentInterests.at(0), lp::NackReason::DUPLICATE));
+    advanceClocks(time::milliseconds(1));
+  } while (false));
+}
+
 BOOST_AUTO_TEST_CASE(RemovePendingInterest)
 {
   const PendingInterestId* interestId =
@@ -316,6 +357,17 @@
   advanceClocks(time::milliseconds(10), 10);
 }
 
+BOOST_AUTO_TEST_CASE(SetInterestFilterEmptyInterestCallback)
+{
+  face.setInterestFilter("/A", nullptr);
+  advanceClocks(time::milliseconds(1));
+
+  BOOST_CHECK_NO_THROW(do {
+    face.receive(*makeInterest("/A/1"));
+    advanceClocks(time::milliseconds(1));
+  } while (false));
+}
+
 BOOST_AUTO_TEST_CASE(SetUnsetInterestFilterWithoutSucessCallback)
 {
   size_t nInterests = 0;