mgmt: Use InMemoryStorage for StatusDataset and Notification produced by managers

Change-Id: I2ed57061e25638c01df7a0323ed303cafe1be455
ref: #2182
diff --git a/src/mgmt/dispatcher.cpp b/src/mgmt/dispatcher.cpp
index 5dbe5cd..3b121ea 100644
--- a/src/mgmt/dispatcher.cpp
+++ b/src/mgmt/dispatcher.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2013-2015 Regents of the University of California.
+ * Copyright (c) 2013-2016 Regents of the University of California.
  *
  * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
  *
@@ -28,6 +28,8 @@
 namespace ndn {
 namespace mgmt {
 
+const time::milliseconds DEFAULT_FRESHNESS_PERIOD = time::milliseconds(1000);
+
 Authorization
 makeAcceptAllAuthorization()
 {
@@ -41,10 +43,12 @@
 }
 
 Dispatcher::Dispatcher(Face& face, security::KeyChain& keyChain,
-                       const security::SigningInfo& signingInfo)
+                       const security::SigningInfo& signingInfo,
+                       size_t imsCapacity)
   : m_face(face)
   , m_keyChain(keyChain)
   , m_signingInfo(signingInfo)
+  , m_storage(m_face.getIoService(), imsCapacity)
 {
 }
 
@@ -147,16 +151,46 @@
 }
 
 void
-Dispatcher::sendData(const Name& dataName, const Block& content,
-                     const MetaInfo& metaInfo)
+Dispatcher::queryStorage(const Name& prefix, const Interest& interest,
+                         const InterestHandler& missContinuation)
+{
+  auto data = m_storage.find(interest);
+  if (data == nullptr) {
+    // invoke missContinuation to process this Interest if the query fails.
+    missContinuation(prefix, interest);
+  }
+  else {
+    // send the fetched data through face if query succeeds.
+    sendOnFace(*data);
+  }
+}
+
+void
+Dispatcher::sendData(const Name& dataName, const Block& content, const MetaInfo& metaInfo,
+                     SendDestination option, time::milliseconds imsFresh)
 {
   shared_ptr<Data> data = make_shared<Data>(dataName);
-  data->setContent(content).setMetaInfo(metaInfo);
+  data->setContent(content).setMetaInfo(metaInfo).setFreshnessPeriod(DEFAULT_FRESHNESS_PERIOD);
 
   m_keyChain.sign(*data, m_signingInfo);
 
+  if (option == SendDestination::IMS || option == SendDestination::FACE_AND_IMS) {
+    lp::CachePolicy policy;
+    policy.setPolicy(lp::CachePolicyType::NO_CACHE);
+    data->setTag(make_shared<lp::CachePolicyTag>(policy));
+    m_storage.insert(*data, imsFresh);
+  }
+
+  if (option == SendDestination::FACE || option == SendDestination::FACE_AND_IMS) {
+    sendOnFace(*data);
+  }
+}
+
+void
+Dispatcher::sendOnFace(const Data& data)
+{
   try {
-    m_face.put(*data);
+    m_face.put(data);
   }
   catch (Face::Error& e) {
 #ifdef NDN_CXX_MGMT_DISPATCHER_ENABLE_LOGGING
@@ -210,14 +244,15 @@
 
 void
 Dispatcher::sendControlResponse(const ControlResponse& resp, const Interest& interest,
-                                bool isNack/*= false*/)
+                                bool isNack)
 {
-  MetaInfo info;
+  MetaInfo metaInfo;
   if (isNack) {
-    info.setType(tlv::ContentType_Nack);
+    metaInfo.setType(tlv::ContentType_Nack);
   }
-
-  sendData(interest.getName(), resp.wireEncode(), info);
+  // control response is always sent out through the face
+  sendData(interest.getName(), resp.wireEncode(), metaInfo, SendDestination::FACE,
+           DEFAULT_FRESHNESS_PERIOD);
 }
 
 void
@@ -238,8 +273,11 @@
          _1, _2, _3, handler);
   AuthorizationRejectedCallback rejected =
     bind(&Dispatcher::afterAuthorizationRejected, this, _1, _2);
-  m_handlers[relPrefix] = bind(&Dispatcher::processStatusDatasetInterest, this,
-                               _1, _2, authorization, accepted, rejected);
+
+  // follow the general path if storage is a miss
+  InterestHandler missContinuation = bind(&Dispatcher::processStatusDatasetInterest, this,
+                                          _1, _2, authorization, accepted, rejected);
+  m_handlers[relPrefix] = bind(&Dispatcher::queryStorage, this, _1, _2, missContinuation);
 }
 
 void
@@ -267,10 +305,31 @@
                                                    const Interest& interest,
                                                    const StatusDatasetHandler& handler)
 {
-  StatusDatasetContext context(interest, bind(&Dispatcher::sendData, this, _1, _2, _3));
+  StatusDatasetContext context(interest,
+                               bind(&Dispatcher::sendStatusDatasetSegment, this, _1, _2, _3, _4),
+                               bind(&Dispatcher::sendControlResponse, this, _1, interest, true));
   handler(prefix, interest, context);
 }
 
+void
+Dispatcher::sendStatusDatasetSegment(const Name& dataName, const Block& content,
+                                     time::milliseconds imsFresh, bool isFinalBlock)
+{
+  // the first segment will be sent to both places (the face and the in-memory storage)
+  // other segments will be inserted to the in-memory storage only
+  auto destination = SendDestination::IMS;
+  if (dataName[-1].toSegment() == 0) {
+    destination = SendDestination::FACE_AND_IMS;
+  }
+
+  MetaInfo metaInfo;
+  if (isFinalBlock) {
+    metaInfo.setFinalBlockId(dataName[-1]);
+  }
+
+  sendData(dataName, content, metaInfo, destination, imsFresh);
+}
+
 PostNotification
 Dispatcher::addNotificationStream(const PartialName& relPrefix)
 {
@@ -282,6 +341,11 @@
     throw std::out_of_range("relPrefix overlaps with another relPrefix");
   }
 
+  // keep silent if Interest does not match a stored notification
+  InterestHandler missContinuation = bind([]{});
+
+  // register a handler for the subscriber of this notification stream
+  m_handlers[relPrefix] = bind(&Dispatcher::queryStorage, this, _1, _2, missContinuation);
   m_streams[relPrefix] = 0;
   return bind(&Dispatcher::postNotification, this, _1, relPrefix);
 }
@@ -299,7 +363,11 @@
   Name streamName(m_topLevelPrefixes.begin()->second.topPrefix);
   streamName.append(relPrefix);
   streamName.appendSequenceNumber(m_streams[streamName]++);
-  sendData(streamName, notification, MetaInfo());
+
+  // notification is sent out the by face after inserting into the in-memory storage,
+  // because a request may be pending in the PIT
+  sendData(streamName, notification, MetaInfo(), SendDestination::FACE_AND_IMS,
+           DEFAULT_FRESHNESS_PERIOD);
 }
 
 } // namespace mgmt
diff --git a/src/mgmt/dispatcher.hpp b/src/mgmt/dispatcher.hpp
index 1e1195e..b19aba6 100644
--- a/src/mgmt/dispatcher.hpp
+++ b/src/mgmt/dispatcher.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2013-2015 Regents of the University of California.
+ * Copyright (c) 2013-2016 Regents of the University of California.
  *
  * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
  *
@@ -25,6 +25,7 @@
 #include "../face.hpp"
 #include "../security/key-chain.hpp"
 #include "../encoding/block.hpp"
+#include "../util/in-memory-storage-fifo.hpp"
 #include "control-response.hpp"
 #include "control-parameters.hpp"
 #include "status-dataset-context.hpp"
@@ -142,9 +143,11 @@
    *  \param face the Face on which the dispatcher operates
    *  \param keyChain a KeyChain to sign Data
    *  \param signingInfo signing parameters to sign Data with \p keyChain
+   *  \param imsCapacity capacity of the internal InMemoryStorage used by dispatcher
    */
   Dispatcher(Face& face, security::KeyChain& keyChain,
-             const security::SigningInfo& signingInfo = security::SigningInfo());
+             const security::SigningInfo& signingInfo = security::SigningInfo(),
+             size_t imsCapacity = 256);
 
   virtual
   ~Dispatcher();
@@ -315,9 +318,52 @@
   void
   afterAuthorizationRejected(RejectReply act, const Interest& interest);
 
+  /**
+   * @brief query Data the in-memory storage by a given Interest
+   *
+   * if the query fails, invoke @p missContinuation to process @p interest.
+   *
+   * @param prefix the top-level prefix
+   * @param interest the request
+   * @param missContinuation the handler of request when the query fails
+   */
   void
-  sendData(const Name& dataName, const Block& content,
-           const MetaInfo& metaInfo);
+  queryStorage(const Name& prefix, const Interest& interest, const InterestHandler& missContinuation);
+
+  enum class SendDestination {
+    NONE = 0,
+    FACE = 1,
+    IMS  = 2,
+    FACE_AND_IMS = 3
+  };
+
+  /**
+   * @brief send data to the face or in-memory storage
+   *
+   * create a data packet with the given @p dataName, @p content, and @p metaInfo,
+   * set its FreshnessPeriod to DEFAULT_FRESHNESS_PERIOD, and then send it out through the face and/or
+   * insert it into the in-memory storage as specified in @p option.
+   *
+   * if it's toward the in-memory storage, set its CachePolicy to NO_CACHE and limit
+   * its FreshnessPeriod in the storage as @p imsFresh
+   *
+   * @param dataName the name of this piece of data
+   * @param content the content of this piece of data
+   * @param metaInfo some meta information of this piece of data
+   * @param destination where to send this piece of data
+   * @param imsFresh freshness period of this piece of data in in-memory storage
+   */
+  void
+  sendData(const Name& dataName, const Block& content, const MetaInfo& metaInfo,
+           SendDestination destination, time::milliseconds imsFresh);
+
+  /**
+   * @brief send out a data packt through the face
+   *
+   * @param data the data packet to insert
+   */
+  void
+  sendOnFace(const Data& data);
 
   /**
    * @brief process the control-command Interest before authorization.
@@ -390,6 +436,18 @@
                                          const Interest& interest,
                                          const StatusDatasetHandler& handler);
 
+  /**
+   * @brief send a segment of StatusDataset
+   *
+   * @param dataName the name of this piece of data
+   * @param content the content of this piece of data
+   * @param imsFresh the freshness period of this piece of data in the in-memory storage
+   * @param isFinalBlock indicates whether this piece of data is the final block
+   */
+  void
+  sendStatusDatasetSegment(const Name& dataName, const Block& content,
+                           time::milliseconds imsFresh, bool isFinalBlock);
+
   void
   postNotification(const Block& notification, const PartialName& relPrefix);
 
@@ -413,6 +471,9 @@
 
   // NotificationStream name => next sequence number
   std::unordered_map<Name, uint64_t> m_streams;
+
+NDN_CXX_PUBLIC_WITH_TESTS_ELSE_PRIVATE:
+  util::InMemoryStorageFifo m_storage;
 };
 
 template<typename CP>
diff --git a/src/mgmt/status-dataset-context.cpp b/src/mgmt/status-dataset-context.cpp
index c3bc332..dbf6774 100644
--- a/src/mgmt/status-dataset-context.cpp
+++ b/src/mgmt/status-dataset-context.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2013-2015 Regents of the University of California.
+ * Copyright (c) 2013-2016 Regents of the University of California.
  *
  * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
  *
@@ -75,7 +75,6 @@
   m_state = State::RESPONDED;
 
   size_t nBytesLeft = block.size();
-
   while (nBytesLeft > 0) {
     size_t nBytesAppend = std::min(nBytesLeft,
                                    (ndn::MAX_NDN_PACKET_SIZE >> 1) - m_buffer->size());
@@ -83,9 +82,9 @@
     nBytesLeft -= nBytesAppend;
 
     if (nBytesLeft > 0) {
-      const Block& content = makeBinaryBlock(tlv::Content, m_buffer->buf(), m_buffer->size());
-      m_dataSender(Name(m_prefix).appendSegment(m_segmentNo++), content,
-                   MetaInfo().setFreshnessPeriod(m_expiry));
+      m_dataSender(Name(m_prefix).appendSegment(m_segmentNo++),
+                   makeBinaryBlock(tlv::Content, m_buffer->buf(), m_buffer->size()),
+                   m_expiry, false);
 
       m_buffer = std::make_shared<EncodingBuffer>();
     }
@@ -101,9 +100,9 @@
 
   m_state = State::FINALIZED;
 
-  auto dataName = Name(m_prefix).appendSegment(m_segmentNo++);
-  m_dataSender(dataName, makeBinaryBlock(tlv::Content, m_buffer->buf(), m_buffer->size()),
-               MetaInfo().setFreshnessPeriod(m_expiry).setFinalBlockId(dataName[-1]));
+  m_dataSender(Name(m_prefix).appendSegment(m_segmentNo),
+               makeBinaryBlock(tlv::Content, m_buffer->buf(), m_buffer->size()),
+               m_expiry, true);
 }
 
 void
@@ -115,14 +114,15 @@
 
   m_state = State::FINALIZED;
 
-  m_dataSender(m_interest.getName(), resp.wireEncode(),
-               MetaInfo().setType(tlv::ContentType_Nack));
+  m_nackSender(resp);
 }
 
 StatusDatasetContext::StatusDatasetContext(const Interest& interest,
-                                           const DataSender& dataSender)
+                                           const DataSender& dataSender,
+                                           const NackSender& nackSender)
   : m_interest(interest)
   , m_dataSender(dataSender)
+  , m_nackSender(nackSender)
   , m_expiry(DEFAULT_STATUS_DATASET_FRESHNESS_PERIOD)
   , m_buffer(make_shared<EncodingBuffer>())
   , m_segmentNo(0)
diff --git a/src/mgmt/status-dataset-context.hpp b/src/mgmt/status-dataset-context.hpp
index 45778ea..42ad0c1 100644
--- a/src/mgmt/status-dataset-context.hpp
+++ b/src/mgmt/status-dataset-context.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2013-2015 Regents of the University of California.
+ * Copyright (c) 2013-2016 Regents of the University of California.
  *
  * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
  *
@@ -92,16 +92,20 @@
   reject(const ControlResponse& resp = ControlResponse().setCode(400));
 
 NDN_CXX_PUBLIC_WITH_TESTS_ELSE_PRIVATE:
-  typedef std::function<void(const Name& dataName, const Block& content,
-                             const MetaInfo& metaInfo)> DataSender;
+  typedef std::function<void(const Name& dataName, const Block& content, time::milliseconds imsFresh,
+                             bool isFinalBlock)> DataSender;
+  typedef std::function<void(const ControlResponse& resp)> NackSender;
 
-  StatusDatasetContext(const Interest& interest, const DataSender& dataSender);
+  StatusDatasetContext(const Interest& interest,
+                       const DataSender& dataSender,
+                       const NackSender& nackSender);
 
 private:
   friend class Dispatcher;
 
   const Interest& m_interest;
   DataSender m_dataSender;
+  NackSender m_nackSender;
   Name m_prefix;
   time::milliseconds m_expiry;
 
diff --git a/tests/unit-tests/mgmt/dispatcher.t.cpp b/tests/unit-tests/mgmt/dispatcher.t.cpp
index 1e7ff30..5458be0 100644
--- a/tests/unit-tests/mgmt/dispatcher.t.cpp
+++ b/tests/unit-tests/mgmt/dispatcher.t.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2013-2015 Regents of the University of California.
+ * Copyright (c) 2013-2016 Regents of the University of California.
  *
  * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
  *
@@ -44,12 +44,14 @@
   DispatcherFixture()
     : face(io, {true, true})
     , dispatcher(face, m_keyChain, security::SigningInfo())
+    , storage(dispatcher.m_storage)
   {
   }
 
 public:
   util::DummyClientFace face;
   mgmt::Dispatcher dispatcher;
+  util::InMemoryStorageFifo& storage;
 };
 
 class VoidParameters : public mgmt::ControlParameters
@@ -292,14 +294,24 @@
   BOOST_CHECK_EQUAL(ControlResponse(face.sentData[1].getContent().blockFromValue()).getCode(), 403);
 
   face.sentData.clear();
-  face.receive(*util::makeInterest("/root/test/small/valid"));
+
+  auto interestSmall = *util::makeInterest("/root/test/small/valid");
+  face.receive(interestSmall);
   advanceClocks(time::milliseconds(1), 10);
+
+  // one data packet is generated and sent to both places
   BOOST_CHECK_EQUAL(face.sentData.size(), 1);
+  BOOST_CHECK_EQUAL(storage.size(), 1);
+
+  auto fetchedData = storage.find(interestSmall);
+  BOOST_REQUIRE(fetchedData != nullptr);
+  BOOST_CHECK(face.sentData[0].wireEncode() == fetchedData->wireEncode());
 
   face.receive(*util::makeInterest(Name("/root/test/small/valid").appendVersion(10))); // should be ignored
   face.receive(*util::makeInterest(Name("/root/test/small/valid").appendSegment(20))); // should be ignored
   advanceClocks(time::milliseconds(1), 10);
   BOOST_CHECK_EQUAL(face.sentData.size(), 1);
+  BOOST_CHECK_EQUAL(storage.size(), 1);
 
   Block content = face.sentData[0].getContent();
   BOOST_CHECK_NO_THROW(content.parse());
@@ -309,36 +321,53 @@
   BOOST_CHECK(content.elements()[1] == smallBlock);
   BOOST_CHECK(content.elements()[2] == smallBlock);
 
+  storage.erase("/", true); // clear the storage
   face.sentData.clear();
   face.receive(*util::makeInterest("/root/test/large/valid"));
   advanceClocks(time::milliseconds(1), 10);
-  BOOST_CHECK_EQUAL(face.sentData.size(), 2);
 
-  const auto& datas = face.sentData;
-  content = [&datas] () -> Block {
+  // two data packets are generated, the first one will be sent to both places
+  // while the second one will only be inserted into the in-memory storage
+  BOOST_CHECK_EQUAL(face.sentData.size(), 1);
+  BOOST_CHECK_EQUAL(storage.size(), 2);
+
+  // segment0 should be sent through the face
+  const auto& component = face.sentData[0].getName().at(-1);
+  BOOST_CHECK(component.isSegment());
+  BOOST_CHECK_EQUAL(component.toSegment(), 0);
+
+  std::vector<Data> dataInStorage;
+  std::copy(storage.begin(), storage.end(), std::back_inserter(dataInStorage));
+
+  // the Data sent through the face should be the same as the first Data in the storage
+  BOOST_CHECK_EQUAL(face.sentData[0].getName(), dataInStorage[0].getName());
+  BOOST_CHECK(face.sentData[0].getContent() == dataInStorage[0].getContent());
+
+  content = [&dataInStorage] () -> Block {
     EncodingBuffer encoder;
-    size_t valueLength = encoder.prependByteArray(datas[1].getContent().value(),
-                                                  datas[1].getContent().value_size());
-    valueLength += encoder.prependByteArray(datas[0].getContent().value(),
-                                            datas[0].getContent().value_size());
+    size_t valueLength = encoder.prependByteArray(dataInStorage[1].getContent().value(),
+                                                  dataInStorage[1].getContent().value_size());
+    valueLength += encoder.prependByteArray(dataInStorage[0].getContent().value(),
+                                            dataInStorage[0].getContent().value_size());
     encoder.prependVarNumber(valueLength);
     encoder.prependVarNumber(tlv::Content);
     return encoder.block();
   }();
 
   BOOST_CHECK_NO_THROW(content.parse());
-
   BOOST_CHECK_EQUAL(content.elements().size(), 3);
   BOOST_CHECK(content.elements()[0] == largeBlock);
   BOOST_CHECK(content.elements()[1] == largeBlock);
   BOOST_CHECK(content.elements()[2] == largeBlock);
 
+  storage.erase("/", true);// clear the storage
   face.sentData.clear();
   face.receive(*util::makeInterest("/root/test/reject/%80%00/valid")); // returns nack
   advanceClocks(time::milliseconds(1));
   BOOST_CHECK_EQUAL(face.sentData.size(), 1);
   BOOST_CHECK(face.sentData[0].getContentType() == tlv::ContentType_Nack);
   BOOST_CHECK_EQUAL(ControlResponse(face.sentData[0].getContent().blockFromValue()).getCode(), 400);
+  BOOST_CHECK_EQUAL(storage.size(), 0); // the nack packet will not be inserted into the in-memory storage
 }
 
 BOOST_FIXTURE_TEST_CASE(NotificationStream, DispatcherFixture)
@@ -358,6 +387,7 @@
   post(block);
   advanceClocks(time::milliseconds(1));
   BOOST_CHECK_EQUAL(face.sentData.size(), 1);
+  BOOST_CHECK_EQUAL(storage.size(), 1);
 
   post(block);
   post(block);
@@ -374,10 +404,24 @@
   BOOST_CHECK(face.sentData[1].getContent().blockFromValue() == block);
   BOOST_CHECK(face.sentData[2].getContent().blockFromValue() == block);
   BOOST_CHECK(face.sentData[3].getContent().blockFromValue() == block);
+
+  // each version of notification will be sent to both places
+  std::vector<Data> dataInStorage;
+  std::copy(storage.begin(), storage.end(), std::back_inserter(dataInStorage));
+  BOOST_CHECK_EQUAL(dataInStorage.size(), 4);
+  BOOST_CHECK_EQUAL(dataInStorage[0].getName(), "/root/test/%FE%00");
+  BOOST_CHECK_EQUAL(dataInStorage[1].getName(), "/root/test/%FE%01");
+  BOOST_CHECK_EQUAL(dataInStorage[2].getName(), "/root/test/%FE%02");
+  BOOST_CHECK_EQUAL(dataInStorage[3].getName(), "/root/test/%FE%03");
+
+  BOOST_CHECK(dataInStorage[0].getContent().blockFromValue() == block);
+  BOOST_CHECK(dataInStorage[1].getContent().blockFromValue() == block);
+  BOOST_CHECK(dataInStorage[2].getContent().blockFromValue() == block);
+  BOOST_CHECK(dataInStorage[3].getContent().blockFromValue() == block);
 }
 
-BOOST_AUTO_TEST_SUITE_END()
-BOOST_AUTO_TEST_SUITE_END()
+BOOST_AUTO_TEST_SUITE_END() // TestDispatcher
+BOOST_AUTO_TEST_SUITE_END() // Mgmt
 
 } // namespace tests
 } // namespace mgmt
diff --git a/tests/unit-tests/mgmt/status-dataset-context.t.cpp b/tests/unit-tests/mgmt/status-dataset-context.t.cpp
index 94f5a28..d7c0716 100644
--- a/tests/unit-tests/mgmt/status-dataset-context.t.cpp
+++ b/tests/unit-tests/mgmt/status-dataset-context.t.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2013-2015 Regents of the University of California.
+ * Copyright (c) 2013-2016 Regents of the University of California.
  *
  * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
  *
@@ -30,27 +30,45 @@
 class StatusDatasetContextFixture
 {
 public:
+  struct SendDataArgs
+  {
+    Name dataName;
+    Block content;
+    time::milliseconds imsFresh;
+    bool isFinalBlock;
+  };
+
   StatusDatasetContextFixture()
     : interest(util::makeInterest("/test/context/interest"))
     , contentBlock(makeStringBlock(tlv::Content, "/test/data/content"))
-    , context(*interest, bind(&StatusDatasetContextFixture::sendData, this, _1, _2, _3))
+    , context(*interest,
+              [this] (const Name& dataName, const Block& content,
+                      time::milliseconds imsFresh, bool isFinalBlock) {
+                SendDataArgs args{dataName, content, imsFresh, isFinalBlock};
+                sendDataHistory.push_back(args);
+              },
+              [this] (const ControlResponse& resp) {
+                sendNackHistory.push_back(resp);
+              })
+    , defaultImsFresh(time::milliseconds(1000))
   {
   }
 
-  void
-  sendData(const Name& dataName, const Block& content, const MetaInfo& info)
+  Name
+  makeSegmentName(size_t segmentNo)
   {
-    sentData.push_back(Data(dataName).setContent(content).setMetaInfo(info));
+    auto name = context.getPrefix();
+    return name.appendSegment(segmentNo);
   }
 
   Block
-  concatenate()
+  concatenateDataContent()
   {
     EncodingBuffer encoder;
     size_t valueLength = 0;
-    for (auto data : sentData) {
-       valueLength += encoder.appendByteArray(data.getContent().value(),
-                                              data.getContent().value_size());
+    for (auto args : sendDataHistory) {
+      const auto& content = args.content;
+      valueLength += encoder.appendByteArray(content.value(), content.value_size());
     }
     encoder.prependVarNumber(valueLength);
     encoder.prependVarNumber(tlv::Content);
@@ -58,13 +76,16 @@
   }
 
 public:
-  std::vector<Data> sentData;
+  std::vector<SendDataArgs> sendDataHistory;
+  std::vector<ControlResponse> sendNackHistory;
   shared_ptr<Interest> interest;
   Block contentBlock;
   mgmt::StatusDatasetContext context;
+  time::milliseconds defaultImsFresh;
 };
 
-BOOST_FIXTURE_TEST_SUITE(MgmtStatusDatasetContext, StatusDatasetContextFixture)
+BOOST_AUTO_TEST_SUITE(Mgmt)
+BOOST_FIXTURE_TEST_SUITE(TestStatusDatasetContext, StatusDatasetContextFixture)
 
 BOOST_AUTO_TEST_CASE(GetPrefix)
 {
@@ -123,14 +144,17 @@
 BOOST_AUTO_TEST_CASE(Respond)
 {
   BOOST_CHECK_NO_THROW(context.append(contentBlock));
-  BOOST_CHECK(sentData.empty()); // does not call end yet
+  BOOST_CHECK(sendDataHistory.empty()); // does not call end yet
 
   BOOST_CHECK_NO_THROW(context.end());
-  BOOST_CHECK_EQUAL(sentData.size(), 1);
-  BOOST_CHECK_EQUAL(sentData[0].getName()[-1].toSegment(), 0);
-  BOOST_CHECK_EQUAL(sentData[0].getName().getPrefix(-1), context.getPrefix());
-  BOOST_CHECK_EQUAL(sentData[0].getFinalBlockId().toSegment(), 0);
-  BOOST_CHECK(sentData[0].getContent().blockFromValue() == contentBlock);
+
+  BOOST_REQUIRE_EQUAL(sendDataHistory.size(), 1);
+  const auto& args = sendDataHistory[0];
+
+  BOOST_CHECK_EQUAL(args.dataName, makeSegmentName(0));
+  BOOST_CHECK(args.content.blockFromValue() == contentBlock);
+  BOOST_CHECK_EQUAL(args.imsFresh, defaultImsFresh);
+  BOOST_CHECK_EQUAL(args.isFinalBlock, true);
 }
 
 BOOST_AUTO_TEST_CASE(RespondLarge)
@@ -148,16 +172,24 @@
 
   BOOST_CHECK_NO_THROW(context.append(largeBlock));
   BOOST_CHECK_NO_THROW(context.end());
-  BOOST_CHECK_EQUAL(sentData.size(), 2);
-  BOOST_CHECK_EQUAL(sentData[0].getName()[-1].toSegment(), 0);
-  BOOST_CHECK_EQUAL(sentData[0].getName().getPrefix(-1), context.getPrefix());
-  BOOST_CHECK_EQUAL(sentData[1].getName()[-1].toSegment(), 1);
-  BOOST_CHECK_EQUAL(sentData[1].getName().getPrefix(-1), context.getPrefix());
-  BOOST_CHECK_EQUAL(sentData[1].getFinalBlockId().toSegment(), 1);
 
-  auto contentLargeBlock = concatenate();
+  // two segments are generated
+  BOOST_REQUIRE_EQUAL(sendDataHistory.size(), 2);
+
+  // check segment0
+  BOOST_CHECK_EQUAL(sendDataHistory[0].dataName, makeSegmentName(0));
+  BOOST_CHECK_EQUAL(sendDataHistory[0].imsFresh, defaultImsFresh);
+  BOOST_CHECK_EQUAL(sendDataHistory[0].isFinalBlock, false);
+
+  // check segment1
+  BOOST_CHECK_EQUAL(sendDataHistory[1].dataName, makeSegmentName(1));
+  BOOST_CHECK_EQUAL(sendDataHistory[1].imsFresh, defaultImsFresh);
+  BOOST_CHECK_EQUAL(sendDataHistory[1].isFinalBlock, true);
+
+  // check data content
+  auto contentLargeBlock = concatenateDataContent();
   BOOST_CHECK_NO_THROW(contentLargeBlock.parse());
-  BOOST_CHECK_EQUAL(contentLargeBlock.elements().size(), 1);
+  BOOST_REQUIRE_EQUAL(contentLargeBlock.elements().size(), 1);
   BOOST_CHECK(contentLargeBlock.elements()[0] == largeBlock);
 }
 
@@ -168,12 +200,14 @@
     BOOST_CHECK_NO_THROW(context.append(contentBlock));
   }
   BOOST_CHECK_NO_THROW(context.end());
-  BOOST_CHECK_EQUAL(sentData.size(), 1);
-  BOOST_CHECK_EQUAL(sentData[0].getName()[-1].toSegment(), 0);
-  BOOST_CHECK_EQUAL(sentData[0].getName().getPrefix(-1), context.getPrefix());
-  BOOST_CHECK_EQUAL(sentData[0].getFinalBlockId().toSegment(), 0);
 
-  auto contentMultiBlocks = concatenate();
+  // check data to in-memory storage
+  BOOST_REQUIRE_EQUAL(sendDataHistory.size(), 1);
+  BOOST_CHECK_EQUAL(sendDataHistory[0].dataName, makeSegmentName(0));
+  BOOST_CHECK_EQUAL(sendDataHistory[0].imsFresh, defaultImsFresh);
+  BOOST_CHECK_EQUAL(sendDataHistory[0].isFinalBlock, true);
+
+  auto contentMultiBlocks = concatenateDataContent();
   BOOST_CHECK_NO_THROW(contentMultiBlocks.parse());
   BOOST_CHECK_EQUAL(contentMultiBlocks.elements().size(), nBlocks);
   for (auto&& element : contentMultiBlocks.elements()) {
@@ -184,23 +218,32 @@
 BOOST_AUTO_TEST_CASE(Reject)
 {
   BOOST_CHECK_NO_THROW(context.reject());
-  BOOST_CHECK_EQUAL(sentData.size(), 1);
-  BOOST_CHECK(sentData[0].getContentType() == tlv::ContentType_Nack);
-  BOOST_CHECK_EQUAL(ControlResponse(sentData[0].getContent().blockFromValue()).getCode(), 400);
+  BOOST_REQUIRE_EQUAL(sendNackHistory.size(), 1);
+  BOOST_CHECK_EQUAL(sendNackHistory[0].getCode(), 400);
 }
 
-BOOST_AUTO_TEST_SUITE(AbnormalState)
+class AbnormalStateTestFixture
+{
+public:
+  AbnormalStateTestFixture()
+    : context(Interest("/abnormal-state"), bind([]{}), bind([]{}))
+  {
+  }
+
+public:
+  mgmt::StatusDatasetContext context;
+};
+
+BOOST_FIXTURE_TEST_SUITE(AbnormalState, AbnormalStateTestFixture)
 
 BOOST_AUTO_TEST_CASE(AppendReject)
 {
-  mgmt::StatusDatasetContext context(Interest("/abnormal-state"), bind([]{}));
   BOOST_CHECK_NO_THROW(context.append(Block("\x82\x01\x02", 3)));
   BOOST_CHECK_THROW(context.reject(), std::domain_error);
 }
 
 BOOST_AUTO_TEST_CASE(AppendEndReject)
 {
-  mgmt::StatusDatasetContext context(Interest("/abnormal-state"), bind([]{}));
   BOOST_CHECK_NO_THROW(context.append(Block("\x82\x01\x02", 3)));
   BOOST_CHECK_NO_THROW(context.end());
   BOOST_CHECK_THROW(context.reject(), std::domain_error);
@@ -208,7 +251,6 @@
 
 BOOST_AUTO_TEST_CASE(EndAppend)
 {
-  mgmt::StatusDatasetContext context (Interest("/abnormal-state"), bind([]{}));
   BOOST_CHECK_NO_THROW(context.end());
   // end, append -> error
   BOOST_CHECK_THROW(context.append(Block("\x82\x01\x02", 3)), std::domain_error);
@@ -216,36 +258,31 @@
 
 BOOST_AUTO_TEST_CASE(EndEnd)
 {
-  mgmt::StatusDatasetContext context(Interest("/abnormal-state"), bind([]{}));
   BOOST_CHECK_NO_THROW(context.end());
   BOOST_CHECK_THROW(context.end(), std::domain_error);
 }
 
 BOOST_AUTO_TEST_CASE(EndReject)
 {
-  mgmt::StatusDatasetContext context(Interest("/abnormal-state"), bind([]{}));
   BOOST_CHECK_NO_THROW(context.end());
   BOOST_CHECK_THROW(context.reject(), std::domain_error);
 }
 
 BOOST_AUTO_TEST_CASE(RejectAppend)
 {
-  mgmt::StatusDatasetContext context(Interest("/abnormal-state"), bind([]{}));
   BOOST_CHECK_NO_THROW(context.reject());
   BOOST_CHECK_THROW(context.append(Block("\x82\x01\x02", 3)), std::domain_error);
 }
 
 BOOST_AUTO_TEST_CASE(RejectEnd)
 {
-  mgmt::StatusDatasetContext context(Interest("/abnormal-state"), bind([]{}));
   BOOST_CHECK_NO_THROW(context.reject());
   BOOST_CHECK_THROW(context.end(), std::domain_error);
 }
 
 BOOST_AUTO_TEST_SUITE_END() // AbnormalState
-
-
-BOOST_AUTO_TEST_SUITE_END()
+BOOST_AUTO_TEST_SUITE_END() // TestStatusDatasetContext
+BOOST_AUTO_TEST_SUITE_END() // Mgmt
 
 } // namespace tests
 } // namespace mgmt