util: add signals to SegmentFetcher for segment retrieval/validation

refs #4438

Change-Id: I5a92e89c733cf49790807edefa1e6c39178de438
diff --git a/src/util/segment-fetcher.cpp b/src/util/segment-fetcher.cpp
index 0d54915..b23b991 100644
--- a/src/util/segment-fetcher.cpp
+++ b/src/util/segment-fetcher.cpp
@@ -46,7 +46,7 @@
 {
 }
 
-void
+shared_ptr<SegmentFetcher>
 SegmentFetcher::fetch(Face& face,
                       const Interest& baseInterest,
                       security::v2::Validator& validator,
@@ -54,10 +54,10 @@
                       const ErrorCallback& errorCallback)
 {
   shared_ptr<security::v2::Validator> validatorPtr(&validator, [] (security::v2::Validator*) {});
-  fetch(face, baseInterest, validatorPtr, completeCallback, errorCallback);
+  return fetch(face, baseInterest, validatorPtr, completeCallback, errorCallback);
 }
 
-void
+shared_ptr<SegmentFetcher>
 SegmentFetcher::fetch(Face& face,
                       const Interest& baseInterest,
                       shared_ptr<security::v2::Validator> validator,
@@ -68,6 +68,8 @@
                                                         errorCallback));
 
   fetcher->fetchFirstSegment(baseInterest, fetcher);
+
+  return fetcher;
 }
 
 void
@@ -79,8 +81,8 @@
   interest.setMustBeFresh(true);
 
   m_face.expressInterest(interest,
-                         bind(&SegmentFetcher::afterSegmentReceived, this, _1, _2, true, self),
-                         bind(&SegmentFetcher::afterNackReceived, this, _1, _2, 0, self),
+                         bind(&SegmentFetcher::afterSegmentReceivedCb, this, _1, _2, true, self),
+                         bind(&SegmentFetcher::afterNackReceivedCb, this, _1, _2, 0, self),
                          bind(m_errorCallback, INTEREST_TIMEOUT, "Timeout"));
 }
 
@@ -95,16 +97,17 @@
   interest.setMustBeFresh(false);
   interest.setName(dataName.getPrefix(-1).appendSegment(segmentNo));
   m_face.expressInterest(interest,
-                         bind(&SegmentFetcher::afterSegmentReceived, this, _1, _2, false, self),
-                         bind(&SegmentFetcher::afterNackReceived, this, _1, _2, 0, self),
+                         bind(&SegmentFetcher::afterSegmentReceivedCb, this, _1, _2, false, self),
+                         bind(&SegmentFetcher::afterNackReceivedCb, this, _1, _2, 0, self),
                          bind(m_errorCallback, INTEREST_TIMEOUT, "Timeout"));
 }
 
 void
-SegmentFetcher::afterSegmentReceived(const Interest& origInterest,
-                                     const Data& data, bool isSegmentZeroExpected,
-                                     shared_ptr<SegmentFetcher> self)
+SegmentFetcher::afterSegmentReceivedCb(const Interest& origInterest,
+                                       const Data& data, bool isSegmentZeroExpected,
+                                       shared_ptr<SegmentFetcher> self)
 {
+  afterSegmentReceived(data);
   m_validator->validate(data,
                         bind(&SegmentFetcher::afterValidationSuccess, this, _1,
                              isSegmentZeroExpected, origInterest, self),
@@ -127,7 +130,7 @@
     else {
       m_buffer->write(reinterpret_cast<const char*>(data.getContent().value()),
                       data.getContent().value_size());
-
+      afterSegmentValidated(data);
       const name::Component& finalBlockId = data.getMetaInfo().getFinalBlockId();
       if (finalBlockId.empty() || (finalBlockId > currentSegment)) {
         fetchNextSegment(origInterest, data.getName(), currentSegment.toSegment() + 1, self);
@@ -151,8 +154,8 @@
 
 
 void
-SegmentFetcher::afterNackReceived(const Interest& origInterest, const lp::Nack& nack,
-                                  uint32_t reExpressCount, shared_ptr<SegmentFetcher> self)
+SegmentFetcher::afterNackReceivedCb(const Interest& origInterest, const lp::Nack& nack,
+                                    uint32_t reExpressCount, shared_ptr<SegmentFetcher> self)
 {
   if (reExpressCount >= MAX_INTEREST_REEXPRESS) {
     m_errorCallback(NACK_ERROR, "Nack Error");
@@ -189,9 +192,9 @@
   }
 
   m_face.expressInterest(interest,
-                         bind(&SegmentFetcher::afterSegmentReceived, this, _1, _2,
+                         bind(&SegmentFetcher::afterSegmentReceivedCb, this, _1, _2,
                               isSegmentZeroExpected, self),
-                         bind(&SegmentFetcher::afterNackReceived, this, _1, _2,
+                         bind(&SegmentFetcher::afterNackReceivedCb, this, _1, _2,
                               ++reExpressCount, self),
                          bind(m_errorCallback, INTEREST_TIMEOUT, "Timeout"));
 }
diff --git a/src/util/segment-fetcher.hpp b/src/util/segment-fetcher.hpp
index 99510f5..cc9725a 100644
--- a/src/util/segment-fetcher.hpp
+++ b/src/util/segment-fetcher.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2013-2017 Regents of the University of California.
+ * Copyright (c) 2013-2018 Regents of the University of California.
  *
  * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
  *
@@ -22,10 +22,11 @@
 #ifndef NDN_UTIL_SEGMENT_FETCHER_HPP
 #define NDN_UTIL_SEGMENT_FETCHER_HPP
 
-#include "scheduler.hpp"
 #include "../common.hpp"
 #include "../face.hpp"
 #include "../security/v2/validator.hpp"
+#include "scheduler.hpp"
+#include "signal.hpp"
 
 namespace ndn {
 
@@ -120,7 +121,7 @@
   };
 
   /**
-   * @brief Initiate segment fetching
+   * @brief Initiates segment fetching
    *
    * @param face          Reference to the Face that should be used to fetch data
    * @param baseInterest  An Interest for the initial segment of requested data.
@@ -135,9 +136,10 @@
    *
    * @param completeCallback    Callback to be fired when all segments are fetched
    * @param errorCallback       Callback to be fired when an error occurs (@see Errors)
+   * @return A shared_ptr to the constructed SegmentFetcher
    */
   static
-  void
+  shared_ptr<SegmentFetcher>
   fetch(Face& face,
         const Interest& baseInterest,
         security::v2::Validator& validator,
@@ -158,9 +160,10 @@
    *
    * @param completeCallback    Callback to be fired when all segments are fetched
    * @param errorCallback       Callback to be fired when an error occurs (@see Errors)
+   * @return A shared_ptr to the constructed SegmentFetcher
    */
   static
-  void
+  shared_ptr<SegmentFetcher>
   fetch(Face& face,
         const Interest& baseInterest,
         shared_ptr<security::v2::Validator> validator,
@@ -181,9 +184,9 @@
                    shared_ptr<SegmentFetcher> self);
 
   void
-  afterSegmentReceived(const Interest& origInterest,
-                       const Data& data, bool isSegmentZeroExpected,
-                       shared_ptr<SegmentFetcher> self);
+  afterSegmentReceivedCb(const Interest& origInterest,
+                         const Data& data, bool isSegmentZeroExpected,
+                         shared_ptr<SegmentFetcher> self);
   void
   afterValidationSuccess(const Data& data,
                          bool isSegmentZeroExpected,
@@ -194,13 +197,24 @@
   afterValidationFailure(const Data& data, const security::v2::ValidationError& error);
 
   void
-  afterNackReceived(const Interest& origInterest, const lp::Nack& nack,
-                    uint32_t reExpressCount, shared_ptr<SegmentFetcher> self);
+  afterNackReceivedCb(const Interest& origInterest, const lp::Nack& nack,
+                      uint32_t reExpressCount, shared_ptr<SegmentFetcher> self);
 
   void
   reExpressInterest(Interest interest, uint32_t reExpressCount,
                     shared_ptr<SegmentFetcher> self);
 
+public:
+  /**
+   * @brief Emits whenever a data segment received
+   */
+  Signal<SegmentFetcher, Data> afterSegmentReceived;
+
+  /**
+   * @brief Emits whenever a received data segment has been successfully validated
+   */
+  Signal<SegmentFetcher, Data> afterSegmentValidated;
+
 private:
   Face& m_face;
   Scheduler m_scheduler;
diff --git a/tests/unit-tests/util/segment-fetcher.t.cpp b/tests/unit-tests/util/segment-fetcher.t.cpp
index dfc2cd8..0b512e8 100644
--- a/tests/unit-tests/util/segment-fetcher.t.cpp
+++ b/tests/unit-tests/util/segment-fetcher.t.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2013-2017 Regents of the University of California.
+ * Copyright (c) 2013-2018 Regents of the University of California.
  *
  * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
  *
@@ -421,6 +421,48 @@
   BOOST_REQUIRE_EQUAL(nData, 1);
 }
 
+BOOST_FIXTURE_TEST_CASE(Signals, Fixture)
+{
+  DummyValidator validator;
+  bool flipResult = false;
+  validator.getPolicy().setResultCallback([&flipResult] (const Name&) {
+      flipResult = !flipResult;
+      return flipResult;
+    });
+  shared_ptr<SegmentFetcher> fetcher =
+    SegmentFetcher::fetch(face, Interest("/hello/world", time::seconds(1000)),
+                          validator,
+                          bind(&Fixture::onComplete, this, _1),
+                          bind(&Fixture::onError, this, _1));
+
+  auto data1 = makeDataSegment("/hello/world", 0, false);
+  auto data2 = makeDataSegment("/hello/world", 1, true);
+
+  size_t nRecvSegments = 0;
+  fetcher->afterSegmentReceived.connect([&nRecvSegments] (const Data& receivedSegment) {
+      ++nRecvSegments;
+    });
+
+  size_t nValidatedSegments = 0;
+  fetcher->afterSegmentValidated.connect([&nValidatedSegments] (const Data& validatedSegment) {
+      ++nValidatedSegments;
+    });
+
+  advanceClocks(time::milliseconds(10), 10);
+
+  face.receive(*data1);
+
+  advanceClocks(time::milliseconds(10), 10);
+
+  face.receive(*data2);
+
+  advanceClocks(time::milliseconds(10), 10);
+
+  BOOST_CHECK_EQUAL(nRecvSegments, 2);
+  BOOST_CHECK_EQUAL(nValidatedSegments, 1);
+  BOOST_CHECK_EQUAL(nErrors, 1);
+}
+
 BOOST_AUTO_TEST_SUITE_END() // TestSegmentFetcher
 BOOST_AUTO_TEST_SUITE_END() // Util