tests: move integrated tests into unit tests

Change-Id: Ifed29e53091ca430582b1c2e7f3514e02d7c8d14
diff --git a/src/logic.cpp b/src/logic.cpp
index 0c5b87a..e0c0501 100644
--- a/src/logic.cpp
+++ b/src/logic.cpp
@@ -121,14 +121,15 @@
 }
 
 void
-Logic::reset()
+Logic::reset(bool isOnInterest)
 {
   m_isInReset = true;
 
   m_state.reset();
   m_log.clear();
 
-  sendResetInterest();
+  if (!isOnInterest)
+    sendResetInterest();
 
   // reset outstanding interest name, so that data for previous interest will be dropped.
   if (m_outstandingInterestId != 0) {
@@ -173,7 +174,7 @@
     sessionName.appendNumber(ndn::time::toUnixTimestamp(ndn::time::system_clock::now()).count());
     m_nodeList[userPrefix].sessionName = sessionName;
     m_nodeList[userPrefix].seqNo = 0;
-    reset();
+    reset(false);
   }
 }
 
@@ -193,7 +194,7 @@
         m_defaultSigningId = DEFAULT_NAME;
       }
     }
-    reset();
+    reset(false);
   }
 }
 
@@ -459,7 +460,7 @@
 Logic::processResetInterest(const Interest& interest)
 {
   _LOG_DEBUG_ID(">> Logic::processResetInterest");
-  reset();
+  reset(true);
 }
 
 void
diff --git a/src/logic.hpp b/src/logic.hpp
index e8ea4f6..b7bfbdf 100644
--- a/src/logic.hpp
+++ b/src/logic.hpp
@@ -126,9 +126,13 @@
 
   ~Logic();
 
-  /// @brief Reset the sync tree (and restart synchronization again)
+  /**
+   * @brief Reset the sync tree (and restart synchronization again)
+   *
+   * @param isOnInterest a flag that tells whether the reset is called by reset interest.
+   */
   void
-  reset();
+  reset(bool isOnInterest = false);
 
   /**
    * @brief Set user prefix
diff --git a/src/socket.cpp b/src/socket.cpp
index 695f758..7356f73 100644
--- a/src/socket.cpp
+++ b/src/socket.cpp
@@ -30,8 +30,8 @@
 
 namespace chronosync {
 
-const ndn::Name Socket::DEFAULT_PREFIX;
 const ndn::Name Socket::DEFAULT_NAME;
+const ndn::Name Socket::DEFAULT_PREFIX;
 const ndn::shared_ptr<ndn::Validator> Socket::DEFAULT_VALIDATOR;
 
 Socket::Socket(const Name& syncPrefix,
@@ -46,12 +46,30 @@
   , m_signingId(signingId)
   , m_validator(validator)
 {
+  if (m_userPrefix != DEFAULT_NAME)
+    m_registeredPrefixList[m_userPrefix] =
+      m_face.setInterestFilter(m_userPrefix,
+                               bind(&Socket::onInterest, this, _1, _2),
+                               [] (const Name& prefix, const std::string& msg) {});
 }
 
 void
 Socket::addSyncNode(const Name& prefix, const Name& signingId)
 {
+  if (prefix == DEFAULT_NAME)
+    return;
+
+  auto itr = m_registeredPrefixList.find(prefix);
+  if (itr != m_registeredPrefixList.end())
+    return;
+
+  if (m_userPrefix == DEFAULT_NAME)
+    m_userPrefix = prefix;
   m_logic.addUserNode(prefix, signingId);
+  m_registeredPrefixList[prefix] =
+    m_face.setInterestFilter(prefix,
+                             bind(&Socket::onInterest, this, _1, _2),
+                             [] (const Name& prefix, const std::string& msg) {});
 }
 
 void
@@ -79,7 +97,7 @@
   else
     m_keyChain.signByIdentity(*data, m_signingId);
 
-  m_face.put(*data);
+  m_ims.insert(*data);
 
   m_logic.updateSeqNo(newSeq, prefix);
 }
@@ -126,6 +144,15 @@
 }
 
 void
+Socket::onInterest(const Name& prefix, const Interest& interest)
+{
+  shared_ptr<const Data>data = m_ims.find(interest);
+  if (static_cast<bool>(data)) {
+    m_face.put(*data);
+  }
+}
+
+void
 Socket::onData(const Interest& interest, Data& data,
                const ndn::OnDataValidated& onValidated,
                const ndn::OnDataValidationFailed& onFailed)
diff --git a/src/socket.hpp b/src/socket.hpp
index 3e09c79..6987003 100644
--- a/src/socket.hpp
+++ b/src/socket.hpp
@@ -26,6 +26,8 @@
 #define CHRONOSYNC_SOCKET_HPP
 
 #include <ndn-cxx/face.hpp>
+#include <ndn-cxx/util/in-memory-storage-persistent.hpp>
+#include <unordered_map>
 
 #include "logic.hpp"
 
@@ -140,6 +142,9 @@
 
 private:
   void
+  onInterest(const Name& prefix, const Interest& interest);
+
+  void
   onData(const Interest& interest, Data& data,
          const ndn::OnDataValidated& dataCallback,
          const ndn::OnDataValidationFailed& failCallback);
@@ -159,6 +164,7 @@
   static const ndn::shared_ptr<ndn::Validator> DEFAULT_VALIDATOR;
 
 private:
+  typedef std::unordered_map<ndn::Name, const ndn::RegisteredPrefixId*> RegisteredPrefixList;
 
   Name m_userPrefix;
   ndn::Face& m_face;
@@ -168,6 +174,9 @@
   ndn::Name m_signingId;
   ndn::KeyChain m_keyChain;
   ndn::shared_ptr<ndn::Validator> m_validator;
+
+  RegisteredPrefixList m_registeredPrefixList;
+  ndn::util::InMemoryStoragePersistent m_ims;
 };
 
 } // namespace chronosync
diff --git a/tests/integrated-tests/test-logic.cpp b/tests/integrated-tests/test-logic.cpp
deleted file mode 100644
index 031280a..0000000
--- a/tests/integrated-tests/test-logic.cpp
+++ /dev/null
@@ -1,301 +0,0 @@
-/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil -*- */
-/*
- * Copyright (c) 2012-2014 University of California, Los Angeles
- *
- * This file is part of ChronoSync, synchronization library for distributed realtime
- * applications for NDN.
- *
- * ChronoSync is free software: you can redistribute it and/or modify it under the terms
- * of the GNU General Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * ChronoSync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
- * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
- * PURPOSE.  See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * ChronoSync, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "logic.hpp"
-
-#include "boost-test.hpp"
-
-namespace chronosync {
-namespace test {
-
-using std::vector;
-
-class Handler
-{
-public:
-  Handler(ndn::Face& face,
-          const Name& syncPrefix,
-          const Name& userPrefix)
-    : logic(face,
-            syncPrefix,
-            userPrefix,
-            bind(&Handler::onUpdate, this, _1))
-  {
-  }
-
-  void
-  onUpdate(const vector<MissingDataInfo>& v)
-  {
-    for (size_t i = 0; i < v.size(); i++) {
-      update(v[i].session, v[i].high, v[i].low);
-    }
-  }
-
-  void
-  update(const Name& p, const SeqNo& high, const SeqNo& low)
-  {
-    map[p] = high;
-  }
-
-  void
-  updateSeqNo(const SeqNo& seqNo)
-  {
-    logic.updateSeqNo(seqNo);
-  }
-
-  Logic logic;
-  std::map<Name, SeqNo> map;
-};
-
-class LogicFixture
-{
-public:
-  LogicFixture()
-    : syncPrefix("/ndn/broadcast/sync")
-    , scheduler(io)
-  {
-    syncPrefix.appendVersion();
-    userPrefix[0] = Name("/user0");
-    userPrefix[1] = Name("/user1");
-    userPrefix[2] = Name("/user2");
-
-    faces[0] = make_shared<ndn::Face>(ref(io));
-    faces[1] = make_shared<ndn::Face>(ref(io));
-    faces[2] = make_shared<ndn::Face>(ref(io));
-  }
-
-  Name syncPrefix;
-  Name userPrefix[3];
-
-  boost::asio::io_service io;
-  shared_ptr<ndn::Face> faces[3];
-  ndn::Scheduler scheduler;
-  shared_ptr<Handler> handler[3];
-};
-
-BOOST_FIXTURE_TEST_SUITE(LogicTests, LogicFixture)
-
-void
-onUpdate(const vector<MissingDataInfo>& v)
-{
-}
-
-BOOST_AUTO_TEST_CASE(Constructor)
-{
-  Name syncPrefix("/ndn/broadcast/sync");
-  Name userPrefix("/user");
-  ndn::Face face;
-  BOOST_REQUIRE_NO_THROW(Logic(face, syncPrefix, userPrefix,
-                               bind(onUpdate, _1)));
-}
-
-BOOST_AUTO_TEST_CASE(TwoBasic)
-{
-  scheduler.scheduleEvent(ndn::time::milliseconds(100),
-    [this] { handler[0] = make_shared<Handler>(ref(*faces[0]), syncPrefix, userPrefix[0]); });
-
-  scheduler.scheduleEvent(ndn::time::milliseconds(200),
-    [this] { handler[1] = make_shared<Handler>(ref(*faces[1]), syncPrefix, userPrefix[1]); });
-
-  scheduler.scheduleEvent(ndn::time::milliseconds(300), [this] { handler[0]->updateSeqNo(1); });
-
-  scheduler.scheduleEvent(ndn::time::milliseconds(1000),
-    [this] { BOOST_CHECK_EQUAL(handler[1]->map[handler[0]->logic.getSessionName()], 1); });
-
-  scheduler.scheduleEvent(ndn::time::milliseconds(1100), [this] { handler[0]->updateSeqNo(2); });
-
-  scheduler.scheduleEvent(ndn::time::milliseconds(1800),
-    [this] { BOOST_CHECK_EQUAL(handler[1]->map[handler[0]->logic.getSessionName()], 2); });
-
-  scheduler.scheduleEvent(ndn::time::milliseconds(1900), [this] { handler[1]->updateSeqNo(2); });
-
-  scheduler.scheduleEvent(ndn::time::milliseconds(2600),
-    [this] { BOOST_CHECK_EQUAL(handler[0]->map[handler[1]->logic.getSessionName()], 2); });
-
-  scheduler.scheduleEvent(ndn::time::milliseconds(2800), [this] { io.stop(); });
-
-  io.run();
-}
-
-BOOST_AUTO_TEST_CASE(ThreeBasic)
-{
-  scheduler.scheduleEvent(ndn::time::milliseconds(100),
-    [this] { handler[0] = make_shared<Handler>(ref(*faces[0]), syncPrefix, userPrefix[0]); });
-
-  scheduler.scheduleEvent(ndn::time::milliseconds(200),
-    [this] { handler[1] = make_shared<Handler>(ref(*faces[1]), syncPrefix, userPrefix[1]); });
-
-  scheduler.scheduleEvent(ndn::time::milliseconds(300),
-    [this] { handler[2] = make_shared<Handler>(ref(*faces[2]), syncPrefix, userPrefix[2]); });
-
-  scheduler.scheduleEvent(ndn::time::milliseconds(500), [this] { handler[0]->updateSeqNo(1); });
-
-  scheduler.scheduleEvent(ndn::time::milliseconds(1400),
-    [this] { BOOST_CHECK_EQUAL(handler[1]->map[handler[0]->logic.getSessionName()], 1); });
-
-  scheduler.scheduleEvent(ndn::time::milliseconds(1450),
-    [this] { BOOST_CHECK_EQUAL(handler[2]->map[handler[0]->logic.getSessionName()], 1); });
-
-  scheduler.scheduleEvent(ndn::time::milliseconds(1500), [this] { handler[1]->updateSeqNo(2); });
-
-  scheduler.scheduleEvent(ndn::time::milliseconds(2400),
-    [this] { BOOST_CHECK_EQUAL(handler[0]->map[handler[1]->logic.getSessionName()], 2); });
-
-  scheduler.scheduleEvent(ndn::time::milliseconds(2450),
-    [this] { BOOST_CHECK_EQUAL(handler[2]->map[handler[1]->logic.getSessionName()], 2); });
-
-  scheduler.scheduleEvent(ndn::time::milliseconds(2500), [this] { handler[2]->updateSeqNo(4); });
-
-  scheduler.scheduleEvent(ndn::time::milliseconds(4400),
-    [this] { BOOST_CHECK_EQUAL(handler[0]->map[handler[2]->logic.getSessionName()], 4); });
-
-  scheduler.scheduleEvent(ndn::time::milliseconds(4450),
-    [this] { BOOST_CHECK_EQUAL(handler[1]->map[handler[2]->logic.getSessionName()], 4); });
-
-  scheduler.scheduleEvent(ndn::time::milliseconds(4500), [this] { io.stop(); });
-
-  io.run();
-}
-
-BOOST_AUTO_TEST_CASE(ResetRecover)
-{
-  scheduler.scheduleEvent(ndn::time::milliseconds(100),
-    [this] { handler[0] = make_shared<Handler>(ref(*faces[0]), syncPrefix, userPrefix[0]); });
-
-  scheduler.scheduleEvent(ndn::time::milliseconds(200),
-    [this] { handler[1] = make_shared<Handler>(ref(*faces[1]), syncPrefix, userPrefix[1]); });
-
-  scheduler.scheduleEvent(ndn::time::milliseconds(500), [this] { handler[0]->updateSeqNo(1); });
-
-  scheduler.scheduleEvent(ndn::time::milliseconds(1400),
-    [this] { BOOST_CHECK_EQUAL(handler[1]->map[handler[0]->logic.getSessionName()], 1); });
-
-  scheduler.scheduleEvent(ndn::time::milliseconds(1500), [this] { handler[1]->updateSeqNo(2); });
-
-  scheduler.scheduleEvent(ndn::time::milliseconds(2400),
-    [this] { BOOST_CHECK_EQUAL(handler[0]->map[handler[1]->logic.getSessionName()], 2); });
-
-  scheduler.scheduleEvent(ndn::time::milliseconds(2500),
-    [this] { handler[2] = make_shared<Handler>(ref(*faces[2]), syncPrefix, userPrefix[2]); });
-
-  scheduler.scheduleEvent(ndn::time::milliseconds(3000),
-    [this] { BOOST_CHECK_EQUAL(handler[1]->map[handler[0]->logic.getSessionName()], 1); });
-
-  scheduler.scheduleEvent(ndn::time::milliseconds(3050),
-    [this] { BOOST_CHECK_EQUAL(handler[0]->map[handler[1]->logic.getSessionName()], 2); });
-
-  scheduler.scheduleEvent(ndn::time::milliseconds(3100), [this] { handler[2]->updateSeqNo(4); });
-
-  scheduler.scheduleEvent(ndn::time::milliseconds(4000),
-    [this] { BOOST_CHECK_EQUAL(handler[1]->map[handler[2]->logic.getSessionName()], 4); });
-
-  scheduler.scheduleEvent(ndn::time::milliseconds(4050),
-    [this] { BOOST_CHECK_EQUAL(handler[0]->map[handler[2]->logic.getSessionName()], 4); });
-
-
-  scheduler.scheduleEvent(ndn::time::milliseconds(4500), [this] { io.stop(); });
-
-  io.run();
-}
-
-BOOST_AUTO_TEST_CASE(RecoverConflict)
-{
-  scheduler.scheduleEvent(ndn::time::milliseconds(0),
-    [this] { handler[0] = make_shared<Handler>(ref(*faces[0]), syncPrefix, userPrefix[0]); });
-
-  scheduler.scheduleEvent(ndn::time::milliseconds(50),
-    [this] { handler[1] = make_shared<Handler>(ref(*faces[1]), syncPrefix, userPrefix[1]); });
-
-  scheduler.scheduleEvent(ndn::time::milliseconds(100),
-    [this] { handler[2] = make_shared<Handler>(ref(*faces[2]), syncPrefix, userPrefix[2]); });
-
-  scheduler.scheduleEvent(ndn::time::milliseconds(500), [this] { handler[0]->updateSeqNo(1); });
-
-  scheduler.scheduleEvent(ndn::time::milliseconds(1400),
-    [this] { BOOST_CHECK_EQUAL(handler[1]->map[handler[0]->logic.getSessionName()], 1); });
-
-  scheduler.scheduleEvent(ndn::time::milliseconds(1400),
-    [this] { BOOST_CHECK_EQUAL(handler[2]->map[handler[0]->logic.getSessionName()], 1); });
-
-  scheduler.scheduleEvent(ndn::time::milliseconds(1500), [this] { handler[1]->updateSeqNo(2); });
-
-  scheduler.scheduleEvent(ndn::time::milliseconds(1500), [this] { handler[2]->updateSeqNo(4); });
-
-  scheduler.scheduleEvent(ndn::time::milliseconds(2400),
-    [this] { BOOST_CHECK_EQUAL(handler[0]->map[handler[1]->logic.getSessionName()], 2); });
-
-  scheduler.scheduleEvent(ndn::time::milliseconds(2450),
-    [this] { BOOST_CHECK_EQUAL(handler[0]->map[handler[2]->logic.getSessionName()], 4); });
-
-  scheduler.scheduleEvent(ndn::time::milliseconds(2500),
-    [this] { BOOST_CHECK_EQUAL(handler[1]->map[handler[2]->logic.getSessionName()], 4); });
-
-  scheduler.scheduleEvent(ndn::time::milliseconds(2550),
-    [this] { BOOST_CHECK_EQUAL(handler[2]->map[handler[1]->logic.getSessionName()], 2); });
-
-  scheduler.scheduleEvent(ndn::time::milliseconds(4500), [this] { io.stop(); });
-
-  io.run();
-}
-
-BOOST_AUTO_TEST_CASE(MultipleUserUnderOneLogic)
-{
-  scheduler.scheduleEvent(ndn::time::milliseconds(0),
-    [this] { handler[0] = make_shared<Handler>(ref(*faces[0]), syncPrefix, userPrefix[0]); });
-
-  scheduler.scheduleEvent(ndn::time::milliseconds(50),
-    [this] { handler[1] = make_shared<Handler>(ref(*faces[1]), syncPrefix, userPrefix[2]); });
-
-  scheduler.scheduleEvent(ndn::time::milliseconds(100),
-    [this] { handler[0]->logic.addUserNode(userPrefix[1]); });
-
-  scheduler.scheduleEvent(ndn::time::milliseconds(500), [this] { handler[0]->updateSeqNo(1); });
-
-  scheduler.scheduleEvent(ndn::time::milliseconds(1400),
-    [this] { BOOST_CHECK_EQUAL(handler[1]->map[handler[0]->logic.getSessionName()], 1); });
-
-  scheduler.scheduleEvent(ndn::time::milliseconds(1500),
-    [this] { handler[0]->logic.updateSeqNo(2, userPrefix[1]); });
-
-  scheduler.scheduleEvent(ndn::time::milliseconds(2400),
-    [this] {
-             Name sessionName = handler[0]->logic.getSessionName(userPrefix[1]);
-             BOOST_CHECK_EQUAL(handler[1]->map[sessionName], 2);
-           });
-
-  scheduler.scheduleEvent(ndn::time::milliseconds(2500), [this] { handler[1]->updateSeqNo(4); });
-
-  scheduler.scheduleEvent(ndn::time::milliseconds(3200),
-    [this] { BOOST_CHECK_EQUAL(handler[0]->map[handler[1]->logic.getSessionName()], 4); });
-
-  scheduler.scheduleEvent(ndn::time::milliseconds(3300),
-    [this] { handler[0]->logic.removeUserNode(userPrefix[0]); });
-
-  scheduler.scheduleEvent(ndn::time::milliseconds(4500),
-    [this] { BOOST_CHECK_EQUAL(handler[1]->logic.getSessionNames().size(), 2); });
-
-  scheduler.scheduleEvent(ndn::time::milliseconds(5000), [this] { io.stop(); });
-
-  io.run();
-}
-
-BOOST_AUTO_TEST_SUITE_END()
-
-} // namespace test
-} // namespace chronosync
diff --git a/tests/integrated-tests/test-socket.cpp b/tests/integrated-tests/test-socket.cpp
deleted file mode 100644
index d06c19d..0000000
--- a/tests/integrated-tests/test-socket.cpp
+++ /dev/null
@@ -1,333 +0,0 @@
-/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil -*- */
-/*
- * Copyright (c) 2012-2014 University of California, Los Angeles
- *
- * This file is part of ChronoSync, synchronization library for distributed realtime
- * applications for NDN.
- *
- * ChronoSync is free software: you can redistribute it and/or modify it under the terms
- * of the GNU General Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * ChronoSync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
- * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
- * PURPOSE.  See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * ChronoSync, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "socket.hpp"
-
-#include "boost-test.hpp"
-
-namespace chronosync {
-namespace test {
-
-using std::string;
-using std::vector;
-using std::map;
-
-/**
- * @brief Emulate an app that use the Socket class
- *
- * The app has two types of data set: one is simply string while the other is integer array.
- * For each type of data set, the app has a specific fetching strategy.
- */
-class SocketTestApp : noncopyable
-{
-public:
-  SocketTestApp(const Name& syncPrefix,
-                const Name& userPrefix,
-                ndn::Face& face,
-                bool isNum)
-    : sum(0)
-    , socket(syncPrefix,
-             userPrefix,
-             face,
-             isNum ? bind(&SocketTestApp::fetchNumbers, this, _1) :
-                     bind(&SocketTestApp::fetchAll, this, _1))
-  {
-  }
-
-  void
-  set(const shared_ptr<const Data>& dataPacket)
-  {
-    // std::cerr << "set Data" << std::endl;
-    Name dataName(dataPacket->getName());
-    string str2(reinterpret_cast<const char*>(dataPacket->getContent().value()),
-                dataPacket->getContent().value_size());
-    data.insert(make_pair(dataName, str2));
-  }
-
-  void
-  set(Name name, const char* buf, int len)
-  {
-    string str2(buf, len);
-    data.insert(make_pair(name, str2));
-  }
-
-  void
-  setNum(const shared_ptr<const Data>& dataPacket)
-  {
-    // std::cerr << "setNum Data" << std::endl;
-    size_t n = dataPacket->getContent().value_size() / 4;
-    const uint32_t* numbers = reinterpret_cast<const uint32_t*>(dataPacket->getContent().value());
-    for (size_t i = 0; i < n; i++) {
-      sum += numbers[i];
-    }
-  }
-
-  void
-  setNum(Name name, const uint8_t* buf, int len)
-  {
-    BOOST_ASSERT(len >= 4);
-
-    int n = len / 4;
-    const uint32_t* numbers = reinterpret_cast<const uint32_t*>(buf);
-    for (int i = 0; i < n; i++) {
-      sum += numbers[i];
-    }
-  }
-
-  void
-  fetchAll(const vector<MissingDataInfo>& v)
-  {
-    // std::cerr << "fetchAll" << std::endl;
-    for (int i = 0; i < v.size(); i++) {
-      for(SeqNo s = v[i].low; s <= v[i].high; ++s) {
-        socket.fetchData(v[i].session, s, [this] (const shared_ptr<const Data>& dataPacket) {
-            this->set(dataPacket);
-          });
-      }
-    }
-  }
-
-  void
-  fetchNumbers(const vector<MissingDataInfo> &v)
-  {
-    // std::cerr << "fetchNumbers" << std::endl;
-    for (int i = 0; i < v.size(); i++) {
-      for(SeqNo s = v[i].low; s <= v[i].high; ++s) {
-        socket.fetchData(v[i].session, s, [this] (const shared_ptr<const Data>& dataPacket) {
-            this->setNum(dataPacket);
-          });
-      }
-    }
-  }
-
-  string
-  toString()
-  {
-    string str = "\n";
-    for (map<Name, string>::iterator it = data.begin(); it != data.end(); ++it) {
-      str += "<";
-      str += it->first.toUri();
-      str += "|";
-      str += it->second;
-      str += ">";
-      str += "\n";
-    }
-
-    return str;
-  }
-
-  map<ndn::Name, string> data;
-  uint32_t sum;
-  Socket socket;
-};
-
-class SocketFixture
-{
-public:
-  SocketFixture()
-    : syncPrefix("/ndn/broadcast/sync")
-    , scheduler(io)
-  {
-    syncPrefix.appendVersion();
-    userPrefix[0] = Name("/user0");
-    userPrefix[1] = Name("/user1");
-    userPrefix[2] = Name("/user2");
-
-    faces[0] = make_shared<ndn::Face>(ref(io));
-    faces[1] = make_shared<ndn::Face>(ref(io));
-    faces[2] = make_shared<ndn::Face>(ref(io));
-  }
-
-  void
-  createSocket(size_t idx, bool isNum)
-  {
-    app[idx] = make_shared<SocketTestApp>(syncPrefix, userPrefix[idx], ref(*faces[idx]), isNum);
-    sessionName[idx] = app[idx]->socket.getLogic().getSessionName();
-  }
-
-  void
-  publishAppData(size_t idx, const string& data)
-  {
-    app[idx]->socket.publishData(reinterpret_cast<const uint8_t*>(data.c_str()), data.size(),
-                                 ndn::time::milliseconds(1000));
-  }
-
-  void
-  setAppData(size_t idx, SeqNo seqNo, const string& data)
-  {
-    Name dataName = sessionName[idx];
-    dataName.appendNumber(seqNo);
-    app[idx]->set(dataName, data.c_str(), data.size());
-  }
-
-  void
-  publishAppNum(size_t idx, const uint8_t* buf, size_t size)
-  {
-    app[idx]->socket.publishData(buf, size, ndn::time::milliseconds(1000));
-  }
-
-  void
-  setAppNum(size_t idx, SeqNo seqNo, const uint8_t* buf, size_t size)
-  {
-    Name dataName = sessionName[idx];
-    dataName.appendNumber(seqNo);
-    app[idx]->setNum(dataName, buf, size);
-  }
-
-  void
-  check(int round)
-  {
-    BOOST_CHECK_EQUAL(app[0]->toString(), app[1]->toString());
-    BOOST_CHECK_EQUAL(app[0]->toString(), app[2]->toString());
-  }
-
-  void
-  check2Num(int num)
-  {
-    BOOST_CHECK_EQUAL(app[0]->sum, app[1]->sum);
-    BOOST_CHECK_EQUAL(app[1]->sum, num);
-  }
-
-  void
-  terminate()
-  {
-    io.stop();
-  }
-
-  Name syncPrefix;
-  Name userPrefix[3];
-  Name sessionName[3];
-
-  boost::asio::io_service io;
-  shared_ptr<ndn::Face> faces[3];
-  ndn::Scheduler scheduler;
-  shared_ptr<SocketTestApp> app[3];
-};
-
-
-
-BOOST_FIXTURE_TEST_SUITE(SocketTests, SocketFixture)
-
-BOOST_AUTO_TEST_CASE(BasicData)
-{
-  scheduler.scheduleEvent(ndn::time::milliseconds(0),
-                          bind(&SocketFixture::createSocket, this, 0, false));
-
-  scheduler.scheduleEvent(ndn::time::milliseconds(50),
-                          bind(&SocketFixture::createSocket, this, 1, false));
-
-  scheduler.scheduleEvent(ndn::time::milliseconds(100),
-                          bind(&SocketFixture::createSocket, this, 2, false));
-
-  string data0 = "Very funny Scotty, now beam down my clothes";
-  scheduler.scheduleEvent(ndn::time::milliseconds(150),
-                          bind(&SocketFixture::publishAppData, this, 0, data0));
-  scheduler.scheduleEvent(ndn::time::milliseconds(1150),
-                          bind(&SocketFixture::setAppData, this, 0, 1, data0));
-  scheduler.scheduleEvent(ndn::time::milliseconds(1160),
-                          bind(&SocketFixture::check, this, 1));
-
-  string data1 = "Yes, give me that ketchup";
-  string data2 = "Don't look conspicuous, it draws fire";
-  scheduler.scheduleEvent(ndn::time::milliseconds(1170),
-                          bind(&SocketFixture::publishAppData, this, 0, data1));
-  scheduler.scheduleEvent(ndn::time::milliseconds(1180),
-                          bind(&SocketFixture::publishAppData, this, 0, data2));
-  scheduler.scheduleEvent(ndn::time::milliseconds(2150),
-                          bind(&SocketFixture::setAppData, this, 0, 2, data1));
-  scheduler.scheduleEvent(ndn::time::milliseconds(2160),
-                          bind(&SocketFixture::setAppData, this, 0, 3, data2));
-  scheduler.scheduleEvent(ndn::time::milliseconds(2170),
-                          bind(&SocketFixture::check, this, 2));
-
-  string data3 = "You surf the Internet, I surf the real world";
-  string data4 = "I got a fortune cookie once that said 'You like Chinese food'";
-  string data5 = "Real men wear pink. Why? Because their wives make them";
-  scheduler.scheduleEvent(ndn::time::milliseconds(3180),
-                          bind(&SocketFixture::publishAppData, this, 2, data3));
-  scheduler.scheduleEvent(ndn::time::milliseconds(3200),
-                          bind(&SocketFixture::publishAppData, this, 1, data4));
-  scheduler.scheduleEvent(ndn::time::milliseconds(3210),
-                          bind(&SocketFixture::publishAppData, this, 1, data5));
-  scheduler.scheduleEvent(ndn::time::milliseconds(4710),
-                          bind(&SocketFixture::setAppData, this, 2, 1, data3));
-  scheduler.scheduleEvent(ndn::time::milliseconds(4720),
-                          bind(&SocketFixture::setAppData, this, 1, 1, data4));
-  scheduler.scheduleEvent(ndn::time::milliseconds(4730),
-                          bind(&SocketFixture::setAppData, this, 1, 2, data5));
-  scheduler.scheduleEvent(ndn::time::milliseconds(4800),
-                          bind(&SocketFixture::check, this, 3));
-
-  // not sure weither this is simultanous data generation from multiple sources
-  string data6 = "Shakespeare says: 'Prose before hos.'";
-  string data7 = "Pick good people, talent never wears out";
-  scheduler.scheduleEvent(ndn::time::milliseconds(5500),
-                          bind(&SocketFixture::publishAppData, this, 0, data6));
-  scheduler.scheduleEvent(ndn::time::milliseconds(5500),
-                          bind(&SocketFixture::publishAppData, this, 1, data7));
-  scheduler.scheduleEvent(ndn::time::milliseconds(6800),
-                          bind(&SocketFixture::setAppData, this, 0, 4, data6));
-  scheduler.scheduleEvent(ndn::time::milliseconds(6800),
-                          bind(&SocketFixture::setAppData, this, 1, 3, data7));
-  scheduler.scheduleEvent(ndn::time::milliseconds(6900),
-                          bind(&SocketFixture::check, this, 4));
-
-  scheduler.scheduleEvent(ndn::time::milliseconds(7000),
-                          bind(&SocketFixture::terminate, this));
-
-  io.run();
-}
-
-BOOST_AUTO_TEST_CASE(BasicNumber)
-{
-  scheduler.scheduleEvent(ndn::time::milliseconds(0),
-                          bind(&SocketFixture::createSocket, this, 0, true));
-  scheduler.scheduleEvent(ndn::time::milliseconds(50),
-                          bind(&SocketFixture::createSocket, this, 1, true));
-
-  uint32_t num1[5] = {0, 1, 2, 3, 4};
-  uint8_t* buf1 = reinterpret_cast<uint8_t*>(num1);
-  size_t size1 = sizeof(num1);
-  scheduler.scheduleEvent(ndn::time::milliseconds(100),
-                          bind(&SocketFixture::publishAppNum, this, 0, buf1, size1));
-  scheduler.scheduleEvent(ndn::time::milliseconds(150),
-                          bind(&SocketFixture::setAppNum, this, 0, 0, buf1, size1));
-  scheduler.scheduleEvent(ndn::time::milliseconds(1000),
-                          bind(&SocketFixture::check2Num, this, 10));
-
-  uint32_t num2[5] = {9, 7, 2, 1, 1};
-  uint8_t* buf2 = reinterpret_cast<uint8_t*>(num2);
-  size_t size2 = sizeof(num2);
-  scheduler.scheduleEvent(ndn::time::milliseconds(1100),
-                          bind(&SocketFixture::publishAppNum, this, 1, buf2, size2));
-  scheduler.scheduleEvent(ndn::time::milliseconds(1150),
-                          bind(&SocketFixture::setAppNum, this, 1, 0, buf2, size2));
-  scheduler.scheduleEvent(ndn::time::milliseconds(2000),
-                          bind(&SocketFixture::check2Num, this, 30));
-
-  scheduler.scheduleEvent(ndn::time::milliseconds(7000),
-                          bind(&SocketFixture::terminate, this));
-
-  io.run();
-}
-
-BOOST_AUTO_TEST_SUITE_END()
-
-} // namespace test
-} // namespace chronosync
diff --git a/tests/unit-tests/test-logic.cpp b/tests/unit-tests/test-logic.cpp
new file mode 100644
index 0000000..53a6f57
--- /dev/null
+++ b/tests/unit-tests/test-logic.cpp
@@ -0,0 +1,344 @@
+/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil -*- */
+/*
+ * Copyright (c) 2012-2014 University of California, Los Angeles
+ *
+ * This file is part of ChronoSync, synchronization library for distributed realtime
+ * applications for NDN.
+ *
+ * ChronoSync is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * ChronoSync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE.  See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * ChronoSync, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "logic.hpp"
+#include "../unit-test-time-fixture.hpp"
+#include <ndn-cxx/util/dummy-client-face.hpp>
+#include "boost-test.hpp"
+
+namespace chronosync {
+namespace test {
+
+using std::vector;
+using ndn::util::DummyClientFace;
+using ndn::util::makeDummyClientFace;
+
+class Handler
+{
+public:
+  Handler(DummyClientFace& face,
+          const Name& syncPrefix,
+          const Name& userPrefix)
+    : logic(face,
+            syncPrefix,
+            userPrefix,
+            bind(&Handler::onUpdate, this, _1))
+  {
+  }
+
+  void
+  onUpdate(const vector<MissingDataInfo>& v)
+  {
+    for (size_t i = 0; i < v.size(); i++) {
+      update(v[i].session, v[i].high, v[i].low);
+    }
+  }
+
+  void
+  update(const Name& p, const SeqNo& high, const SeqNo& low)
+  {
+    map[p] = high;
+  }
+
+  void
+  updateSeqNo(const SeqNo& seqNo)
+  {
+    logic.updateSeqNo(seqNo);
+  }
+
+  Logic logic;
+  std::map<Name, SeqNo> map;
+};
+
+class LogicFixture : public ndn::tests::UnitTestTimeFixture
+{
+public:
+  LogicFixture()
+    : syncPrefix("/ndn/broadcast/sync")
+  {
+    syncPrefix.appendVersion();
+    userPrefix[0] = Name("/user0");
+    userPrefix[1] = Name("/user1");
+    userPrefix[2] = Name("/user2");
+
+    faces[0] = makeDummyClientFace(ref(io), {true, true});
+    faces[1] = makeDummyClientFace(ref(io), {true, true});
+    faces[2] = makeDummyClientFace(ref(io), {true, true});
+
+    for (int i = 0; i < 3; i++) {
+      readInterestOffset[i] = 0;
+      readDataOffset[i] = 0;
+    }
+  }
+
+  void
+  passPacket()
+  {
+    for (int i = 0; i < 3; i++)
+      checkFace(i);
+  }
+
+  void
+  checkFace(int sender)
+  {
+    while (faces[sender]->sentInterests.size() > readInterestOffset[sender]) {
+      for (int i = 0; i < 3; i++) {
+        if (sender != i)
+          faces[i]->receive(faces[sender]->sentInterests[readInterestOffset[sender]]);
+      }
+      readInterestOffset[sender]++;
+    }
+    while (faces[sender]->sentDatas.size() > readDataOffset[sender]) {
+      for (int i = 0; i < 3; i++) {
+        if (sender != i)
+          faces[i]->receive(faces[sender]->sentDatas[readDataOffset[sender]]);
+      }
+      readDataOffset[sender]++;
+    }
+  }
+
+  Name syncPrefix;
+  Name userPrefix[3];
+
+  shared_ptr<DummyClientFace> faces[3];
+  shared_ptr<Handler> handler[3];
+
+  size_t readInterestOffset[3];
+  size_t readDataOffset[3];
+};
+
+BOOST_FIXTURE_TEST_SUITE(LogicTests, LogicFixture)
+
+void
+onUpdate(const vector<MissingDataInfo>& v)
+{
+}
+
+BOOST_AUTO_TEST_CASE(Constructor)
+{
+  Name syncPrefix("/ndn/broadcast/sync");
+  Name userPrefix("/user");
+  shared_ptr<DummyClientFace> face = makeDummyClientFace(ref(io), {true, true});
+  BOOST_REQUIRE_NO_THROW(Logic(ref(*face), syncPrefix, userPrefix,
+                               bind(onUpdate, _1)));
+}
+
+BOOST_AUTO_TEST_CASE(TwoBasic)
+{
+  handler[0] = make_shared<Handler>(ref(*faces[0]), syncPrefix, userPrefix[0]);
+  advanceClocks(ndn::time::milliseconds(10), 10);
+
+  handler[1] = make_shared<Handler>(ref(*faces[1]), syncPrefix, userPrefix[1]);
+  advanceClocks(ndn::time::milliseconds(10), 10);
+
+  handler[0]->updateSeqNo(1);
+
+  for (int i = 0; i < 50; i++) {
+    advanceClocks(ndn::time::milliseconds(2), 10);
+    passPacket();
+  }
+  BOOST_CHECK_EQUAL(handler[1]->map[handler[0]->logic.getSessionName()], 1);
+  advanceClocks(ndn::time::milliseconds(10), 10);
+
+  handler[0]->updateSeqNo(2);
+
+  for (int i = 0; i < 50; i++) {
+    advanceClocks(ndn::time::milliseconds(2), 10);
+    passPacket();
+  }
+  BOOST_CHECK_EQUAL(handler[1]->map[handler[0]->logic.getSessionName()], 2);
+  advanceClocks(ndn::time::milliseconds(10), 10);
+
+  handler[1]->updateSeqNo(2);
+
+  for (int i = 0; i < 50; i++) {
+    advanceClocks(ndn::time::milliseconds(2), 10);
+    passPacket();
+  }
+  BOOST_CHECK_EQUAL(handler[0]->map[handler[1]->logic.getSessionName()], 2);
+}
+
+BOOST_AUTO_TEST_CASE(ThreeBasic)
+{
+  handler[0] = make_shared<Handler>(ref(*faces[0]), syncPrefix, userPrefix[0]);
+  advanceClocks(ndn::time::milliseconds(10), 10);
+
+  handler[1] = make_shared<Handler>(ref(*faces[1]), syncPrefix, userPrefix[1]);
+  advanceClocks(ndn::time::milliseconds(10), 10);
+
+  handler[2] = make_shared<Handler>(ref(*faces[2]), syncPrefix, userPrefix[2]);
+  advanceClocks(ndn::time::milliseconds(10), 20);
+
+  handler[0]->updateSeqNo(1);
+
+  for (int i = 0; i < 70; i++) {
+    advanceClocks(ndn::time::milliseconds(2), 10);
+    passPacket();
+  }
+  BOOST_CHECK_EQUAL(handler[1]->map[handler[0]->logic.getSessionName()], 1);
+  BOOST_CHECK_EQUAL(handler[2]->map[handler[0]->logic.getSessionName()], 1);
+
+  handler[1]->updateSeqNo(2);
+
+  for (int i = 0; i < 50; i++) {
+    advanceClocks(ndn::time::milliseconds(2), 10);
+    passPacket();
+  }
+  BOOST_CHECK_EQUAL(handler[0]->map[handler[1]->logic.getSessionName()], 2);
+  BOOST_CHECK_EQUAL(handler[2]->map[handler[1]->logic.getSessionName()], 2);
+
+  handler[2]->updateSeqNo(4);
+
+  for (int i = 0; i < 100; i++) {
+    advanceClocks(ndn::time::milliseconds(2), 10);
+    passPacket();
+  }
+  BOOST_CHECK_EQUAL(handler[0]->map[handler[2]->logic.getSessionName()], 4);
+  BOOST_CHECK_EQUAL(handler[1]->map[handler[2]->logic.getSessionName()], 4);
+}
+
+BOOST_AUTO_TEST_CASE(ResetRecover)
+{
+  handler[0] = make_shared<Handler>(ref(*faces[0]), syncPrefix, userPrefix[0]);
+  advanceClocks(ndn::time::milliseconds(10), 10);
+
+  handler[1] = make_shared<Handler>(ref(*faces[1]), syncPrefix, userPrefix[1]);
+  advanceClocks(ndn::time::milliseconds(10), 30);
+
+  handler[0]->updateSeqNo(1);
+
+  for (int i = 0; i < 50; i++) {
+    advanceClocks(ndn::time::milliseconds(2), 10);
+    passPacket();
+  }
+  BOOST_CHECK_EQUAL(handler[1]->map[handler[0]->logic.getSessionName()], 1);
+
+  handler[1]->updateSeqNo(2);
+
+  for (int i = 0; i < 50; i++) {
+    advanceClocks(ndn::time::milliseconds(2), 10);
+    passPacket();
+  }
+  BOOST_CHECK_EQUAL(handler[0]->map[handler[1]->logic.getSessionName()], 2);
+
+  advanceClocks(ndn::time::milliseconds(10), 10);
+  handler[2] = make_shared<Handler>(ref(*faces[2]), syncPrefix, userPrefix[2]);
+
+  for (int i = 0; i < 50; i++) {
+    advanceClocks(ndn::time::milliseconds(2), 10);
+    passPacket();
+  }
+  BOOST_CHECK_EQUAL(handler[1]->map[handler[0]->logic.getSessionName()], 1);
+  BOOST_CHECK_EQUAL(handler[0]->map[handler[1]->logic.getSessionName()], 2);
+
+  handler[2]->updateSeqNo(4);
+
+  for (int i = 0; i < 50; i++) {
+    advanceClocks(ndn::time::milliseconds(2), 10);
+    passPacket();
+  }
+  BOOST_CHECK_EQUAL(handler[1]->map[handler[2]->logic.getSessionName()], 4);
+  BOOST_CHECK_EQUAL(handler[0]->map[handler[2]->logic.getSessionName()], 4);
+}
+
+BOOST_AUTO_TEST_CASE(RecoverConflict)
+{
+  handler[0] = make_shared<Handler>(ref(*faces[0]), syncPrefix, userPrefix[0]);
+  advanceClocks(ndn::time::milliseconds(10), 10);
+
+  handler[1] = make_shared<Handler>(ref(*faces[1]), syncPrefix, userPrefix[1]);
+  advanceClocks(ndn::time::milliseconds(10), 10);
+
+  handler[2] = make_shared<Handler>(ref(*faces[2]), syncPrefix, userPrefix[2]);
+  advanceClocks(ndn::time::milliseconds(10), 30);
+
+  handler[0]->updateSeqNo(1);
+
+  for (int i = 0; i < 50; i++) {
+    advanceClocks(ndn::time::milliseconds(2), 10);
+    passPacket();
+  }
+  BOOST_CHECK_EQUAL(handler[1]->map[handler[0]->logic.getSessionName()], 1);
+  BOOST_CHECK_EQUAL(handler[2]->map[handler[0]->logic.getSessionName()], 1);
+
+  handler[1]->updateSeqNo(2);
+  handler[2]->updateSeqNo(4);
+
+  for (int i = 0; i < 75; i++) {
+    advanceClocks(ndn::time::milliseconds(2), 10);
+    passPacket();
+  }
+  BOOST_CHECK_EQUAL(handler[0]->map[handler[1]->logic.getSessionName()], 2);
+  BOOST_CHECK_EQUAL(handler[0]->map[handler[2]->logic.getSessionName()], 4);
+  BOOST_CHECK_EQUAL(handler[1]->map[handler[2]->logic.getSessionName()], 4);
+  BOOST_CHECK_EQUAL(handler[2]->map[handler[1]->logic.getSessionName()], 2);
+}
+
+BOOST_AUTO_TEST_CASE(MultipleUserUnderOneLogic)
+{
+  handler[0] = make_shared<Handler>(ref(*faces[0]), syncPrefix, userPrefix[0]);
+  advanceClocks(ndn::time::milliseconds(10), 10);
+
+  handler[1] = make_shared<Handler>(ref(*faces[1]), syncPrefix, userPrefix[2]);
+  advanceClocks(ndn::time::milliseconds(10), 10);
+
+  handler[0]->logic.addUserNode(userPrefix[1]);
+
+  for (int i = 0; i < 20; i++) {
+    advanceClocks(ndn::time::milliseconds(2), 10);
+    passPacket();
+  }
+
+  handler[0]->updateSeqNo(1);
+
+  for (int i = 0; i < 50; i++) {
+    advanceClocks(ndn::time::milliseconds(2), 10);
+    passPacket();
+  }
+  BOOST_CHECK_EQUAL(handler[1]->map[handler[0]->logic.getSessionName()], 1);
+
+  handler[0]->logic.updateSeqNo(2, userPrefix[1]);
+
+  for (int i = 0; i < 50; i++) {
+    advanceClocks(ndn::time::milliseconds(2), 10);
+    passPacket();
+  }
+  BOOST_CHECK_EQUAL(handler[1]->map[handler[0]->logic.getSessionName(userPrefix[1])], 2);
+
+  handler[1]->updateSeqNo(4);
+
+  for (int i = 0; i < 50; i++) {
+    advanceClocks(ndn::time::milliseconds(2), 10);
+    passPacket();
+  }
+  BOOST_CHECK_EQUAL(handler[0]->map[handler[1]->logic.getSessionName()], 4);
+
+  handler[0]->logic.removeUserNode(userPrefix[0]);
+
+  for (int i = 0; i < 100; i++) {
+    advanceClocks(ndn::time::milliseconds(2), 10);
+    passPacket();
+  }
+  BOOST_CHECK_EQUAL(handler[1]->logic.getSessionNames().size(), 2);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace test
+} // namespace chronosync
diff --git a/tests/unit-tests/test-multiple-user.cpp b/tests/unit-tests/test-multiple-user.cpp
index 44209e8..7effecc 100644
--- a/tests/unit-tests/test-multiple-user.cpp
+++ b/tests/unit-tests/test-multiple-user.cpp
@@ -50,11 +50,11 @@
   std::map<Name, SeqNo> map;
 };
 
-class LogicFixture
+class MultiUserFixture
 {
 public:
 
-  LogicFixture()
+  MultiUserFixture()
     : syncPrefix("/ndn/broadcast/sync")
     , scheduler(io)
   {
@@ -75,7 +75,7 @@
   shared_ptr<Handler> handler;
 };
 
-BOOST_FIXTURE_TEST_SUITE(LogicTests, LogicFixture)
+BOOST_FIXTURE_TEST_SUITE(MultiUserTests, MultiUserFixture)
 
 BOOST_AUTO_TEST_CASE(ThreeUserNode)
 {
diff --git a/tests/unit-tests/test-socket.cpp b/tests/unit-tests/test-socket.cpp
new file mode 100644
index 0000000..5bfcecf
--- /dev/null
+++ b/tests/unit-tests/test-socket.cpp
@@ -0,0 +1,359 @@
+/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil -*- */
+/*
+ * Copyright (c) 2012-2014 University of California, Los Angeles
+ *
+ * This file is part of ChronoSync, synchronization library for distributed realtime
+ * applications for NDN.
+ *
+ * ChronoSync is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * ChronoSync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE.  See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * ChronoSync, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "socket.hpp"
+#include "../unit-test-time-fixture.hpp"
+#include <ndn-cxx/util/dummy-client-face.hpp>
+#include "boost-test.hpp"
+
+namespace chronosync {
+namespace test {
+
+using std::string;
+using std::vector;
+using std::map;
+using ndn::util::DummyClientFace;
+using ndn::util::makeDummyClientFace;
+
+
+/**
+ * @brief Emulate an app that use the Socket class
+ *
+ * The app has two types of data set: one is simply string while the other is integer array.
+ * For each type of data set, the app has a specific fetching strategy.
+ */
+class SocketTestApp : noncopyable
+{
+public:
+  SocketTestApp(const Name& syncPrefix,
+                const Name& userPrefix,
+                DummyClientFace& face,
+                bool isNum)
+    : sum(0)
+    , socket(syncPrefix,
+             userPrefix,
+             face,
+             isNum ? bind(&SocketTestApp::fetchNumbers, this, _1) :
+                     bind(&SocketTestApp::fetchAll, this, _1))
+  {
+  }
+
+  void
+  set(const shared_ptr<const Data>& dataPacket)
+  {
+    // std::cerr << "set Data" << std::endl;
+    Name dataName(dataPacket->getName());
+    string str2(reinterpret_cast<const char*>(dataPacket->getContent().value()),
+                dataPacket->getContent().value_size());
+    data.insert(make_pair(dataName, str2));
+  }
+
+  void
+  set(Name name, const char* buf, int len)
+  {
+    string str2(buf, len);
+    data.insert(make_pair(name, str2));
+  }
+
+  void
+  setNum(const shared_ptr<const Data>& dataPacket)
+  {
+    // std::cerr << "setNum Data" << std::endl;
+    size_t n = dataPacket->getContent().value_size() / 4;
+    const uint32_t* numbers = reinterpret_cast<const uint32_t*>(dataPacket->getContent().value());
+    for (size_t i = 0; i < n; i++) {
+      sum += numbers[i];
+    }
+  }
+
+  void
+  setNum(Name name, const uint8_t* buf, int len)
+  {
+    BOOST_ASSERT(len >= 4);
+
+    int n = len / 4;
+    const uint32_t* numbers = reinterpret_cast<const uint32_t*>(buf);
+    for (int i = 0; i < n; i++) {
+      sum += numbers[i];
+    }
+  }
+
+  void
+  fetchAll(const vector<MissingDataInfo>& v)
+  {
+    // std::cerr << "fetchAll" << std::endl;
+    for (int i = 0; i < v.size(); i++) {
+      for(SeqNo s = v[i].low; s <= v[i].high; ++s) {
+        socket.fetchData(v[i].session, s, [this] (const shared_ptr<const Data>& dataPacket) {
+            this->set(dataPacket);
+          });
+      }
+    }
+  }
+
+  void
+  fetchNumbers(const vector<MissingDataInfo> &v)
+  {
+    // std::cerr << "fetchNumbers" << std::endl;
+    for (int i = 0; i < v.size(); i++) {
+      for(SeqNo s = v[i].low; s <= v[i].high; ++s) {
+        socket.fetchData(v[i].session, s, [this] (const shared_ptr<const Data>& dataPacket) {
+            this->setNum(dataPacket);
+          });
+      }
+    }
+  }
+
+  string
+  toString()
+  {
+    string str = "\n";
+    for (map<Name, string>::iterator it = data.begin(); it != data.end(); ++it) {
+      str += "<";
+      str += it->first.toUri();
+      str += "|";
+      str += it->second;
+      str += ">";
+      str += "\n";
+    }
+
+    return str;
+  }
+
+  map<ndn::Name, string> data;
+  uint32_t sum;
+  Socket socket;
+};
+
+class SocketFixture : public ndn::tests::UnitTestTimeFixture
+{
+public:
+  SocketFixture()
+    : syncPrefix("/ndn/broadcast/sync")
+  {
+    syncPrefix.appendVersion();
+    userPrefix[0] = Name("/user0");
+    userPrefix[1] = Name("/user1");
+    userPrefix[2] = Name("/user2");
+
+    faces[0] = makeDummyClientFace(ref(io), {true, true});
+    faces[1] = makeDummyClientFace(ref(io), {true, true});
+    faces[2] = makeDummyClientFace(ref(io), {true, true});
+
+    for (int i = 0; i < 3; i++) {
+      readInterestOffset[i] = 0;
+      readDataOffset[i] = 0;
+    }
+  }
+
+  void
+  passPacket()
+  {
+    for (int i = 0; i < 3; i++)
+      checkFace(i);
+  }
+
+  void
+  checkFace(int sender)
+  {
+    while (faces[sender]->sentInterests.size() > readInterestOffset[sender]) {
+      for (int i = 0; i < 3; i++) {
+        if (sender != i)
+          faces[i]->receive(faces[sender]->sentInterests[readInterestOffset[sender]]);
+      }
+      readInterestOffset[sender]++;
+    }
+    while (faces[sender]->sentDatas.size() > readDataOffset[sender]) {
+      for (int i = 0; i < 3; i++) {
+        if (sender != i)
+          faces[i]->receive(faces[sender]->sentDatas[readDataOffset[sender]]);
+      }
+      readDataOffset[sender]++;
+    }
+  }
+
+  void
+  createSocket(size_t idx, bool isNum)
+  {
+    app[idx] = make_shared<SocketTestApp>(syncPrefix, userPrefix[idx], ref(*faces[idx]), isNum);
+    sessionName[idx] = app[idx]->socket.getLogic().getSessionName();
+  }
+
+  void
+  publishAppData(size_t idx, const string& data)
+  {
+    app[idx]->socket.publishData(reinterpret_cast<const uint8_t*>(data.c_str()), data.size(),
+                                 ndn::time::milliseconds(1000));
+  }
+
+  void
+  setAppData(size_t idx, SeqNo seqNo, const string& data)
+  {
+    Name dataName = sessionName[idx];
+    dataName.appendNumber(seqNo);
+    app[idx]->set(dataName, data.c_str(), data.size());
+  }
+
+  void
+  publishAppNum(size_t idx, const uint8_t* buf, size_t size)
+  {
+    app[idx]->socket.publishData(buf, size, ndn::time::milliseconds(1000));
+  }
+
+  void
+  setAppNum(size_t idx, SeqNo seqNo, const uint8_t* buf, size_t size)
+  {
+    Name dataName = sessionName[idx];
+    dataName.appendNumber(seqNo);
+    app[idx]->setNum(dataName, buf, size);
+  }
+
+  Name syncPrefix;
+  Name userPrefix[3];
+  Name sessionName[3];
+
+  shared_ptr<DummyClientFace> faces[3];
+  shared_ptr<SocketTestApp> app[3];
+
+  size_t readInterestOffset[3];
+  size_t readDataOffset[3];
+};
+
+
+
+BOOST_FIXTURE_TEST_SUITE(SocketTests, SocketFixture)
+
+BOOST_AUTO_TEST_CASE(BasicData)
+{
+  createSocket(0, false);
+  advanceClocks(ndn::time::milliseconds(10), 5);
+  createSocket(1, false);
+  advanceClocks(ndn::time::milliseconds(10), 5);
+  createSocket(2, false);
+  advanceClocks(ndn::time::milliseconds(10), 5);
+
+  string data0 = "Very funny Scotty, now beam down my clothes";
+  publishAppData(0, data0);
+
+  for (int i = 0; i < 50; i++) {
+    advanceClocks(ndn::time::milliseconds(2), 10);
+    passPacket();
+  }
+  setAppData(0, 1, data0);
+
+  advanceClocks(ndn::time::milliseconds(10), 1);
+  BOOST_CHECK_EQUAL(app[0]->toString(), app[1]->toString());
+  BOOST_CHECK_EQUAL(app[0]->toString(), app[2]->toString());
+
+  string data1 = "Yes, give me that ketchup";
+  string data2 = "Don't look conspicuous, it draws fire";
+  publishAppData(0, data1);
+  advanceClocks(ndn::time::milliseconds(10), 1);
+  publishAppData(0, data2);
+
+  for (int i = 0; i < 50; i++) {
+    advanceClocks(ndn::time::milliseconds(2), 10);
+    passPacket();
+  }
+  setAppData(0, 2, data1);
+  advanceClocks(ndn::time::milliseconds(10), 1);
+  setAppData(0, 3, data2);
+
+  advanceClocks(ndn::time::milliseconds(10), 1);
+  BOOST_CHECK_EQUAL(app[0]->toString(), app[1]->toString());
+  BOOST_CHECK_EQUAL(app[0]->toString(), app[2]->toString());
+
+  string data3 = "You surf the Internet, I surf the real world";
+  string data4 = "I got a fortune cookie once that said 'You like Chinese food'";
+  string data5 = "Real men wear pink. Why? Because their wives make them";
+  publishAppData(2, data3);
+  advanceClocks(ndn::time::milliseconds(10), 2);
+  publishAppData(1, data4);
+  advanceClocks(ndn::time::milliseconds(10), 1);
+  publishAppData(1, data5);
+
+  for (int i = 0; i < 100; i++) {
+    advanceClocks(ndn::time::milliseconds(2), 10);
+    passPacket();
+  }
+  setAppData(2, 1, data3);
+  advanceClocks(ndn::time::milliseconds(10), 1);
+  setAppData(1, 1, data4);
+  advanceClocks(ndn::time::milliseconds(10), 1);
+  setAppData(1, 2, data5);
+
+  advanceClocks(ndn::time::milliseconds(10), 7);
+  BOOST_CHECK_EQUAL(app[0]->toString(), app[1]->toString());
+  BOOST_CHECK_EQUAL(app[0]->toString(), app[2]->toString());
+
+  string data6 = "Shakespeare says: 'Prose before hos.'";
+  string data7 = "Pick good people, talent never wears out";
+  publishAppData(0, data6);
+  publishAppData(1, data7);
+
+  for (int i = 0; i < 100; i++) {
+    advanceClocks(ndn::time::milliseconds(2), 10);
+    passPacket();
+  }
+  setAppData(0, 4, data6);
+  setAppData(1, 3, data7);
+
+  BOOST_CHECK_EQUAL(app[0]->toString(), app[1]->toString());
+  BOOST_CHECK_EQUAL(app[0]->toString(), app[2]->toString());
+}
+
+BOOST_AUTO_TEST_CASE(BasicNumber)
+{
+  createSocket(0, true);
+  advanceClocks(ndn::time::milliseconds(10), 5);
+  createSocket(1, true);
+  advanceClocks(ndn::time::milliseconds(10), 5);
+
+  uint32_t num1[5] = {0, 1, 2, 3, 4};
+  uint8_t* buf1 = reinterpret_cast<uint8_t*>(num1);
+  size_t size1 = sizeof(num1);
+  publishAppNum(0, buf1, size1);
+  advanceClocks(ndn::time::milliseconds(10), 5);
+  setAppNum(0, 0, buf1, size1);
+
+  for (int i = 0; i < 100; i++) {
+    advanceClocks(ndn::time::milliseconds(2), 10);
+    passPacket();
+  }
+  BOOST_CHECK_EQUAL(app[0]->sum, app[1]->sum);
+  BOOST_CHECK_EQUAL(app[1]->sum, 10);
+
+  uint32_t num2[5] = {9, 7, 2, 1, 1};
+  uint8_t* buf2 = reinterpret_cast<uint8_t*>(num2);
+  size_t size2 = sizeof(num2);
+  publishAppNum(1, buf2, size2);
+  setAppNum(1, 0, buf2, size2);
+
+  for (int i = 0; i < 50; i++) {
+    advanceClocks(ndn::time::milliseconds(2), 10);
+    passPacket();
+  }
+  BOOST_CHECK_EQUAL(app[0]->sum, app[1]->sum);
+  BOOST_CHECK_EQUAL(app[1]->sum, 30);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace test
+} // namespace chronosync