util: augment SegmentFetcher with congestion control

refs #4364

Change-Id: Ibfebc1f173382d1faf88e0d3d707b8ab2dae5e43
diff --git a/tests/unit-tests/util/segment-fetcher.t.cpp b/tests/unit-tests/util/segment-fetcher.t.cpp
index d8f2bf5..c804f70 100644
--- a/tests/unit-tests/util/segment-fetcher.t.cpp
+++ b/tests/unit-tests/util/segment-fetcher.t.cpp
@@ -30,15 +30,14 @@
 #include "make-interest-data.hpp"
 #include "../identity-management-time-fixture.hpp"
 
+#include <set>
+
 namespace ndn {
 namespace util {
 namespace tests {
 
 using namespace ndn::tests;
 
-BOOST_AUTO_TEST_SUITE(Util)
-BOOST_AUTO_TEST_SUITE(TestSegmentFetcher)
-
 class Fixture : public IdentityManagementTimeFixture
 {
 public:
@@ -69,11 +68,11 @@
   }
 
   void
-  onComplete(const ConstBufferPtr& data)
+  onComplete(ConstBufferPtr data)
   {
     ++nCompletions;
     dataSize = data->size();
-    dataString = std::string(data->get<char>());
+    dataBuf = data;
   }
 
   void
@@ -92,22 +91,88 @@
     fetcher->onError.connect(bind(&Fixture::onError, this, _1));
   }
 
+  void
+  onInterest(const Interest& interest)
+  {
+    if (interest.getName().get(-1).isSegment()) {
+      if (segmentsToDropOrNack.size() > 0 &&
+          interest.getName().get(-1).toSegment() == segmentsToDropOrNack.front()) {
+        segmentsToDropOrNack.pop();
+        if (sendNackInsteadOfDropping) {
+          lp::Nack nack = makeNack(interest, nackReason);
+          face.receive(nack);
+        }
+        return;
+      }
+
+      auto data = makeDataSegment("/hello/world/version0",
+                                  interest.getName().get(-1).toSegment(),
+                                  interest.getName().get(-1).toSegment() == nSegments - 1);
+      face.receive(*data);
+
+      uniqSegmentsSent.insert(interest.getName().get(-1).toSegment());
+      if (uniqSegmentsSent.size() == nSegments) {
+        io.stop();
+      }
+    }
+    else {
+      if (segmentsToDropOrNack.size() > 0 &&
+          segmentsToDropOrNack.front() == 0) {
+        segmentsToDropOrNack.pop();
+        if (sendNackInsteadOfDropping) {
+          lp::Nack nack = makeNack(interest, nackReason);
+          face.receive(nack);
+        }
+        return;
+      }
+
+      auto data = makeDataSegment("/hello/world/version0", defaultSegmentToSend, nSegments == 1);
+      face.receive(*data);
+      uniqSegmentsSent.insert(defaultSegmentToSend);
+    }
+  }
+
 public:
   DummyClientFace face;
+  std::set<uint64_t> uniqSegmentsSent;
 
   int nErrors = 0;
   uint32_t lastError = 0;
   int nCompletions = 0;
   size_t dataSize = 0;
-  std::string dataString;
+  ConstBufferPtr dataBuf;
+
+  // number of segments in fetched object
+  uint64_t nSegments = 0;
+  std::queue<uint64_t> segmentsToDropOrNack;
+  bool sendNackInsteadOfDropping = false;
+  lp::NackReason nackReason = lp::NackReason::NONE;
+  // segment that is sent in response to an Interest w/o a segment component in its name
+  uint64_t defaultSegmentToSend = 0;
 };
 
-BOOST_FIXTURE_TEST_CASE(Timeout, Fixture)
+BOOST_AUTO_TEST_SUITE(Util)
+BOOST_FIXTURE_TEST_SUITE(TestSegmentFetcher, Fixture)
+
+BOOST_AUTO_TEST_CASE(InvalidOptions)
+{
+  SegmentFetcher::Options options;
+  options.mdCoef = 1.5;
+  DummyValidator acceptValidator;
+  BOOST_CHECK_THROW(SegmentFetcher::start(face, Interest("/hello/world"), acceptValidator, options),
+                    std::invalid_argument);
+}
+
+BOOST_AUTO_TEST_CASE(ExceedMaxTimeout)
 {
   DummyValidator acceptValidator;
-  shared_ptr<SegmentFetcher> fetcher = SegmentFetcher::start(face, Interest("/hello/world", 100_ms),
-                                                             acceptValidator);
+  size_t nAfterSegmentTimedOut = 0;
+  SegmentFetcher::Options options;
+  options.maxTimeout = 100_ms;
+  shared_ptr<SegmentFetcher> fetcher = SegmentFetcher::start(face, Interest("/hello/world"),
+                                                             acceptValidator, options);
   connectSignals(fetcher);
+  fetcher->afterSegmentTimedOut.connect(bind([&nAfterSegmentTimedOut] { ++nAfterSegmentTimedOut; }));
 
   advanceClocks(1_ms);
   BOOST_REQUIRE_EQUAL(face.sentInterests.size(), 1);
@@ -121,46 +186,338 @@
   BOOST_CHECK_EQUAL(nErrors, 0);
   BOOST_CHECK_EQUAL(nCompletions, 0);
   BOOST_CHECK_EQUAL(face.sentData.size(), 0);
+  BOOST_CHECK_EQUAL(nAfterSegmentTimedOut, 0);
 
   advanceClocks(1_ms, 2);
   BOOST_CHECK_EQUAL(nErrors, 1);
   BOOST_CHECK_EQUAL(lastError, static_cast<uint32_t>(SegmentFetcher::INTEREST_TIMEOUT));
   BOOST_REQUIRE_EQUAL(face.sentInterests.size(), 1);
   BOOST_CHECK_EQUAL(face.sentData.size(), 0);
+  BOOST_CHECK_EQUAL(nAfterSegmentTimedOut, 1);
 }
 
-BOOST_FIXTURE_TEST_CASE(Basic, Fixture)
+BOOST_AUTO_TEST_CASE(BasicSingleSegment)
 {
   DummyValidator acceptValidator;
   size_t nAfterSegmentReceived = 0;
   size_t nAfterSegmentValidated = 0;
-  shared_ptr<SegmentFetcher> fetcher = SegmentFetcher::start(face, Interest("/hello/world", 1000_s),
+  size_t nAfterSegmentNacked = 0;
+  size_t nAfterSegmentTimedOut = 0;
+  shared_ptr<SegmentFetcher> fetcher = SegmentFetcher::start(face, Interest("/hello/world"),
                                                              acceptValidator);
   connectSignals(fetcher);
   fetcher->afterSegmentReceived.connect(bind([&nAfterSegmentReceived] { ++nAfterSegmentReceived; }));
   fetcher->afterSegmentValidated.connect(bind([&nAfterSegmentValidated] { ++nAfterSegmentValidated; }));
+  fetcher->afterSegmentNacked.connect(bind([&nAfterSegmentNacked] { ++nAfterSegmentNacked; }));
+  fetcher->afterSegmentTimedOut.connect(bind([&nAfterSegmentTimedOut] { ++nAfterSegmentTimedOut; }));
 
   advanceClocks(10_ms);
 
-  for (uint64_t i = 0; i < 400; i++) {
-    advanceClocks(10_ms);
-    face.receive(*makeDataSegment("/hello/world/version0", i, false));
-  }
-  advanceClocks(10_ms);
-  face.receive(*makeDataSegment("/hello/world/version0", 400, true));
+  face.receive(*makeDataSegment("/hello/world/version0", 0, true));
 
   advanceClocks(10_ms);
 
   BOOST_CHECK_EQUAL(nErrors, 0);
   BOOST_CHECK_EQUAL(nCompletions, 1);
-  BOOST_CHECK_EQUAL(nAfterSegmentReceived, 401);
-  BOOST_CHECK_EQUAL(nAfterSegmentValidated, 401);
+  BOOST_CHECK_EQUAL(nAfterSegmentReceived, 1);
+  BOOST_CHECK_EQUAL(nAfterSegmentValidated, 1);
+  BOOST_CHECK_EQUAL(nAfterSegmentNacked, 0);
+  BOOST_CHECK_EQUAL(nAfterSegmentTimedOut, 0);
 }
 
-BOOST_FIXTURE_TEST_CASE(MissingSegmentNum, Fixture)
+BOOST_AUTO_TEST_CASE(ConstantCwnd)
+{
+  SegmentFetcher::Options options;
+  options.useConstantCwnd = true;
+  DummyValidator acceptValidator;
+  size_t nAfterSegmentReceived = 0;
+  size_t nAfterSegmentValidated = 0;
+  size_t nAfterSegmentNacked = 0;
+  size_t nAfterSegmentTimedOut = 0;
+  shared_ptr<SegmentFetcher> fetcher = SegmentFetcher::start(face, Interest("/hello/world"),
+                                                             acceptValidator, options);
+  connectSignals(fetcher);
+  fetcher->afterSegmentReceived.connect(bind([&nAfterSegmentReceived] { ++nAfterSegmentReceived; }));
+  fetcher->afterSegmentValidated.connect(bind([&nAfterSegmentValidated] { ++nAfterSegmentValidated; }));
+  fetcher->afterSegmentNacked.connect(bind([&nAfterSegmentNacked] { ++nAfterSegmentNacked; }));
+  fetcher->afterSegmentTimedOut.connect(bind([&nAfterSegmentTimedOut] { ++nAfterSegmentTimedOut; }));
+
+  advanceClocks(10_ms);
+
+  BOOST_CHECK_EQUAL(fetcher->m_cwnd, 1.0);
+  BOOST_CHECK_EQUAL(fetcher->m_nSegmentsInFlight, 1);
+
+  face.receive(*makeDataSegment("/hello/world/version0", 0, false));
+
+  advanceClocks(10_ms);
+
+  BOOST_CHECK_EQUAL(fetcher->m_cwnd, 1.0);
+  BOOST_CHECK_EQUAL(fetcher->m_nSegmentsInFlight, 1);
+  BOOST_REQUIRE_EQUAL(face.sentInterests.size(), 2);
+  BOOST_CHECK_EQUAL(face.sentInterests.back().getName().get(-1).toSegment(), 1);
+  face.receive(*makeDataSegment("/hello/world/version0", 1, false));
+
+  advanceClocks(10_ms);
+
+  BOOST_CHECK_EQUAL(fetcher->m_cwnd, 1.0);
+  BOOST_CHECK_EQUAL(fetcher->m_nSegmentsInFlight, 1);
+  BOOST_CHECK_EQUAL(face.sentInterests.size(), 3);
+  BOOST_CHECK_EQUAL(face.sentInterests.back().getName().get(-1).toSegment(), 2);
+  face.receive(*makeDataSegment("/hello/world/version0", 2, false));
+
+  advanceClocks(10_ms);
+
+  BOOST_CHECK_EQUAL(fetcher->m_cwnd, 1.0);
+  BOOST_CHECK_EQUAL(fetcher->m_nSegmentsInFlight, 1);
+  BOOST_CHECK_EQUAL(face.sentInterests.size(), 4);
+  BOOST_CHECK_EQUAL(face.sentInterests.back().getName().get(-1).toSegment(), 3);
+  face.receive(*makeDataSegment("/hello/world/version0", 3, false));
+
+  advanceClocks(10_ms);
+
+  BOOST_CHECK_EQUAL(fetcher->m_cwnd, 1.0);
+  BOOST_CHECK_EQUAL(fetcher->m_nSegmentsInFlight, 1);
+  BOOST_CHECK_EQUAL(face.sentInterests.size(), 5);
+  BOOST_CHECK_EQUAL(face.sentInterests.back().getName().get(-1).toSegment(), 4);
+  nackLastInterest(lp::NackReason::CONGESTION);
+
+  advanceClocks(10_ms);
+
+  BOOST_CHECK_EQUAL(fetcher->m_cwnd, 1.0);
+  BOOST_CHECK_EQUAL(fetcher->m_nSegmentsInFlight, 1);
+  BOOST_CHECK_EQUAL(face.sentInterests.size(), 6);
+  BOOST_CHECK_EQUAL(face.sentInterests.back().getName().get(-1).toSegment(), 4);
+  face.receive(*makeDataSegment("/hello/world/version0", 4, true));
+
+  advanceClocks(10_ms);
+
+  BOOST_CHECK_EQUAL(nErrors, 0);
+  BOOST_CHECK_EQUAL(nCompletions, 1);
+  BOOST_CHECK_EQUAL(nAfterSegmentReceived, 5);
+  BOOST_CHECK_EQUAL(nAfterSegmentValidated, 5);
+  BOOST_CHECK_EQUAL(nAfterSegmentNacked, 1);
+  BOOST_CHECK_EQUAL(nAfterSegmentTimedOut, 0);
+  BOOST_CHECK_EQUAL(fetcher->m_cwnd, 1.0);
+}
+
+BOOST_AUTO_TEST_CASE(BasicMultipleSegments)
 {
   DummyValidator acceptValidator;
-  shared_ptr<SegmentFetcher> fetcher = SegmentFetcher::start(face, Interest("/hello/world", 1000_s),
+  size_t nAfterSegmentReceived = 0;
+  size_t nAfterSegmentValidated = 0;
+  size_t nAfterSegmentNacked = 0;
+  size_t nAfterSegmentTimedOut = 0;
+  nSegments = 401;
+  sendNackInsteadOfDropping = false;
+  face.onSendInterest.connect(bind(&Fixture::onInterest, this, _1));
+
+  shared_ptr<SegmentFetcher> fetcher = SegmentFetcher::start(face, Interest("/hello/world"),
+                                                             acceptValidator);
+  connectSignals(fetcher);
+  fetcher->afterSegmentReceived.connect(bind([&nAfterSegmentReceived] { ++nAfterSegmentReceived; }));
+  fetcher->afterSegmentValidated.connect(bind([&nAfterSegmentValidated] { ++nAfterSegmentValidated; }));
+  fetcher->afterSegmentNacked.connect(bind([&nAfterSegmentNacked] { ++nAfterSegmentNacked; }));
+  fetcher->afterSegmentTimedOut.connect(bind([&nAfterSegmentTimedOut] { ++nAfterSegmentTimedOut; }));
+
+  face.processEvents(1_s);
+
+  BOOST_CHECK_EQUAL(nErrors, 0);
+  BOOST_CHECK_EQUAL(nCompletions, 1);
+  BOOST_CHECK_EQUAL(dataSize, 14 * 401);
+  BOOST_CHECK_EQUAL(nAfterSegmentReceived, 401);
+  BOOST_CHECK_EQUAL(nAfterSegmentValidated, 401);
+  BOOST_CHECK_EQUAL(nAfterSegmentNacked, 0);
+  BOOST_CHECK_EQUAL(nAfterSegmentTimedOut, 0);
+}
+
+BOOST_AUTO_TEST_CASE(FirstSegmentNotZero)
+{
+  DummyValidator acceptValidator;
+  size_t nAfterSegmentReceived = 0;
+  size_t nAfterSegmentValidated = 0;
+  size_t nAfterSegmentNacked = 0;
+  size_t nAfterSegmentTimedOut = 0;
+  nSegments = 401;
+  sendNackInsteadOfDropping = false;
+  defaultSegmentToSend = 47;
+  face.onSendInterest.connect(bind(&Fixture::onInterest, this, _1));
+
+  shared_ptr<SegmentFetcher> fetcher = SegmentFetcher::start(face, Interest("/hello/world"),
+                                                             acceptValidator);
+  connectSignals(fetcher);
+  fetcher->afterSegmentReceived.connect(bind([&nAfterSegmentReceived] { ++nAfterSegmentReceived; }));
+  fetcher->afterSegmentValidated.connect(bind([&nAfterSegmentValidated] { ++nAfterSegmentValidated; }));
+  fetcher->afterSegmentNacked.connect(bind([&nAfterSegmentNacked] { ++nAfterSegmentNacked; }));
+  fetcher->afterSegmentTimedOut.connect(bind([&nAfterSegmentTimedOut] { ++nAfterSegmentTimedOut; }));
+
+  face.processEvents(1_s);
+
+  BOOST_CHECK_EQUAL(nErrors, 0);
+  BOOST_CHECK_EQUAL(nCompletions, 1);
+  BOOST_CHECK_EQUAL(dataSize, 14 * 401);
+  BOOST_CHECK_EQUAL(nAfterSegmentReceived, 401);
+  BOOST_CHECK_EQUAL(nAfterSegmentValidated, 401);
+  BOOST_CHECK_EQUAL(nAfterSegmentNacked, 0);
+  BOOST_CHECK_EQUAL(nAfterSegmentTimedOut, 0);
+}
+
+BOOST_AUTO_TEST_CASE(WindowSize)
+{
+  DummyValidator acceptValidator;
+  size_t nAfterSegmentReceived = 0;
+  size_t nAfterSegmentValidated = 0;
+  shared_ptr<SegmentFetcher> fetcher = SegmentFetcher::start(face, Interest("/hello/world"),
+                                                             acceptValidator);
+  connectSignals(fetcher);
+  fetcher->afterSegmentReceived.connect(bind([&nAfterSegmentReceived] { ++nAfterSegmentReceived; }));
+  fetcher->afterSegmentValidated.connect(bind([&nAfterSegmentValidated] { ++nAfterSegmentValidated; }));
+
+  advanceClocks(10_ms); // T+10ms
+
+  BOOST_CHECK_EQUAL(fetcher->m_timeLastSegmentReceived, time::steady_clock::now() - 10_ms);
+  BOOST_CHECK_EQUAL(fetcher->m_retxQueue.size(), 0);
+  BOOST_CHECK_EQUAL(fetcher->m_nextSegmentNum, 0);
+  BOOST_CHECK_EQUAL(fetcher->m_cwnd, 1.0);
+  BOOST_CHECK_EQUAL(fetcher->m_ssthresh, std::numeric_limits<double>::max());
+  BOOST_CHECK_EQUAL(fetcher->m_nSegmentsInFlight, 1);
+  BOOST_CHECK_EQUAL(fetcher->m_nSegments, 0);
+  BOOST_CHECK_EQUAL(fetcher->m_nBytesReceived, 0);
+  BOOST_CHECK_EQUAL(fetcher->m_highInterest, 0);
+  BOOST_CHECK_EQUAL(fetcher->m_highData, 0);
+  BOOST_CHECK_EQUAL(fetcher->m_recPoint, 0);
+  BOOST_CHECK_EQUAL(fetcher->m_nReceived, 0);
+  BOOST_CHECK_EQUAL(fetcher->m_receivedSegments.size(), 0);
+  BOOST_CHECK_EQUAL(fetcher->m_pendingSegments.size(), 1);
+  BOOST_CHECK_EQUAL(face.sentInterests.size(), 1);
+
+  double oldCwnd = fetcher->m_cwnd;
+  double oldSsthresh = fetcher->m_ssthresh;
+  uint64_t oldNextSegmentNum = fetcher->m_nextSegmentNum;
+
+  face.receive(*makeDataSegment("/hello/world/version0", 0, false));
+
+  advanceClocks(10_ms); //T+20ms
+
+  BOOST_CHECK_EQUAL(fetcher->m_timeLastSegmentReceived, time::steady_clock::now() - 10_ms);
+  BOOST_CHECK_EQUAL(fetcher->m_retxQueue.size(), 0);
+  BOOST_CHECK_EQUAL(fetcher->m_versionedDataName, "/hello/world/version0");
+  // +2 below because m_nextSegmentNum will be incremented in the receive callback if segment 0 is
+  // the first received
+  BOOST_CHECK_EQUAL(fetcher->m_nextSegmentNum, oldNextSegmentNum + fetcher->m_options.aiStep + 2);
+  BOOST_CHECK_EQUAL(fetcher->m_cwnd, oldCwnd + fetcher->m_options.aiStep);
+  BOOST_CHECK_EQUAL(fetcher->m_ssthresh, oldSsthresh);
+  BOOST_CHECK_EQUAL(fetcher->m_nSegmentsInFlight, oldCwnd + fetcher->m_options.aiStep);
+  BOOST_CHECK_EQUAL(fetcher->m_nSegments, 0);
+  BOOST_CHECK_EQUAL(fetcher->m_nBytesReceived, 14);
+  BOOST_CHECK_EQUAL(fetcher->m_highInterest, fetcher->m_nextSegmentNum - 1);
+  BOOST_CHECK_EQUAL(fetcher->m_highData, 0);
+  BOOST_CHECK_EQUAL(fetcher->m_recPoint, 0);
+  BOOST_CHECK_EQUAL(fetcher->m_receivedSegments.size(), 1);
+  BOOST_CHECK_EQUAL(fetcher->m_pendingSegments.size(), fetcher->m_cwnd);
+  BOOST_CHECK_EQUAL(face.sentInterests.size(), 1 + fetcher->m_cwnd);
+
+  oldCwnd = fetcher->m_cwnd;
+  oldNextSegmentNum = fetcher->m_nextSegmentNum;
+
+  face.receive(*makeDataSegment("/hello/world/version0", 2, false));
+
+  advanceClocks(10_ms); //T+30ms
+
+  BOOST_CHECK_EQUAL(fetcher->m_timeLastSegmentReceived, time::steady_clock::now() - 10_ms);
+  BOOST_CHECK_EQUAL(fetcher->m_retxQueue.size(), 0);
+  BOOST_CHECK_EQUAL(fetcher->m_versionedDataName, "/hello/world/version0");
+  BOOST_CHECK_EQUAL(fetcher->m_nextSegmentNum, oldNextSegmentNum + fetcher->m_options.aiStep + 1);
+  BOOST_CHECK_EQUAL(fetcher->m_cwnd, oldCwnd + fetcher->m_options.aiStep);
+  BOOST_CHECK_EQUAL(fetcher->m_ssthresh, oldSsthresh);
+  BOOST_CHECK_EQUAL(fetcher->m_nSegmentsInFlight, fetcher->m_cwnd);
+  BOOST_CHECK_EQUAL(fetcher->m_nSegments, 0);
+  BOOST_CHECK_EQUAL(fetcher->m_nBytesReceived, 28);
+  BOOST_CHECK_EQUAL(fetcher->m_highInterest, fetcher->m_nextSegmentNum - 1);
+  BOOST_CHECK_EQUAL(fetcher->m_highData, 2);
+  BOOST_CHECK_EQUAL(fetcher->m_recPoint, 0);
+  BOOST_CHECK_EQUAL(fetcher->m_receivedSegments.size(), 2);
+  BOOST_CHECK_EQUAL(fetcher->m_pendingSegments.size(), fetcher->m_cwnd);
+  BOOST_CHECK_EQUAL(face.sentInterests.size(), 2 + fetcher->m_cwnd);
+
+  oldCwnd = fetcher->m_cwnd;
+  oldNextSegmentNum = fetcher->m_nextSegmentNum;
+
+  face.receive(*makeDataSegment("/hello/world/version0", 1, false));
+
+  advanceClocks(10_ms); //T+40ms
+
+  BOOST_CHECK_EQUAL(fetcher->m_timeLastSegmentReceived, time::steady_clock::now() - 10_ms);
+  BOOST_CHECK_EQUAL(fetcher->m_retxQueue.size(), 0);
+  BOOST_CHECK_EQUAL(fetcher->m_versionedDataName, "/hello/world/version0");
+  BOOST_CHECK_EQUAL(fetcher->m_nextSegmentNum, oldNextSegmentNum + fetcher->m_options.aiStep + 1);
+  BOOST_CHECK_EQUAL(fetcher->m_cwnd, oldCwnd + fetcher->m_options.aiStep);
+  BOOST_CHECK_EQUAL(fetcher->m_ssthresh, oldSsthresh);
+  BOOST_CHECK_EQUAL(fetcher->m_nSegmentsInFlight, fetcher->m_cwnd);
+  BOOST_CHECK_EQUAL(fetcher->m_nSegments, 0);
+  BOOST_CHECK_EQUAL(fetcher->m_nBytesReceived, 42);
+  BOOST_CHECK_EQUAL(fetcher->m_highInterest, fetcher->m_nextSegmentNum - 1);
+  BOOST_CHECK_EQUAL(fetcher->m_highData, 2);
+  BOOST_CHECK_EQUAL(fetcher->m_recPoint, 0);
+  BOOST_CHECK_EQUAL(fetcher->m_receivedSegments.size(), 3);
+  BOOST_CHECK_EQUAL(fetcher->m_pendingSegments.size(), fetcher->m_cwnd);
+  BOOST_CHECK_EQUAL(face.sentInterests.size(), 3 + fetcher->m_cwnd);
+
+  oldCwnd = fetcher->m_cwnd;
+  oldSsthresh = fetcher->m_ssthresh;
+  oldNextSegmentNum = fetcher->m_nextSegmentNum;
+  size_t oldSentInterestsSize = face.sentInterests.size();
+
+  nackLastInterest(lp::NackReason::CONGESTION); //T+50ms
+
+  BOOST_CHECK_EQUAL(fetcher->m_timeLastSegmentReceived, time::steady_clock::now() - 20_ms);
+  BOOST_CHECK_EQUAL(fetcher->m_retxQueue.size(), 1);
+  BOOST_CHECK_EQUAL(fetcher->m_versionedDataName, "/hello/world/version0");
+  BOOST_CHECK_EQUAL(fetcher->m_nextSegmentNum, oldNextSegmentNum);
+  BOOST_CHECK_EQUAL(fetcher->m_cwnd, oldCwnd / 2.0);
+  BOOST_CHECK_EQUAL(fetcher->m_ssthresh, oldCwnd / 2.0);
+  BOOST_CHECK_EQUAL(fetcher->m_nSegmentsInFlight, oldCwnd - 1);
+  BOOST_CHECK_EQUAL(fetcher->m_nSegments, 0);
+  BOOST_CHECK_EQUAL(fetcher->m_nBytesReceived, 42);
+  BOOST_CHECK_EQUAL(fetcher->m_highInterest, fetcher->m_nextSegmentNum - 1);
+  BOOST_CHECK_EQUAL(fetcher->m_highData, 2);
+  BOOST_CHECK_EQUAL(fetcher->m_recPoint, fetcher->m_nextSegmentNum - 1);
+  BOOST_CHECK_EQUAL(fetcher->m_receivedSegments.size(), 3);
+  // The Nacked segment will remain in pendingSegments, so the size of the structure doesn't change
+  BOOST_CHECK_EQUAL(fetcher->m_pendingSegments.size(), oldCwnd);
+  BOOST_CHECK_EQUAL(face.sentInterests.size(), oldSentInterestsSize);
+
+  advanceClocks(10_ms); //T+60ms
+
+  BOOST_CHECK_EQUAL(fetcher->m_timeLastSegmentReceived, time::steady_clock::now() - 30_ms);
+  BOOST_CHECK_EQUAL(fetcher->m_retxQueue.size(), 1);
+  BOOST_CHECK_EQUAL(fetcher->m_versionedDataName, "/hello/world/version0");
+  BOOST_CHECK_EQUAL(fetcher->m_nextSegmentNum, oldNextSegmentNum);
+  BOOST_CHECK_EQUAL(fetcher->m_cwnd, oldCwnd / 2.0);
+  BOOST_CHECK_EQUAL(fetcher->m_ssthresh, oldCwnd / 2.0);
+  BOOST_CHECK_EQUAL(fetcher->m_nSegmentsInFlight, oldCwnd - 1);
+  BOOST_CHECK_EQUAL(fetcher->m_nSegments, 0);
+  BOOST_CHECK_EQUAL(fetcher->m_nBytesReceived, 42);
+  BOOST_CHECK_EQUAL(fetcher->m_highInterest, fetcher->m_nextSegmentNum - 1);
+  BOOST_CHECK_EQUAL(fetcher->m_highData, 2);
+  BOOST_CHECK_EQUAL(fetcher->m_recPoint, fetcher->m_nextSegmentNum - 1);
+  BOOST_CHECK_EQUAL(fetcher->m_receivedSegments.size(), 3);
+  BOOST_CHECK_EQUAL(fetcher->m_pendingSegments.size(), oldCwnd);
+  BOOST_CHECK_EQUAL(face.sentInterests.size(), oldSentInterestsSize);
+
+  // Properly end test case
+  lp::Nack nack = makeNack(face.sentInterests[face.sentInterests.size() - 2], lp::NackReason::NO_ROUTE);
+  face.receive(nack);
+  advanceClocks(10_ms); //T+70ms
+
+  BOOST_CHECK_EQUAL(nErrors, 1);
+  BOOST_CHECK_EQUAL(lastError, static_cast<uint32_t>(SegmentFetcher::NACK_ERROR));
+  BOOST_CHECK_EQUAL(nCompletions, 0);
+}
+
+BOOST_AUTO_TEST_CASE(MissingSegmentNum)
+{
+  DummyValidator acceptValidator;
+  shared_ptr<SegmentFetcher> fetcher = SegmentFetcher::start(face, Interest("/hello/world"),
                                                              acceptValidator);
   connectSignals(fetcher);
 
@@ -178,107 +535,165 @@
   BOOST_CHECK_EQUAL(nCompletions, 0);
 }
 
-BOOST_FIXTURE_TEST_CASE(DuplicateNack, Fixture)
+BOOST_AUTO_TEST_CASE(MoreSegmentsThanNSegments)
 {
   DummyValidator acceptValidator;
-  shared_ptr<SegmentFetcher> fetcher = SegmentFetcher::start(face, Interest("/hello/world", 1000_s),
+  size_t nAfterSegmentReceived = 0;
+  size_t nAfterSegmentValidated = 0;
+  size_t nAfterSegmentNacked = 0;
+  size_t nAfterSegmentTimedOut = 0;
+  shared_ptr<SegmentFetcher> fetcher = SegmentFetcher::start(face, Interest("/hello/world"),
                                                              acceptValidator);
   connectSignals(fetcher);
+  fetcher->afterSegmentReceived.connect(bind([&nAfterSegmentReceived] { ++nAfterSegmentReceived; }));
+  fetcher->afterSegmentValidated.connect(bind([&nAfterSegmentValidated] { ++nAfterSegmentValidated; }));
+  fetcher->afterSegmentNacked.connect(bind([&nAfterSegmentNacked] { ++nAfterSegmentNacked; }));
+  fetcher->afterSegmentTimedOut.connect(bind([&nAfterSegmentTimedOut] { ++nAfterSegmentTimedOut; }));
+
   advanceClocks(10_ms);
 
-  // receive nack for the original interest
-  nackLastInterest(lp::NackReason::DUPLICATE);
-
-  // receive nack due to Duplication for the reexpressed interests
-  for (uint32_t i = 1; i <= SegmentFetcher::MAX_INTEREST_REEXPRESS; ++i) {
-    nackLastInterest(lp::NackReason::DUPLICATE);
-  }
-
-  BOOST_CHECK_EQUAL(face.sentInterests.size(), (SegmentFetcher::MAX_INTEREST_REEXPRESS + 1));
-  BOOST_CHECK_EQUAL(lastError, static_cast<uint32_t>(SegmentFetcher::NACK_ERROR));
-}
-
-BOOST_FIXTURE_TEST_CASE(CongestionNack, Fixture)
-{
-  DummyValidator acceptValidator;
-  shared_ptr<SegmentFetcher> fetcher = SegmentFetcher::start(face, Interest("/hello/world", 1000_s),
-                                                             acceptValidator);
-  connectSignals(fetcher);
+  face.receive(*makeDataSegment("/hello/world/version0", 0, false));
   advanceClocks(10_ms);
 
-  // receive nack for the original interest
-  nackLastInterest(lp::NackReason::CONGESTION);
+  BOOST_CHECK_EQUAL(nErrors, 0);
+  BOOST_CHECK_EQUAL(nCompletions, 0);
 
-  // receive nack due to Congestion for the reexpressed interests
-  for (uint32_t i = 1; i <= SegmentFetcher::MAX_INTEREST_REEXPRESS; ++i) {
-    nackLastInterest(lp::NackReason::CONGESTION);
-  }
-
-  BOOST_CHECK_EQUAL(face.sentInterests.size(), (SegmentFetcher::MAX_INTEREST_REEXPRESS + 1));
-  BOOST_CHECK_EQUAL(lastError, static_cast<uint32_t>(SegmentFetcher::NACK_ERROR));
-}
-
-BOOST_FIXTURE_TEST_CASE(SegmentZero, Fixture)
-{
-  int nNacks = 2;
-
-  ndn::Name interestName("ndn:/A");
-  DummyValidator acceptValidator;
-  shared_ptr<SegmentFetcher> fetcher = SegmentFetcher::start(face, Interest(interestName),
-                                                             acceptValidator);
-  connectSignals(fetcher);
-
-  advanceClocks(1000_ms);
-
-  for (uint64_t segmentNo = 0; segmentNo <= 3; segmentNo++) {
-    if (segmentNo == 1) {
-      while (nNacks--) {
-        nackLastInterest(lp::NackReason::CONGESTION);
-        advanceClocks(10_ms);
-      }
-    }
-
-    auto data = makeDataSegment(interestName, segmentNo, segmentNo == 3);
-    face.receive(*data);
-    advanceClocks(10_ms);
-  }
-
-  // Total number of sent interests should be 6: one interest for segment zero and segment one each,
-  // two re-expressed interests for segment one after getting nack twice, and two interests for
-  // segment two and three
-  BOOST_REQUIRE_EQUAL(face.sentInterests.size(), 6);
-
-  BOOST_CHECK_EQUAL(face.sentInterests[0].getName(), ndn::Name("ndn:/A"));
-  BOOST_CHECK_EQUAL(face.sentInterests[1].getName(), ndn::Name("ndn:/A/%00%01"));
-  BOOST_CHECK_EQUAL(face.sentInterests[2].getName(), ndn::Name("ndn:/A/%00%01"));
-  BOOST_CHECK_EQUAL(face.sentInterests[3].getName(), ndn::Name("ndn:/A/%00%01"));
-  BOOST_CHECK_EQUAL(face.sentInterests[4].getName(), ndn::Name("ndn:/A/%00%02"));
-  BOOST_CHECK_EQUAL(face.sentInterests[5].getName(), ndn::Name("ndn:/A/%00%03"));
-}
-
-BOOST_FIXTURE_TEST_CASE(SingleSegment, Fixture)
-{
-  DummyValidator acceptValidator;
-  shared_ptr<SegmentFetcher> fetcher = SegmentFetcher::start(face, Interest("ndn:/", 1000_s),
-                                                             acceptValidator);
-  connectSignals(fetcher);
+  face.receive(*makeDataSegment("/hello/world/version0", 1, false));
   advanceClocks(10_ms);
-  nackLastInterest(lp::NackReason::DUPLICATE);
-  face.receive(*makeDataSegment("/hello/world", 0, true));
 
-  BOOST_REQUIRE_EQUAL(face.sentInterests.size(), 2);
-  BOOST_CHECK_EQUAL(face.sentInterests[0].getName(), ndn::Name("ndn:/"));
-  BOOST_CHECK_EQUAL(face.sentInterests[1].getName(), ndn::Name("ndn:/"));
-  BOOST_REQUIRE_EQUAL(nCompletions, 1);
+  BOOST_CHECK_EQUAL(nErrors, 0);
+  BOOST_CHECK_EQUAL(nCompletions, 0);
+
+  face.receive(*makeDataSegment("/hello/world/version0", 2, false));
+  advanceClocks(10_ms);
+
+  BOOST_CHECK_EQUAL(nErrors, 0);
+  BOOST_CHECK_EQUAL(nCompletions, 0);
+
+  face.receive(*makeDataSegment("/hello/world/version0", 3, false));
+  advanceClocks(10_ms);
+
+  BOOST_CHECK_EQUAL(nErrors, 0);
+  BOOST_CHECK_EQUAL(nCompletions, 0);
+
+  auto data4 = makeDataSegment("/hello/world/version0", 4, false);
+  data4->setFinalBlock(name::Component::fromSegment(2));
+  face.receive(*data4);
+  advanceClocks(10_ms);
+
+  BOOST_CHECK_EQUAL(nErrors, 0);
+  BOOST_CHECK_EQUAL(nCompletions, 1);
+  BOOST_CHECK_EQUAL(dataSize, 14 * 3);
+  BOOST_CHECK_EQUAL(nAfterSegmentReceived, 5);
+  BOOST_CHECK_EQUAL(nAfterSegmentValidated, 5);
+  BOOST_CHECK_EQUAL(nAfterSegmentNacked, 0);
+  BOOST_CHECK_EQUAL(nAfterSegmentTimedOut, 0);
 }
 
-BOOST_FIXTURE_TEST_CASE(ValidationFailure, Fixture)
+BOOST_AUTO_TEST_CASE(DuplicateNack)
+{
+  DummyValidator acceptValidator;
+  size_t nAfterSegmentReceived = 0;
+  size_t nAfterSegmentValidated = 0;
+  size_t nAfterSegmentNacked = 0;
+  size_t nAfterSegmentTimedOut = 0;
+  nSegments = 401;
+  segmentsToDropOrNack.push(0);
+  segmentsToDropOrNack.push(200);
+  sendNackInsteadOfDropping = true;
+  nackReason = lp::NackReason::DUPLICATE;
+  face.onSendInterest.connect(bind(&Fixture::onInterest, this, _1));
+
+  shared_ptr<SegmentFetcher> fetcher = SegmentFetcher::start(face, Interest("/hello/world"),
+                                                             acceptValidator);
+  connectSignals(fetcher);
+  fetcher->afterSegmentReceived.connect(bind([&nAfterSegmentReceived] { ++nAfterSegmentReceived; }));
+  fetcher->afterSegmentValidated.connect(bind([&nAfterSegmentValidated] { ++nAfterSegmentValidated; }));
+  fetcher->afterSegmentNacked.connect(bind([&nAfterSegmentNacked] { ++nAfterSegmentNacked; }));
+  fetcher->afterSegmentTimedOut.connect(bind([&nAfterSegmentTimedOut] { ++nAfterSegmentTimedOut; }));
+
+  face.processEvents(1_s);
+
+  BOOST_CHECK_EQUAL(nErrors, 0);
+  BOOST_CHECK_EQUAL(nCompletions, 1);
+  BOOST_CHECK_EQUAL(dataSize, 14 * 401);
+  BOOST_CHECK_EQUAL(nAfterSegmentReceived, 401);
+  BOOST_CHECK_EQUAL(nAfterSegmentValidated, 401);
+  BOOST_CHECK_EQUAL(nAfterSegmentNacked, 2);
+  BOOST_CHECK_EQUAL(nAfterSegmentTimedOut, 0);
+}
+
+BOOST_AUTO_TEST_CASE(CongestionNack)
+{
+  DummyValidator acceptValidator;
+  size_t nAfterSegmentReceived = 0;
+  size_t nAfterSegmentValidated = 0;
+  size_t nAfterSegmentNacked = 0;
+  size_t nAfterSegmentTimedOut = 0;
+  nSegments = 401;
+  segmentsToDropOrNack.push(0);
+  segmentsToDropOrNack.push(200);
+  sendNackInsteadOfDropping = true;
+  nackReason = lp::NackReason::CONGESTION;
+  face.onSendInterest.connect(bind(&Fixture::onInterest, this, _1));
+
+  shared_ptr<SegmentFetcher> fetcher = SegmentFetcher::start(face, Interest("/hello/world"),
+                                                             acceptValidator);
+  connectSignals(fetcher);
+  fetcher->afterSegmentReceived.connect(bind([&nAfterSegmentReceived] { ++nAfterSegmentReceived; }));
+  fetcher->afterSegmentValidated.connect(bind([&nAfterSegmentValidated] { ++nAfterSegmentValidated; }));
+  fetcher->afterSegmentNacked.connect(bind([&nAfterSegmentNacked] { ++nAfterSegmentNacked; }));
+  fetcher->afterSegmentTimedOut.connect(bind([&nAfterSegmentTimedOut] { ++nAfterSegmentTimedOut; }));
+
+  face.processEvents(1_s);
+
+  BOOST_CHECK_EQUAL(nErrors, 0);
+  BOOST_CHECK_EQUAL(nCompletions, 1);
+  BOOST_CHECK_EQUAL(dataSize, 14 * 401);
+  BOOST_CHECK_EQUAL(nAfterSegmentReceived, 401);
+  BOOST_CHECK_EQUAL(nAfterSegmentValidated, 401);
+  BOOST_CHECK_EQUAL(nAfterSegmentNacked, 2);
+  BOOST_CHECK_EQUAL(nAfterSegmentTimedOut, 0);
+}
+
+BOOST_AUTO_TEST_CASE(OtherNackReason)
+{
+  DummyValidator acceptValidator;
+  size_t nAfterSegmentReceived = 0;
+  size_t nAfterSegmentValidated = 0;
+  size_t nAfterSegmentNacked = 0;
+  size_t nAfterSegmentTimedOut = 0;
+  segmentsToDropOrNack.push(0);
+  sendNackInsteadOfDropping = true;
+  nackReason = lp::NackReason::NO_ROUTE;
+  face.onSendInterest.connect(bind(&Fixture::onInterest, this, _1));
+
+  shared_ptr<SegmentFetcher> fetcher = SegmentFetcher::start(face, Interest("/hello/world"),
+                                                             acceptValidator);
+  connectSignals(fetcher);
+  fetcher->afterSegmentReceived.connect(bind([&nAfterSegmentReceived] { ++nAfterSegmentReceived; }));
+  fetcher->afterSegmentValidated.connect(bind([&nAfterSegmentValidated] { ++nAfterSegmentValidated; }));
+  fetcher->afterSegmentNacked.connect(bind([&nAfterSegmentNacked] { ++nAfterSegmentNacked; }));
+  fetcher->afterSegmentTimedOut.connect(bind([&nAfterSegmentTimedOut] { ++nAfterSegmentTimedOut; }));
+
+  face.processEvents(1_s);
+
+  BOOST_CHECK_EQUAL(nErrors, 1);
+  BOOST_CHECK_EQUAL(nCompletions, 0);
+  BOOST_CHECK_EQUAL(nAfterSegmentReceived, 0);
+  BOOST_CHECK_EQUAL(nAfterSegmentValidated, 0);
+  BOOST_CHECK_EQUAL(nAfterSegmentNacked, 1);
+  BOOST_CHECK_EQUAL(nAfterSegmentTimedOut, 0);
+  BOOST_CHECK_EQUAL(face.sentInterests.size(), 1);
+}
+
+BOOST_AUTO_TEST_CASE(ValidationFailure)
 {
   DummyValidator validator;
   validator.getPolicy().setResultCallback([] (const Name& name) {
       return name.at(-1).toSegment() % 2 == 0;
     });
-  shared_ptr<SegmentFetcher> fetcher = SegmentFetcher::start(face, Interest("/hello/world", 1000_s),
+  shared_ptr<SegmentFetcher> fetcher = SegmentFetcher::start(face, Interest("/hello/world"),
                                                              validator);
   connectSignals(fetcher);
 
@@ -297,14 +712,19 @@
 
   advanceClocks(10_ms, 10);
 
+  BOOST_CHECK_EQUAL(fetcher->m_timeLastSegmentReceived, time::steady_clock::now() - 100_ms);
+
   face.receive(*data1);
 
   advanceClocks(10_ms, 10);
 
+  BOOST_CHECK_EQUAL(fetcher->m_timeLastSegmentReceived, time::steady_clock::now() - 100_ms);
+
   face.receive(*data2);
 
   advanceClocks(10_ms, 10);
 
+  BOOST_CHECK_EQUAL(fetcher->m_timeLastSegmentReceived, time::steady_clock::now() - 200_ms);
   BOOST_CHECK_EQUAL(nRecvSegments, 2);
   BOOST_CHECK_EQUAL(nValidatedSegments, 1);
   BOOST_CHECK_EQUAL(nErrors, 1);
@@ -312,7 +732,7 @@
 
 // Tests deprecated `fetch` API that uses callbacks instead of signals. This test case will be
 // removed when this API is removed.
-BOOST_FIXTURE_TEST_CASE(DeprecatedFetch, Fixture)
+BOOST_AUTO_TEST_CASE(DeprecatedFetch)
 {
   DummyValidator acceptValidator;
   SegmentFetcher::fetch(face, Interest("/hello/world", 1000_s),
@@ -330,11 +750,11 @@
 
   BOOST_CHECK_EQUAL(dataSize, 14);
 
-  const uint8_t buffer[] = "Hello, world!";
-  std::string bufferString(reinterpret_cast<const char*>(buffer));
+  const uint8_t buffer[] = "Hello, world!\0";
+  BOOST_REQUIRE_EQUAL(dataBuf->size(), 14);
+  BOOST_CHECK_EQUAL_COLLECTIONS(dataBuf->begin(), dataBuf->end(), buffer, buffer + 14);
 
-  BOOST_CHECK_EQUAL(dataString, bufferString);
-
+  BOOST_CHECK_EQUAL(face.getNPendingInterests(), 0);
   BOOST_REQUIRE_EQUAL(face.sentInterests.size(), 1);
   BOOST_CHECK_EQUAL(face.sentData.size(), 0);
 
@@ -342,11 +762,65 @@
   BOOST_CHECK_EQUAL(interest.getName(), "/hello/world");
   BOOST_CHECK_EQUAL(interest.getMustBeFresh(), true);
   BOOST_CHECK_EQUAL(interest.getChildSelector(), 1);
+  BOOST_CHECK_EQUAL(interest.getInterestLifetime(), 1000_s);
+}
+
+// Tests deprecated `fetch` API that uses callbacks instead of signals. This test case will be
+// removed when this API is removed.
+BOOST_AUTO_TEST_CASE(DeprecatedFetchMultipleSegments)
+{
+  DummyValidator acceptValidator;
+  SegmentFetcher::fetch(face, Interest("/hello/world", 1000_s),
+                        acceptValidator,
+                        bind(&Fixture::onComplete, this, _1),
+                        bind(&Fixture::onError, this, _1));
+
+  advanceClocks(10_ms);
+
+  face.receive(*makeDataSegment("/hello/world/version0", 0, false));
+  advanceClocks(10_ms);
+
+  face.receive(*makeDataSegment("/hello/world/version0", 1, false));
+  advanceClocks(10_ms);
+
+  face.receive(*makeDataSegment("/hello/world/version0", 2, true));
+  advanceClocks(10_ms);
+
+  BOOST_CHECK_EQUAL(nErrors, 0);
+  BOOST_CHECK_EQUAL(nCompletions, 1);
+
+  BOOST_CHECK_EQUAL(dataSize, 14 * 3);
+
+  const uint8_t buffer[] = "Hello, world!\0Hello, world!\0Hello, world!\0";
+  BOOST_REQUIRE_EQUAL(dataBuf->size(), 14 * 3);
+  BOOST_CHECK_EQUAL_COLLECTIONS(dataBuf->begin(), dataBuf->end(), buffer, buffer + (14 * 3));
+
+  BOOST_CHECK_EQUAL(face.getNPendingInterests(), 0);
+  BOOST_REQUIRE_EQUAL(face.sentInterests.size(), 3);
+  BOOST_CHECK_EQUAL(face.sentData.size(), 0);
+
+  const Interest& interest0 = face.sentInterests[0];
+  BOOST_CHECK_EQUAL(interest0.getName(), "/hello/world");
+  BOOST_CHECK_EQUAL(interest0.getMustBeFresh(), true);
+  BOOST_CHECK_EQUAL(interest0.getChildSelector(), 1);
+  BOOST_CHECK_EQUAL(interest0.getInterestLifetime(), 1000_s);
+
+  const Interest& interest1 = face.sentInterests[1];
+  BOOST_CHECK_EQUAL(interest1.getName(), "/hello/world/version0/%00%01");
+  BOOST_CHECK_EQUAL(interest1.getMustBeFresh(), false);
+  BOOST_CHECK_EQUAL(interest1.getChildSelector(), 0);
+  BOOST_CHECK_EQUAL(interest1.getInterestLifetime(), 1000_s);
+
+  const Interest& interest2 = face.sentInterests[2];
+  BOOST_CHECK_EQUAL(interest2.getName(), "/hello/world/version0/%00%02");
+  BOOST_CHECK_EQUAL(interest2.getMustBeFresh(), false);
+  BOOST_CHECK_EQUAL(interest2.getChildSelector(), 0);
+  BOOST_CHECK_EQUAL(interest2.getInterestLifetime(), 1000_s);
 }
 
 // Tests deprecated `fetch` API that uses callbacks instead of signals (with an accepting shared_ptr
 // Validator). This test case will be removed when this API is removed.
-BOOST_FIXTURE_TEST_CASE(DeprecatedFetchSharedPtrComplete, Fixture)
+BOOST_AUTO_TEST_CASE(DeprecatedFetchSharedPtrComplete)
 {
   auto acceptValidator = make_shared<DummyValidator>(true);
   SegmentFetcher::fetch(face, Interest("/hello/world", 1000_s),
@@ -372,11 +846,11 @@
 
   BOOST_CHECK_EQUAL(dataSize, 14);
 
-  const uint8_t buffer[] = "Hello, world!";
-  std::string bufferString(reinterpret_cast<const char*>(buffer));
+  const uint8_t buffer[] = "Hello, world!\0";
+  BOOST_REQUIRE_EQUAL(dataBuf->size(), 14);
+  BOOST_CHECK_EQUAL_COLLECTIONS(dataBuf->begin(), dataBuf->end(), buffer, buffer + 14);
 
-  BOOST_CHECK_EQUAL(dataString, bufferString);
-
+  BOOST_CHECK_EQUAL(face.getNPendingInterests(), 0);
   BOOST_REQUIRE_EQUAL(face.sentInterests.size(), 1);
   BOOST_CHECK_EQUAL(face.sentData.size(), 0);
 
@@ -384,11 +858,12 @@
   BOOST_CHECK_EQUAL(interest.getName(), "/hello/world");
   BOOST_CHECK_EQUAL(interest.getMustBeFresh(), true);
   BOOST_CHECK_EQUAL(interest.getChildSelector(), 1);
+  BOOST_CHECK_EQUAL(interest.getInterestLifetime(), 1000_s);
 }
 
 // Tests deprecated `fetch` API that uses callbacks instead of signals (with a rejecting shared_ptr
 // Validator). This test case will be removed when this API is removed.
-BOOST_FIXTURE_TEST_CASE(DeprecatedFetchSharedPtrError, Fixture)
+BOOST_AUTO_TEST_CASE(DeprecatedFetchSharedPtrError)
 {
   auto acceptValidator = make_shared<DummyValidator>(false);
   SegmentFetcher::fetch(face, Interest("/hello/world", 1000_s),
@@ -412,6 +887,7 @@
   BOOST_CHECK_EQUAL(nErrors, 1);
   BOOST_CHECK_EQUAL(nCompletions, 0);
 
+  BOOST_CHECK_EQUAL(face.getNPendingInterests(), 0);
   BOOST_REQUIRE_EQUAL(face.sentInterests.size(), 1);
   BOOST_CHECK_EQUAL(face.sentData.size(), 0);
 
@@ -419,6 +895,7 @@
   BOOST_CHECK_EQUAL(interest.getName(), "/hello/world");
   BOOST_CHECK_EQUAL(interest.getMustBeFresh(), true);
   BOOST_CHECK_EQUAL(interest.getChildSelector(), 1);
+  BOOST_CHECK_EQUAL(interest.getInterestLifetime(), 1000_s);
 }
 
 BOOST_AUTO_TEST_SUITE_END() // TestSegmentFetcher