src: Reorganizing source code in preparation to merge NRD code

Note that as of this commit, there are several changes in location of
compiled binaries in `build/` folder:

* `nfd` has been moved to `build/bin/nfd`
* `unit-tests` has been split into `unit-tests-core` and `unit-tests-daemon`

Change-Id: I2c830c117879edbaa5457d6423c13f0273285919
Refs: #1486
diff --git a/tests/daemon/face/dummy-face.hpp b/tests/daemon/face/dummy-face.hpp
new file mode 100644
index 0000000..7039286
--- /dev/null
+++ b/tests/daemon/face/dummy-face.hpp
@@ -0,0 +1,93 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014  Regents of the University of California,
+ *                     Arizona Board of Regents,
+ *                     Colorado State University,
+ *                     University Pierre & Marie Curie, Sorbonne University,
+ *                     Washington University in St. Louis,
+ *                     Beijing Institute of Technology
+ *
+ * This file is part of NFD (Named Data Networking Forwarding Daemon).
+ * See AUTHORS.md for complete list of NFD authors and contributors.
+ *
+ * NFD 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.
+ *
+ * NFD 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
+ * NFD, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+#ifndef NFD_TESTS_NFD_FACE_DUMMY_FACE_HPP
+#define NFD_TESTS_NFD_FACE_DUMMY_FACE_HPP
+
+#include "face/face.hpp"
+#include "face/local-face.hpp"
+
+namespace nfd {
+namespace tests {
+
+/** \class DummyFace
+ *  \brief a Face for unit testing
+ */
+template<class FaceBase>
+class DummyFaceImpl : public FaceBase
+{
+public:
+  DummyFaceImpl()
+    : FaceBase(FaceUri("dummy://"), FaceUri("dummy://"))
+  {
+  }
+
+  virtual void
+  sendInterest(const Interest& interest)
+  {
+    this->onSendInterest(interest);
+    m_sentInterests.push_back(interest);
+    this->afterSend();
+  }
+
+  virtual void
+  sendData(const Data& data)
+  {
+    this->onSendData(data);
+    m_sentDatas.push_back(data);
+    this->afterSend();
+  }
+
+  virtual void
+  close()
+  {
+    this->onFail("close");
+  }
+
+  void
+  receiveInterest(const Interest& interest)
+  {
+    this->onReceiveInterest(interest);
+  }
+
+  void
+  receiveData(const Data& data)
+  {
+    this->onReceiveData(data);
+  }
+
+  EventEmitter<> afterSend;
+
+public:
+  std::vector<Interest> m_sentInterests;
+  std::vector<Data> m_sentDatas;
+};
+
+typedef DummyFaceImpl<Face> DummyFace;
+typedef DummyFaceImpl<LocalFace> DummyLocalFace;
+
+} // namespace tests
+} // namespace nfd
+
+#endif // NFD_TESTS_NFD_FACE_DUMMY_FACE_HPP
diff --git a/tests/daemon/face/ethernet.cpp b/tests/daemon/face/ethernet.cpp
new file mode 100644
index 0000000..d27fda4
--- /dev/null
+++ b/tests/daemon/face/ethernet.cpp
@@ -0,0 +1,162 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014  Regents of the University of California,
+ *                     Arizona Board of Regents,
+ *                     Colorado State University,
+ *                     University Pierre & Marie Curie, Sorbonne University,
+ *                     Washington University in St. Louis,
+ *                     Beijing Institute of Technology
+ *
+ * This file is part of NFD (Named Data Networking Forwarding Daemon).
+ * See AUTHORS.md for complete list of NFD authors and contributors.
+ *
+ * NFD 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.
+ *
+ * NFD 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
+ * NFD, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+#include "face/ethernet-factory.hpp"
+#include "core/network-interface.hpp"
+#include "tests/test-common.hpp"
+
+#include <ndn-cpp-dev/security/key-chain.hpp>
+#include <pcap/pcap.h>
+
+namespace nfd {
+namespace tests {
+
+BOOST_FIXTURE_TEST_SUITE(FaceEthernet, BaseFixture)
+
+class InterfacesFixture : protected BaseFixture
+{
+protected:
+  InterfacesFixture()
+  {
+    std::list< shared_ptr<NetworkInterfaceInfo> > ifs = listNetworkInterfaces();
+    for (std::list< shared_ptr<NetworkInterfaceInfo> >::const_iterator i = ifs.begin();
+         i != ifs.end();
+         ++i)
+      {
+        if (!(*i)->isLoopback() && (*i)->isUp())
+          {
+            pcap_t* p = pcap_create((*i)->name.c_str(), 0);
+            if (!p)
+              continue;
+
+            if (pcap_activate(p) == 0)
+              m_interfaces.push_back(*i);
+
+            pcap_close(p);
+          }
+      }
+  }
+
+protected:
+  std::list< shared_ptr<NetworkInterfaceInfo> > m_interfaces;
+};
+
+
+BOOST_FIXTURE_TEST_CASE(MulticastFacesMap, InterfacesFixture)
+{
+  EthernetFactory factory;
+
+  if (m_interfaces.empty())
+    {
+      BOOST_WARN_MESSAGE(false, "No interfaces available, cannot perform MulticastFacesMap test");
+      return;
+    }
+
+  shared_ptr<EthernetFace> face1;
+  BOOST_REQUIRE_NO_THROW(face1 = factory.createMulticastFace(m_interfaces.front(),
+                                                             ethernet::getBroadcastAddress()));
+  shared_ptr<EthernetFace> face1bis;
+  BOOST_REQUIRE_NO_THROW(face1bis = factory.createMulticastFace(m_interfaces.front(),
+                                                                ethernet::getBroadcastAddress()));
+  BOOST_CHECK_EQUAL(face1, face1bis);
+
+  if (m_interfaces.size() > 1)
+    {
+      shared_ptr<EthernetFace> face2;
+      BOOST_REQUIRE_NO_THROW(face2 = factory.createMulticastFace(m_interfaces.back(),
+                                                                 ethernet::getBroadcastAddress()));
+      BOOST_CHECK_NE(face1, face2);
+    }
+  else
+    {
+      BOOST_WARN_MESSAGE(false, "Cannot test second EthernetFace creation, "
+                         "only one interface available");
+    }
+
+  shared_ptr<EthernetFace> face3;
+  BOOST_REQUIRE_NO_THROW(face3 = factory.createMulticastFace(m_interfaces.front(),
+                                                             ethernet::getDefaultMulticastAddress()));
+  BOOST_CHECK_NE(face1, face3);
+}
+
+BOOST_FIXTURE_TEST_CASE(SendPacket, InterfacesFixture)
+{
+  EthernetFactory factory;
+
+  if (m_interfaces.empty())
+    {
+      BOOST_WARN_MESSAGE(false, "No interfaces available for pcap, cannot perform SendPacket test");
+      return;
+    }
+
+  shared_ptr<EthernetFace> face = factory.createMulticastFace(m_interfaces.front(),
+                                                              ethernet::getDefaultMulticastAddress());
+
+  BOOST_REQUIRE(static_cast<bool>(face));
+
+  BOOST_CHECK(!face->isOnDemand());
+  BOOST_CHECK_EQUAL(face->isLocal(), false);
+  BOOST_CHECK_EQUAL(face->getRemoteUri().toString(),
+                    "ether://" + ethernet::getDefaultMulticastAddress().toString());
+  BOOST_CHECK_EQUAL(face->getLocalUri().toString(),
+                    "dev://" + m_interfaces.front()->name);
+
+  Interest interest1("ndn:/TpnzGvW9R");
+  Data     data1    ("ndn:/KfczhUqVix");
+  data1.setContent(0, 0);
+  Interest interest2("ndn:/QWiIMfj5sL");
+  Data     data2    ("ndn:/XNBV796f");
+  data2.setContent(0, 0);
+
+  ndn::SignatureSha256WithRsa fakeSignature;
+  fakeSignature.setValue(ndn::dataBlock(tlv::SignatureValue, reinterpret_cast<const uint8_t*>(0), 0));
+
+  // set fake signature on data1 and data2
+  data1.setSignature(fakeSignature);
+  data2.setSignature(fakeSignature);
+
+  BOOST_CHECK_NO_THROW(face->sendInterest(interest1));
+  BOOST_CHECK_NO_THROW(face->sendData    (data1    ));
+  BOOST_CHECK_NO_THROW(face->sendInterest(interest2));
+  BOOST_CHECK_NO_THROW(face->sendData    (data2    ));
+
+//  m_ioRemaining = 4;
+//  m_ioService.run();
+//  m_ioService.reset();
+
+//  BOOST_REQUIRE_EQUAL(m_face1_receivedInterests.size(), 1);
+//  BOOST_REQUIRE_EQUAL(m_face1_receivedDatas    .size(), 1);
+//  BOOST_REQUIRE_EQUAL(m_face2_receivedInterests.size(), 1);
+//  BOOST_REQUIRE_EQUAL(m_face2_receivedDatas    .size(), 1);
+
+//  BOOST_CHECK_EQUAL(m_face1_receivedInterests[0].getName(), interest2.getName());
+//  BOOST_CHECK_EQUAL(m_face1_receivedDatas    [0].getName(), data2.getName());
+//  BOOST_CHECK_EQUAL(m_face2_receivedInterests[0].getName(), interest1.getName());
+//  BOOST_CHECK_EQUAL(m_face2_receivedDatas    [0].getName(), data1.getName());
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+} // namespace nfd
diff --git a/tests/daemon/face/face.cpp b/tests/daemon/face/face.cpp
new file mode 100644
index 0000000..46c0345
--- /dev/null
+++ b/tests/daemon/face/face.cpp
@@ -0,0 +1,74 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014  Regents of the University of California,
+ *                     Arizona Board of Regents,
+ *                     Colorado State University,
+ *                     University Pierre & Marie Curie, Sorbonne University,
+ *                     Washington University in St. Louis,
+ *                     Beijing Institute of Technology
+ *
+ * This file is part of NFD (Named Data Networking Forwarding Daemon).
+ * See AUTHORS.md for complete list of NFD authors and contributors.
+ *
+ * NFD 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.
+ *
+ * NFD 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
+ * NFD, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+#include "face/face.hpp"
+#include "face/local-face.hpp"
+#include "dummy-face.hpp"
+
+#include "tests/test-common.hpp"
+
+namespace nfd {
+namespace tests {
+
+BOOST_FIXTURE_TEST_SUITE(FaceFace, BaseFixture)
+
+BOOST_AUTO_TEST_CASE(Description)
+{
+  DummyFace face;
+  face.setDescription("3pFsKrvWr");
+  BOOST_CHECK_EQUAL(face.getDescription(), "3pFsKrvWr");
+}
+
+BOOST_AUTO_TEST_CASE(LocalControlHeaderEnabled)
+{
+  DummyLocalFace face;
+
+  BOOST_CHECK_EQUAL(face.isLocalControlHeaderEnabled(), false);
+
+  face.setLocalControlHeaderFeature(LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID, true);
+  BOOST_CHECK_EQUAL(face.isLocalControlHeaderEnabled(), true);
+  BOOST_CHECK_EQUAL(face.isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID), true);
+  BOOST_CHECK_EQUAL(face.isLocalControlHeaderEnabled(
+                         LOCAL_CONTROL_FEATURE_NEXT_HOP_FACE_ID), false);
+
+  face.setLocalControlHeaderFeature(LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID, false);
+  BOOST_CHECK_EQUAL(face.isLocalControlHeaderEnabled(), false);
+  BOOST_CHECK_EQUAL(face.isLocalControlHeaderEnabled(
+                         LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID), false);
+}
+
+BOOST_AUTO_TEST_CASE(Counters)
+{
+  DummyFace face;
+  const FaceCounters& counters = face.getCounters();
+  BOOST_CHECK_EQUAL(counters.getNInInterests() , 0);
+  BOOST_CHECK_EQUAL(counters.getNInDatas()     , 0);
+  BOOST_CHECK_EQUAL(counters.getNOutInterests(), 0);
+  BOOST_CHECK_EQUAL(counters.getNOutDatas()    , 0);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+} // namespace nfd
diff --git a/tests/daemon/face/ndnlp.cpp b/tests/daemon/face/ndnlp.cpp
new file mode 100644
index 0000000..f5ba8e1
--- /dev/null
+++ b/tests/daemon/face/ndnlp.cpp
@@ -0,0 +1,232 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014  Regents of the University of California,
+ *                     Arizona Board of Regents,
+ *                     Colorado State University,
+ *                     University Pierre & Marie Curie, Sorbonne University,
+ *                     Washington University in St. Louis,
+ *                     Beijing Institute of Technology
+ *
+ * This file is part of NFD (Named Data Networking Forwarding Daemon).
+ * See AUTHORS.md for complete list of NFD authors and contributors.
+ *
+ * NFD 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.
+ *
+ * NFD 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
+ * NFD, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+#include "face/ndnlp-sequence-generator.hpp"
+#include "face/ndnlp-slicer.hpp"
+#include "face/ndnlp-partial-message-store.hpp"
+
+#include "tests/test-common.hpp"
+
+#include <boost/scoped_array.hpp>
+
+namespace nfd {
+namespace tests {
+
+BOOST_FIXTURE_TEST_SUITE(FaceNdnlp, BaseFixture)
+
+BOOST_AUTO_TEST_CASE(SequenceBlock)
+{
+  ndnlp::SequenceBlock sb(0x8000, 2);
+  BOOST_CHECK_EQUAL(sb.count(), 2);
+  BOOST_CHECK_EQUAL(sb[0], 0x8000);
+  BOOST_CHECK_EQUAL(sb[1], 0x8001);
+  BOOST_CHECK_THROW(sb[2], std::out_of_range);
+}
+
+// sequence number can safely wrap around
+BOOST_AUTO_TEST_CASE(SequenceBlockWrap)
+{
+  ndnlp::SequenceBlock sb(std::numeric_limits<uint64_t>::max(), 2);
+  BOOST_CHECK_EQUAL(sb[0], std::numeric_limits<uint64_t>::max());
+  BOOST_CHECK_EQUAL(sb[1], std::numeric_limits<uint64_t>::min());
+  BOOST_CHECK_EQUAL(sb[1] - sb[0], 1);
+}
+
+BOOST_AUTO_TEST_CASE(SequenceGenerator)
+{
+  ndnlp::SequenceGenerator seqgen;
+
+  ndnlp::SequenceBlock sb1 = seqgen.nextBlock(2);
+  BOOST_CHECK_EQUAL(sb1.count(), 2);
+
+  ndnlp::SequenceBlock sb2 = seqgen.nextBlock(1);
+  BOOST_CHECK_NE(sb1[0], sb2[0]);
+  BOOST_CHECK_NE(sb1[1], sb2[0]);
+}
+
+// slice a Block to one NDNLP packet
+BOOST_AUTO_TEST_CASE(Slice1)
+{
+  uint8_t blockValue[60];
+  memset(blockValue, 0xcc, sizeof(blockValue));
+  Block block = ndn::dataBlock(0x01, blockValue, sizeof(blockValue));
+
+  ndnlp::Slicer slicer(9000);
+  ndnlp::PacketArray pa = slicer.slice(block);
+
+  BOOST_REQUIRE_EQUAL(pa->size(), 1);
+
+  const Block& pkt = pa->at(0);
+  BOOST_CHECK_EQUAL(pkt.type(), static_cast<uint32_t>(tlv::NdnlpData));
+  pkt.parse();
+
+  const Block::element_container& elements = pkt.elements();
+  BOOST_REQUIRE_EQUAL(elements.size(), 2);
+
+  const Block& sequenceElement = elements[0];
+  BOOST_CHECK_EQUAL(sequenceElement.type(), static_cast<uint32_t>(tlv::NdnlpSequence));
+  BOOST_REQUIRE_EQUAL(sequenceElement.value_size(), sizeof(uint64_t));
+
+  const Block& payloadElement = elements[1];
+  BOOST_CHECK_EQUAL(payloadElement.type(), static_cast<uint32_t>(tlv::NdnlpPayload));
+  size_t payloadSize = payloadElement.value_size();
+  BOOST_CHECK_EQUAL(payloadSize, block.size());
+
+  BOOST_CHECK_EQUAL_COLLECTIONS(payloadElement.value_begin(), payloadElement.value_end(),
+                                block.begin(),                block.end());
+}
+
+// slice a Block to four NDNLP packets
+BOOST_AUTO_TEST_CASE(Slice4)
+{
+  uint8_t blockValue[5050];
+  memset(blockValue, 0xcc, sizeof(blockValue));
+  Block block = ndn::dataBlock(0x01, blockValue, sizeof(blockValue));
+
+  ndnlp::Slicer slicer(1500);
+  ndnlp::PacketArray pa = slicer.slice(block);
+
+  BOOST_REQUIRE_EQUAL(pa->size(), 4);
+
+  uint64_t seq0 = 0xdddd;
+
+  size_t totalPayloadSize = 0;
+
+  for (size_t i = 0; i < 4; ++i) {
+    const Block& pkt = pa->at(i);
+    BOOST_CHECK_EQUAL(pkt.type(), static_cast<uint32_t>(tlv::NdnlpData));
+    pkt.parse();
+
+    const Block::element_container& elements = pkt.elements();
+    BOOST_REQUIRE_EQUAL(elements.size(), 4);
+
+    const Block& sequenceElement = elements[0];
+    BOOST_CHECK_EQUAL(sequenceElement.type(), static_cast<uint32_t>(tlv::NdnlpSequence));
+    BOOST_REQUIRE_EQUAL(sequenceElement.value_size(), sizeof(uint64_t));
+    uint64_t seq = be64toh(*reinterpret_cast<const uint64_t*>(
+                             &*sequenceElement.value_begin()));
+    if (i == 0) {
+      seq0 = seq;
+    }
+    BOOST_CHECK_EQUAL(seq, seq0 + i);
+
+    const Block& fragIndexElement = elements[1];
+    BOOST_CHECK_EQUAL(fragIndexElement.type(), static_cast<uint32_t>(tlv::NdnlpFragIndex));
+    uint64_t fragIndex = ndn::readNonNegativeInteger(fragIndexElement);
+    BOOST_CHECK_EQUAL(fragIndex, i);
+
+    const Block& fragCountElement = elements[2];
+    BOOST_CHECK_EQUAL(fragCountElement.type(), static_cast<uint32_t>(tlv::NdnlpFragCount));
+    uint64_t fragCount = ndn::readNonNegativeInteger(fragCountElement);
+    BOOST_CHECK_EQUAL(fragCount, 4);
+
+    const Block& payloadElement = elements[3];
+    BOOST_CHECK_EQUAL(payloadElement.type(), static_cast<uint32_t>(tlv::NdnlpPayload));
+    size_t payloadSize = payloadElement.value_size();
+    totalPayloadSize += payloadSize;
+  }
+
+  BOOST_CHECK_EQUAL(totalPayloadSize, block.size());
+}
+
+class ReassembleFixture : protected BaseFixture
+{
+protected:
+  ReassembleFixture()
+    : m_slicer(1500)
+  {
+    m_partialMessageStore.onReceive +=
+      // push_back in C++11 has 2 overloads, and specific version needs to be selected
+      bind(static_cast<void (std::vector<Block>::*)(const Block&)>(&std::vector<Block>::push_back),
+           &m_received, _1);
+  }
+
+  Block
+  makeBlock(size_t valueLength)
+  {
+    boost::scoped_array<uint8_t> blockValue(new uint8_t[valueLength]);
+    memset(blockValue.get(), 0xcc, valueLength);
+    return ndn::dataBlock(0x01, blockValue.get(), valueLength);
+  }
+
+protected:
+  ndnlp::Slicer m_slicer;
+  ndnlp::PartialMessageStore m_partialMessageStore;
+
+  // received network layer packets
+  std::vector<Block> m_received;
+};
+
+// reassemble one NDNLP packets into one Block
+BOOST_FIXTURE_TEST_CASE(Reassemble1, ReassembleFixture)
+{
+  Block block = makeBlock(60);
+  ndnlp::PacketArray pa = m_slicer.slice(block);
+  BOOST_REQUIRE_EQUAL(pa->size(), 1);
+
+  BOOST_CHECK_EQUAL(m_received.size(), 0);
+  m_partialMessageStore.receiveNdnlpData(pa->at(0));
+
+  BOOST_REQUIRE_EQUAL(m_received.size(), 1);
+  BOOST_CHECK_EQUAL_COLLECTIONS(m_received.at(0).begin(), m_received.at(0).end(),
+                                block.begin(),            block.end());
+}
+
+// reassemble four and two NDNLP packets into two Blocks
+BOOST_FIXTURE_TEST_CASE(Reassemble4and2, ReassembleFixture)
+{
+  Block block = makeBlock(5050);
+  ndnlp::PacketArray pa = m_slicer.slice(block);
+  BOOST_REQUIRE_EQUAL(pa->size(), 4);
+
+  Block block2 = makeBlock(2000);
+  ndnlp::PacketArray pa2 = m_slicer.slice(block2);
+  BOOST_REQUIRE_EQUAL(pa2->size(), 2);
+
+  BOOST_CHECK_EQUAL(m_received.size(), 0);
+  m_partialMessageStore.receiveNdnlpData(pa->at(0));
+  BOOST_CHECK_EQUAL(m_received.size(), 0);
+  m_partialMessageStore.receiveNdnlpData(pa->at(1));
+  BOOST_CHECK_EQUAL(m_received.size(), 0);
+  m_partialMessageStore.receiveNdnlpData(pa2->at(1));
+  BOOST_CHECK_EQUAL(m_received.size(), 0);
+  m_partialMessageStore.receiveNdnlpData(pa->at(1));
+  BOOST_CHECK_EQUAL(m_received.size(), 0);
+  m_partialMessageStore.receiveNdnlpData(pa2->at(0));
+  BOOST_CHECK_EQUAL(m_received.size(), 1);
+  m_partialMessageStore.receiveNdnlpData(pa->at(3));
+  BOOST_CHECK_EQUAL(m_received.size(), 1);
+  m_partialMessageStore.receiveNdnlpData(pa->at(2));
+
+  BOOST_REQUIRE_EQUAL(m_received.size(), 2);
+  BOOST_CHECK_EQUAL_COLLECTIONS(m_received.at(1).begin(), m_received.at(1).end(),
+                                block.begin(),            block.end());
+  BOOST_CHECK_EQUAL_COLLECTIONS(m_received.at(0).begin(), m_received.at(0).end(),
+                                block2.begin(),           block2.end());
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+} // namespace nfd
diff --git a/tests/daemon/face/tcp.cpp b/tests/daemon/face/tcp.cpp
new file mode 100644
index 0000000..07e6ec5
--- /dev/null
+++ b/tests/daemon/face/tcp.cpp
@@ -0,0 +1,440 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014  Regents of the University of California,
+ *                     Arizona Board of Regents,
+ *                     Colorado State University,
+ *                     University Pierre & Marie Curie, Sorbonne University,
+ *                     Washington University in St. Louis,
+ *                     Beijing Institute of Technology
+ *
+ * This file is part of NFD (Named Data Networking Forwarding Daemon).
+ * See AUTHORS.md for complete list of NFD authors and contributors.
+ *
+ * NFD 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.
+ *
+ * NFD 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
+ * NFD, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+#include "face/tcp-factory.hpp"
+#include <ndn-cpp-dev/security/key-chain.hpp>
+
+#include "tests/test-common.hpp"
+#include "tests/limited-io.hpp"
+
+namespace nfd {
+namespace tests {
+
+BOOST_FIXTURE_TEST_SUITE(FaceTcp, BaseFixture)
+
+BOOST_AUTO_TEST_CASE(ChannelMap)
+{
+  TcpFactory factory;
+
+  shared_ptr<TcpChannel> channel1 = factory.createChannel("127.0.0.1", "20070");
+  shared_ptr<TcpChannel> channel1a = factory.createChannel("127.0.0.1", "20070");
+  BOOST_CHECK_EQUAL(channel1, channel1a);
+  BOOST_CHECK_EQUAL(channel1->getUri().toString(), "tcp4://127.0.0.1:20070");
+
+  shared_ptr<TcpChannel> channel2 = factory.createChannel("127.0.0.1", "20071");
+  BOOST_CHECK_NE(channel1, channel2);
+
+  shared_ptr<TcpChannel> channel3 = factory.createChannel("::1", "20071");
+  BOOST_CHECK_NE(channel2, channel3);
+  BOOST_CHECK_EQUAL(channel3->getUri().toString(), "tcp6://[::1]:20071");
+}
+
+class EndToEndFixture : protected BaseFixture
+{
+public:
+  void
+  channel1_onFaceCreated(const shared_ptr<Face>& newFace)
+  {
+    BOOST_CHECK(!static_cast<bool>(face1));
+    face1 = newFace;
+    face1->onReceiveInterest +=
+      bind(&EndToEndFixture::face1_onReceiveInterest, this, _1);
+    face1->onReceiveData +=
+      bind(&EndToEndFixture::face1_onReceiveData, this, _1);
+    face1->onFail +=
+      bind(&EndToEndFixture::face1_onFail, this);
+
+    limitedIo.afterOp();
+  }
+
+  void
+  channel1_onConnectFailed(const std::string& reason)
+  {
+    BOOST_CHECK_MESSAGE(false, reason);
+
+    limitedIo.afterOp();
+  }
+
+  void
+  face1_onReceiveInterest(const Interest& interest)
+  {
+    face1_receivedInterests.push_back(interest);
+
+    limitedIo.afterOp();
+  }
+
+  void
+  face1_onReceiveData(const Data& data)
+  {
+    face1_receivedDatas.push_back(data);
+
+    limitedIo.afterOp();
+  }
+
+  void
+  face1_onFail()
+  {
+    face1.reset();
+    limitedIo.afterOp();
+  }
+
+  void
+  channel2_onFaceCreated(const shared_ptr<Face>& newFace)
+  {
+    BOOST_CHECK(!static_cast<bool>(face2));
+    face2 = newFace;
+    face2->onReceiveInterest +=
+      bind(&EndToEndFixture::face2_onReceiveInterest, this, _1);
+    face2->onReceiveData +=
+      bind(&EndToEndFixture::face2_onReceiveData, this, _1);
+    face2->onFail +=
+      bind(&EndToEndFixture::face2_onFail, this);
+
+    limitedIo.afterOp();
+  }
+
+  void
+  channel2_onConnectFailed(const std::string& reason)
+  {
+    BOOST_CHECK_MESSAGE(false, reason);
+
+    limitedIo.afterOp();
+  }
+
+  void
+  face2_onReceiveInterest(const Interest& interest)
+  {
+    face2_receivedInterests.push_back(interest);
+
+    limitedIo.afterOp();
+  }
+
+  void
+  face2_onReceiveData(const Data& data)
+  {
+    face2_receivedDatas.push_back(data);
+
+    limitedIo.afterOp();
+  }
+
+  void
+  face2_onFail()
+  {
+    face2.reset();
+    limitedIo.afterOp();
+  }
+
+  void
+  channel_onFaceCreated(const shared_ptr<Face>& newFace)
+  {
+    faces.push_back(newFace);
+    limitedIo.afterOp();
+  }
+
+  void
+  channel_onConnectFailed(const std::string& reason)
+  {
+    BOOST_CHECK_MESSAGE(false, reason);
+
+    limitedIo.afterOp();
+  }
+
+  void
+  checkFaceList(size_t shouldBe)
+  {
+    BOOST_CHECK_EQUAL(faces.size(), shouldBe);
+  }
+
+public:
+  LimitedIo limitedIo;
+
+  shared_ptr<Face> face1;
+  std::vector<Interest> face1_receivedInterests;
+  std::vector<Data> face1_receivedDatas;
+  shared_ptr<Face> face2;
+  std::vector<Interest> face2_receivedInterests;
+  std::vector<Data> face2_receivedDatas;
+
+  std::list< shared_ptr<Face> > faces;
+};
+
+BOOST_FIXTURE_TEST_CASE(EndToEnd4, EndToEndFixture)
+{
+  TcpFactory factory1;
+
+  shared_ptr<TcpChannel> channel1 = factory1.createChannel("127.0.0.1", "20070");
+  factory1.createChannel("127.0.0.1", "20071");
+
+  BOOST_CHECK_EQUAL(channel1->isListening(), false);
+
+  channel1->listen(bind(&EndToEndFixture::channel1_onFaceCreated,   this, _1),
+                   bind(&EndToEndFixture::channel1_onConnectFailed, this, _1));
+
+  BOOST_CHECK_EQUAL(channel1->isListening(), true);
+
+  TcpFactory factory2;
+
+  shared_ptr<TcpChannel> channel2 = factory2.createChannel("127.0.0.2", "20070");
+  factory2.createChannel("127.0.0.2", "20071");
+
+  factory2.createFace(FaceUri("tcp://127.0.0.1:20070"),
+                      bind(&EndToEndFixture::channel2_onFaceCreated, this, _1),
+                      bind(&EndToEndFixture::channel2_onConnectFailed, this, _1));
+
+  BOOST_CHECK_MESSAGE(limitedIo.run(2, time::seconds(10)) == LimitedIo::EXCEED_OPS,
+                      "TcpChannel error: cannot connect or cannot accept connection");
+
+  BOOST_REQUIRE(static_cast<bool>(face1));
+  BOOST_REQUIRE(static_cast<bool>(face2));
+
+  BOOST_CHECK(face1->isOnDemand());
+  BOOST_CHECK(!face2->isOnDemand());
+
+  BOOST_CHECK_EQUAL(face2->getRemoteUri().toString(), "tcp4://127.0.0.1:20070");
+  BOOST_CHECK_EQUAL(face1->getLocalUri().toString(), "tcp4://127.0.0.1:20070");
+  // face1 has an unknown remoteUri, since the source port is automatically chosen by OS
+
+  BOOST_CHECK_EQUAL(face1->isLocal(), true);
+  BOOST_CHECK_EQUAL(face2->isLocal(), true);
+
+  BOOST_CHECK_EQUAL(static_cast<bool>(dynamic_pointer_cast<LocalFace>(face1)), true);
+  BOOST_CHECK_EQUAL(static_cast<bool>(dynamic_pointer_cast<LocalFace>(face2)), true);
+
+  // integrated tests needs to check that TcpFace for non-loopback fails these tests...
+
+  Interest interest1("ndn:/TpnzGvW9R");
+  Data     data1    ("ndn:/KfczhUqVix");
+  data1.setContent(0, 0);
+  Interest interest2("ndn:/QWiIMfj5sL");
+  Data     data2    ("ndn:/XNBV796f");
+  data2.setContent(0, 0);
+
+  ndn::SignatureSha256WithRsa fakeSignature;
+  fakeSignature.setValue(ndn::dataBlock(tlv::SignatureValue, reinterpret_cast<const uint8_t*>(0), 0));
+
+  // set fake signature on data1 and data2
+  data1.setSignature(fakeSignature);
+  data2.setSignature(fakeSignature);
+
+  face1->sendInterest(interest1);
+  face1->sendInterest(interest1);
+  face1->sendInterest(interest1);
+  face1->sendData    (data1    );
+  face2->sendInterest(interest2);
+  face2->sendData    (data2    );
+  face2->sendData    (data2    );
+  face2->sendData    (data2    );
+
+  BOOST_CHECK_MESSAGE(limitedIo.run(8, time::seconds(10)) == LimitedIo::EXCEED_OPS,
+                      "TcpChannel error: cannot send or receive Interest/Data packets");
+
+
+  BOOST_REQUIRE_EQUAL(face1_receivedInterests.size(), 1);
+  BOOST_REQUIRE_EQUAL(face1_receivedDatas    .size(), 3);
+  BOOST_REQUIRE_EQUAL(face2_receivedInterests.size(), 3);
+  BOOST_REQUIRE_EQUAL(face2_receivedDatas    .size(), 1);
+
+  BOOST_CHECK_EQUAL(face1_receivedInterests[0].getName(), interest2.getName());
+  BOOST_CHECK_EQUAL(face1_receivedDatas    [0].getName(), data2.getName());
+  BOOST_CHECK_EQUAL(face2_receivedInterests[0].getName(), interest1.getName());
+  BOOST_CHECK_EQUAL(face2_receivedDatas    [0].getName(), data1.getName());
+
+  const FaceCounters& counters1 = face1->getCounters();
+  BOOST_CHECK_EQUAL(counters1.getNInInterests() , 1);
+  BOOST_CHECK_EQUAL(counters1.getNInDatas()     , 3);
+  BOOST_CHECK_EQUAL(counters1.getNOutInterests(), 3);
+  BOOST_CHECK_EQUAL(counters1.getNOutDatas()    , 1);
+
+  const FaceCounters& counters2 = face2->getCounters();
+  BOOST_CHECK_EQUAL(counters2.getNInInterests() , 3);
+  BOOST_CHECK_EQUAL(counters2.getNInDatas()     , 1);
+  BOOST_CHECK_EQUAL(counters2.getNOutInterests(), 1);
+  BOOST_CHECK_EQUAL(counters2.getNOutDatas()    , 3);
+}
+
+BOOST_FIXTURE_TEST_CASE(EndToEnd6, EndToEndFixture)
+{
+  TcpFactory factory1;
+
+  shared_ptr<TcpChannel> channel1 = factory1.createChannel("::1", "20070");
+  shared_ptr<TcpChannel> channel2 = factory1.createChannel("::1", "20071");
+
+  channel1->listen(bind(&EndToEndFixture::channel1_onFaceCreated,   this, _1),
+                   bind(&EndToEndFixture::channel1_onConnectFailed, this, _1));
+
+  TcpFactory factory2;
+
+  factory2.createChannel("::2", "20070");
+
+  factory2.createFace(FaceUri("tcp://[::1]:20070"),
+                      bind(&EndToEndFixture::channel2_onFaceCreated, this, _1),
+                      bind(&EndToEndFixture::channel2_onConnectFailed, this, _1));
+
+  BOOST_CHECK_MESSAGE(limitedIo.run(2, time::seconds(10)) == LimitedIo::EXCEED_OPS,
+                      "TcpChannel error: cannot connect or cannot accept connection");
+
+  BOOST_REQUIRE(static_cast<bool>(face1));
+  BOOST_REQUIRE(static_cast<bool>(face2));
+
+  BOOST_CHECK_EQUAL(face2->getRemoteUri().toString(), "tcp6://[::1]:20070");
+  BOOST_CHECK_EQUAL(face1->getLocalUri().toString(), "tcp6://[::1]:20070");
+  // face1 has an unknown remoteUri, since the source port is automatically chosen by OS
+
+  BOOST_CHECK_EQUAL(face1->isLocal(), true);
+  BOOST_CHECK_EQUAL(face2->isLocal(), true);
+
+  BOOST_CHECK_EQUAL(static_cast<bool>(dynamic_pointer_cast<LocalFace>(face1)), true);
+  BOOST_CHECK_EQUAL(static_cast<bool>(dynamic_pointer_cast<LocalFace>(face2)), true);
+
+  // integrated tests needs to check that TcpFace for non-loopback fails these tests...
+
+  Interest interest1("ndn:/TpnzGvW9R");
+  Data     data1    ("ndn:/KfczhUqVix");
+  data1.setContent(0, 0);
+  Interest interest2("ndn:/QWiIMfj5sL");
+  Data     data2    ("ndn:/XNBV796f");
+  data2.setContent(0, 0);
+
+  ndn::SignatureSha256WithRsa fakeSignature;
+  fakeSignature.setValue(ndn::dataBlock(tlv::SignatureValue, reinterpret_cast<const uint8_t*>(0), 0));
+
+  // set fake signature on data1 and data2
+  data1.setSignature(fakeSignature);
+  data2.setSignature(fakeSignature);
+
+  face1->sendInterest(interest1);
+  face1->sendData    (data1    );
+  face2->sendInterest(interest2);
+  face2->sendData    (data2    );
+
+  BOOST_CHECK_MESSAGE(limitedIo.run(4, time::seconds(10)) == LimitedIo::EXCEED_OPS,
+                      "TcpChannel error: cannot send or receive Interest/Data packets");
+
+
+  BOOST_REQUIRE_EQUAL(face1_receivedInterests.size(), 1);
+  BOOST_REQUIRE_EQUAL(face1_receivedDatas    .size(), 1);
+  BOOST_REQUIRE_EQUAL(face2_receivedInterests.size(), 1);
+  BOOST_REQUIRE_EQUAL(face2_receivedDatas    .size(), 1);
+
+  BOOST_CHECK_EQUAL(face1_receivedInterests[0].getName(), interest2.getName());
+  BOOST_CHECK_EQUAL(face1_receivedDatas    [0].getName(), data2.getName());
+  BOOST_CHECK_EQUAL(face2_receivedInterests[0].getName(), interest1.getName());
+  BOOST_CHECK_EQUAL(face2_receivedDatas    [0].getName(), data1.getName());
+}
+
+BOOST_FIXTURE_TEST_CASE(MultipleAccepts, EndToEndFixture)
+{
+  TcpFactory factory;
+
+  shared_ptr<TcpChannel> channel1 = factory.createChannel("127.0.0.1", "20070");
+  shared_ptr<TcpChannel> channel2 = factory.createChannel("127.0.0.1", "20071");
+
+  channel1->listen(bind(&EndToEndFixture::channel_onFaceCreated,   this, _1),
+                   bind(&EndToEndFixture::channel_onConnectFailed, this, _1));
+
+  channel2->connect("127.0.0.1", "20070",
+                    bind(&EndToEndFixture::channel_onFaceCreated, this, _1),
+                    bind(&EndToEndFixture::channel_onConnectFailed, this, _1),
+                    time::seconds(4)); // very short timeout
+
+  BOOST_CHECK_MESSAGE(limitedIo.run(2, time::seconds(10)) == LimitedIo::EXCEED_OPS,
+                      "TcpChannel error: cannot connect or cannot accept connection");
+
+
+  BOOST_CHECK_EQUAL(faces.size(), 2);
+
+  shared_ptr<TcpChannel> channel3 = factory.createChannel("127.0.0.1", "20072");
+  channel3->connect("127.0.0.1", "20070",
+                    bind(&EndToEndFixture::channel_onFaceCreated, this, _1),
+                    bind(&EndToEndFixture::channel_onConnectFailed, this, _1),
+                    time::seconds(4)); // very short timeout
+
+
+  shared_ptr<TcpChannel> channel4 = factory.createChannel("127.0.0.1", "20073");
+
+  BOOST_CHECK_NE(channel3, channel4);
+
+  scheduler::schedule(time::seconds(1),
+      bind(&TcpChannel::connect, channel4, "127.0.0.1", "20070",
+          // does not work without static_cast
+           static_cast<TcpChannel::FaceCreatedCallback>(
+               bind(&EndToEndFixture::channel_onFaceCreated, this, _1)),
+           static_cast<TcpChannel::ConnectFailedCallback>(
+               bind(&EndToEndFixture::channel_onConnectFailed, this, _1)),
+           time::seconds(4)));
+
+  scheduler::schedule(time::milliseconds(500),
+                      bind(&EndToEndFixture::checkFaceList, this, 4));
+
+  BOOST_CHECK_MESSAGE(limitedIo.run(4,// 2 connects and 2 accepts
+                      time::seconds(10)) == LimitedIo::EXCEED_OPS,
+                      "TcpChannel error: cannot connect or cannot accept multiple connections");
+
+  BOOST_CHECK_EQUAL(faces.size(), 6);
+}
+
+
+BOOST_FIXTURE_TEST_CASE(FaceClosing, EndToEndFixture)
+{
+  TcpFactory factory;
+
+  shared_ptr<TcpChannel> channel1 = factory.createChannel("127.0.0.1", "20070");
+  shared_ptr<TcpChannel> channel2 = factory.createChannel("127.0.0.1", "20071");
+
+  channel1->listen(bind(&EndToEndFixture::channel1_onFaceCreated,   this, _1),
+                   bind(&EndToEndFixture::channel1_onConnectFailed, this, _1));
+
+  channel2->connect("127.0.0.1", "20070",
+                    bind(&EndToEndFixture::channel2_onFaceCreated, this, _1),
+                    bind(&EndToEndFixture::channel2_onConnectFailed, this, _1),
+                    time::seconds(4)); // very short timeout
+
+  BOOST_CHECK_MESSAGE(limitedIo.run(2, time::seconds(10)) == LimitedIo::EXCEED_OPS,
+                      "TcpChannel error: cannot connect or cannot accept connection");
+
+  BOOST_CHECK_EQUAL(channel1->size(), 1);
+  BOOST_CHECK_EQUAL(channel2->size(), 1);
+
+  BOOST_REQUIRE(static_cast<bool>(face1));
+  BOOST_CHECK(static_cast<bool>(face2));
+
+  // Face::close must be invoked during io run to be counted as an op
+  scheduler::schedule(time::milliseconds(100), bind(&Face::close, face1));
+
+  BOOST_CHECK_MESSAGE(limitedIo.run(2, time::seconds(10)) == LimitedIo::EXCEED_OPS,
+                      "FaceClosing error: cannot properly close faces");
+
+  // both faces should get closed
+  BOOST_CHECK(!static_cast<bool>(face1));
+  BOOST_CHECK(!static_cast<bool>(face2));
+
+  BOOST_CHECK_EQUAL(channel1->size(), 0);
+  BOOST_CHECK_EQUAL(channel2->size(), 0);
+}
+
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+} // namespace nfd
diff --git a/tests/daemon/face/udp.cpp b/tests/daemon/face/udp.cpp
new file mode 100644
index 0000000..2dfca3a
--- /dev/null
+++ b/tests/daemon/face/udp.cpp
@@ -0,0 +1,915 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014  Regents of the University of California,
+ *                     Arizona Board of Regents,
+ *                     Colorado State University,
+ *                     University Pierre & Marie Curie, Sorbonne University,
+ *                     Washington University in St. Louis,
+ *                     Beijing Institute of Technology
+ *
+ * This file is part of NFD (Named Data Networking Forwarding Daemon).
+ * See AUTHORS.md for complete list of NFD authors and contributors.
+ *
+ * NFD 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.
+ *
+ * NFD 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
+ * NFD, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+#include "face/udp-factory.hpp"
+
+#include "tests/test-common.hpp"
+#include "tests/limited-io.hpp"
+
+namespace nfd {
+namespace tests {
+
+BOOST_FIXTURE_TEST_SUITE(FaceUdp, BaseFixture)
+
+class FactoryErrorCheck : protected BaseFixture
+{
+public:
+  bool isTheSameMulticastEndpoint(const UdpFactory::Error& e) {
+    return strcmp(e.what(),
+                  "Cannot create the requested UDP unicast channel, local "
+                  "endpoint is already allocated for a UDP multicast face") == 0;
+  }
+
+  bool isNotMulticastAddress(const UdpFactory::Error& e) {
+    return strcmp(e.what(),
+                  "Cannot create the requested UDP multicast face, "
+                  "the multicast group given as input is not a multicast address") == 0;
+  }
+
+  bool isTheSameUnicastEndpoint(const UdpFactory::Error& e) {
+    return strcmp(e.what(),
+                  "Cannot create the requested UDP multicast face, local "
+                  "endpoint is already allocated for a UDP unicast channel") == 0;
+  }
+
+  bool isLocalEndpointOnDifferentGroup(const UdpFactory::Error& e) {
+    return strcmp(e.what(),
+                  "Cannot create the requested UDP multicast face, local "
+                  "endpoint is already allocated for a UDP multicast face "
+                  "on a different multicast group") == 0;
+  }
+};
+
+BOOST_FIXTURE_TEST_CASE(ChannelMapUdp, FactoryErrorCheck)
+{
+  using boost::asio::ip::udp;
+
+  UdpFactory factory = UdpFactory();
+
+  //to instantiate multicast face on a specific ip address, change interfaceIp
+  std::string interfaceIp = "0.0.0.0";
+
+  shared_ptr<UdpChannel> channel1 = factory.createChannel("127.0.0.1", "20070");
+  shared_ptr<UdpChannel> channel1a = factory.createChannel("127.0.0.1", "20070");
+  BOOST_CHECK_EQUAL(channel1, channel1a);
+  BOOST_CHECK_EQUAL(channel1->getUri().toString(), "udp4://127.0.0.1:20070");
+
+  shared_ptr<UdpChannel> channel2 = factory.createChannel("127.0.0.1", "20071");
+  BOOST_CHECK_NE(channel1, channel2);
+
+  shared_ptr<UdpChannel> channel3 = factory.createChannel(interfaceIp, "20070");
+
+  shared_ptr<UdpChannel> channel4 = factory.createChannel("::1", "20071");
+  BOOST_CHECK_NE(channel2, channel4);
+  BOOST_CHECK_EQUAL(channel4->getUri().toString(), "udp6://[::1]:20071");
+
+  //same endpoint of a unicast channel
+  BOOST_CHECK_EXCEPTION(factory.createMulticastFace(interfaceIp,
+                                                    "224.0.0.1",
+                                                    "20070"),
+                        UdpFactory::Error,
+                        isTheSameUnicastEndpoint);
+
+
+  shared_ptr<MulticastUdpFace> multicastFace1 = factory.createMulticastFace(interfaceIp,
+                                                                            "224.0.0.1",
+                                                                            "20072");
+  shared_ptr<MulticastUdpFace> multicastFace1a = factory.createMulticastFace(interfaceIp,
+                                                                            "224.0.0.1",
+                                                                            "20072");
+  BOOST_CHECK_EQUAL(multicastFace1, multicastFace1a);
+
+
+  //same endpoint of a multicast face
+  BOOST_CHECK_EXCEPTION(factory.createChannel(interfaceIp, "20072"),
+                        UdpFactory::Error,
+                        isTheSameMulticastEndpoint);
+
+  //same multicast endpoint, different group
+  BOOST_CHECK_EXCEPTION(factory.createMulticastFace(interfaceIp,
+                                                    "224.0.0.42",
+                                                    "20072"),
+                        UdpFactory::Error,
+                        isLocalEndpointOnDifferentGroup);
+
+  BOOST_CHECK_EXCEPTION(factory.createMulticastFace(interfaceIp,
+                                                    "192.168.10.15",
+                                                    "20025"),
+                        UdpFactory::Error,
+                        isNotMulticastAddress);
+
+
+//  //Test commented because it required to be run in a machine that can resolve ipv6 query
+//  shared_ptr<UdpChannel> channel1v6 = factory.createChannel(//"::1",
+//                                                     "fe80::5e96:9dff:fe7d:9c8d%en1",
+//                                                     //"fe80::aa54:b2ff:fe08:27b8%wlan0",
+//                                                     "20070");
+//
+//  //the creation of multicastFace2 works properly. It has been disable because it needs an IP address of
+//  //an available network interface (different from the first one used)
+//  shared_ptr<MulticastUdpFace> multicastFace2 = factory.createMulticastFace("192.168.1.17",
+//                                                                            "224.0.0.1",
+//                                                                            "20073");
+//  BOOST_CHECK_NE(multicastFace1, multicastFace2);
+//
+//
+//  //ipv6 - work in progress
+//  shared_ptr<MulticastUdpFace> multicastFace3 = factory.createMulticastFace("fe80::5e96:9dff:fe7d:9c8d%en1",
+//                                                                            "FF01:0:0:0:0:0:0:2",
+//                                                                            "20073");
+//
+//  shared_ptr<MulticastUdpFace> multicastFace4 = factory.createMulticastFace("fe80::aa54:b2ff:fe08:27b8%wlan0",
+//                                                                            "FF01:0:0:0:0:0:0:2",
+//                                                                            "20073");
+//
+//  BOOST_CHECK_EQUAL(multicastFace3, multicastFace4);
+//
+//  shared_ptr<MulticastUdpFace> multicastFace5 = factory.createMulticastFace("::1",
+//                                                                            "FF01:0:0:0:0:0:0:2",
+//                                                                            "20073");
+//
+//  BOOST_CHECK_NE(multicastFace3, multicastFace5);
+//
+//  //same local ipv6 endpoint for a different multicast group
+//  BOOST_CHECK_THROW(factory.createMulticastFace("fe80::aa54:b2ff:fe08:27b8%wlan0",
+//                                                "FE01:0:0:0:0:0:0:2",
+//                                                "20073"),
+//                    UdpFactory::Error);
+//
+//  //same local ipv6 (expect for th port number) endpoint for a different multicast group
+//  BOOST_CHECK_THROW(factory.createMulticastFace("fe80::aa54:b2ff:fe08:27b8%wlan0",
+//                                                "FE01:0:0:0:0:0:0:2",
+//                                                "20075"),
+//                    UdpFactory::Error);
+//
+//  BOOST_CHECK_THROW(factory.createMulticastFace("fa80::20a:9dff:fef6:12ff",
+//                                                "FE12:0:0:0:0:0:0:2",
+//                                                "20075"),
+//                    UdpFactory::Error);
+//
+//  //not a multicast ipv6
+//  BOOST_CHECK_THROW(factory.createMulticastFace("fa80::20a:9dff:fef6:12ff",
+//                                                "A112:0:0:0:0:0:0:2",
+//                                                "20075"),
+//                    UdpFactory::Error);
+}
+
+class EndToEndFixture : protected BaseFixture
+{
+public:
+  void
+  channel1_onFaceCreated(const shared_ptr<Face>& newFace)
+  {
+    BOOST_CHECK(!static_cast<bool>(face1));
+    channel1_onFaceCreatedNoCheck(newFace);
+  }
+
+  void
+  channel1_onFaceCreatedNoCheck(const shared_ptr<Face>& newFace)
+  {
+    face1 = newFace;
+    face1->onReceiveInterest +=
+      bind(&EndToEndFixture::face1_onReceiveInterest, this, _1);
+    face1->onReceiveData +=
+      bind(&EndToEndFixture::face1_onReceiveData, this, _1);
+    face1->onFail +=
+      bind(&EndToEndFixture::face1_onFail, this);
+    BOOST_CHECK_MESSAGE(true, "channel 1 face created");
+
+    faces.push_back(face1);
+
+    limitedIo.afterOp();
+  }
+
+  void
+  channel1_onConnectFailed(const std::string& reason)
+  {
+    BOOST_CHECK_MESSAGE(false, reason);
+
+    limitedIo.afterOp();
+  }
+
+  void
+  face1_onReceiveInterest(const Interest& interest)
+  {
+    face1_receivedInterests.push_back(interest);
+
+    limitedIo.afterOp();
+  }
+
+  void
+  face1_onReceiveData(const Data& data)
+  {
+    face1_receivedDatas.push_back(data);
+
+    limitedIo.afterOp();
+  }
+
+  void
+  face1_onFail()
+  {
+    face1.reset();
+    limitedIo.afterOp();
+  }
+
+  void
+  channel2_onFaceCreated(const shared_ptr<Face>& newFace)
+  {
+    BOOST_CHECK(!static_cast<bool>(face2));
+    face2 = newFace;
+    face2->onReceiveInterest +=
+      bind(&EndToEndFixture::face2_onReceiveInterest, this, _1);
+    face2->onReceiveData +=
+      bind(&EndToEndFixture::face2_onReceiveData, this, _1);
+    face2->onFail +=
+      bind(&EndToEndFixture::face2_onFail, this);
+
+    faces.push_back(face2);
+
+    BOOST_CHECK_MESSAGE(true, "channel 2 face created");
+    limitedIo.afterOp();
+  }
+
+  void
+  channel2_onConnectFailed(const std::string& reason)
+  {
+    BOOST_CHECK_MESSAGE(false, reason);
+
+    limitedIo.afterOp();
+  }
+
+  void
+  face2_onReceiveInterest(const Interest& interest)
+  {
+    face2_receivedInterests.push_back(interest);
+
+    limitedIo.afterOp();
+  }
+
+  void
+  face2_onReceiveData(const Data& data)
+  {
+    face2_receivedDatas.push_back(data);
+
+    limitedIo.afterOp();
+  }
+
+  void
+  face2_onFail()
+  {
+    face2.reset();
+    limitedIo.afterOp();
+  }
+
+  void
+  channel3_onFaceCreated(const shared_ptr<Face>& newFace)
+  {
+    BOOST_CHECK(!static_cast<bool>(face1));
+    face3 = newFace;
+    faces.push_back(newFace);
+
+    limitedIo.afterOp();
+  }
+
+  void
+  channel_onFaceCreated(const shared_ptr<Face>& newFace)
+  {
+    faces.push_back(newFace);
+    limitedIo.afterOp();
+  }
+
+  void
+  channel_onConnectFailed(const std::string& reason)
+  {
+    BOOST_CHECK_MESSAGE(false, reason);
+
+    limitedIo.afterOp();
+  }
+
+  void
+  channel_onConnectFailedOk(const std::string& reason)
+  {
+    //it's ok, it was supposed to fail
+    limitedIo.afterOp();
+  }
+
+  void
+  checkFaceList(size_t shouldBe)
+  {
+    BOOST_CHECK_EQUAL(faces.size(), shouldBe);
+  }
+
+public:
+  LimitedIo limitedIo;
+
+  shared_ptr<Face> face1;
+  std::vector<Interest> face1_receivedInterests;
+  std::vector<Data> face1_receivedDatas;
+  shared_ptr<Face> face2;
+  std::vector<Interest> face2_receivedInterests;
+  std::vector<Data> face2_receivedDatas;
+  shared_ptr<Face> face3;
+
+  std::list< shared_ptr<Face> > faces;
+};
+
+
+BOOST_FIXTURE_TEST_CASE(EndToEnd4, EndToEndFixture)
+{
+  UdpFactory factory;
+
+  factory.createChannel("127.0.0.1", "20071");
+
+  factory.createFace(FaceUri("udp4://127.0.0.1:20070"),
+                     bind(&EndToEndFixture::channel2_onFaceCreated, this, _1),
+                     bind(&EndToEndFixture::channel2_onConnectFailed, this, _1));
+
+
+  BOOST_CHECK_MESSAGE(limitedIo.run(1, time::seconds(1)) == LimitedIo::EXCEED_OPS,
+                      "UdpChannel error: cannot connect or cannot accept connection");
+
+  BOOST_REQUIRE(static_cast<bool>(face2));
+  BOOST_CHECK_EQUAL(face2->getRemoteUri().toString(), "udp4://127.0.0.1:20070");
+  BOOST_CHECK_EQUAL(face2->getLocalUri().toString(), "udp4://127.0.0.1:20071");
+  BOOST_CHECK_EQUAL(face2->isLocal(), false);
+  // face1 is not created yet
+
+  shared_ptr<UdpChannel> channel1 = factory.createChannel("127.0.0.1", "20070");
+  channel1->listen(bind(&EndToEndFixture::channel1_onFaceCreated,   this, _1),
+                   bind(&EndToEndFixture::channel1_onConnectFailed, this, _1));
+
+  Interest interest1("ndn:/TpnzGvW9R");
+  Data     data1    ("ndn:/KfczhUqVix");
+  data1.setContent(0, 0);
+  Interest interest2("ndn:/QWiIMfj5sL");
+  Data     data2    ("ndn:/XNBV796f");
+  data2.setContent(0, 0);
+  Interest interest3("ndn:/QWiIhjgkj5sL");
+  Data     data3    ("ndn:/XNBV794f");
+  data3.setContent(0, 0);
+
+
+  ndn::SignatureSha256WithRsa fakeSignature;
+  fakeSignature.setValue(ndn::dataBlock(tlv::SignatureValue,
+                                        reinterpret_cast<const uint8_t*>(0),
+                                        0));
+
+  // set fake signature on data1 and data2
+  data1.setSignature(fakeSignature);
+  data2.setSignature(fakeSignature);
+  data3.setSignature(fakeSignature);
+
+  face2->sendInterest(interest2);
+  face2->sendData    (data2    );
+  face2->sendData    (data2    );
+  face2->sendData    (data2    );
+
+  BOOST_CHECK_MESSAGE(limitedIo.run(5,//4 send + 1 listen return
+                      time::seconds(4)) == LimitedIo::EXCEED_OPS,
+                      "UdpChannel error: cannot send or receive Interest/Data packets");
+
+  BOOST_REQUIRE(static_cast<bool>(face1));
+  BOOST_CHECK_EQUAL(face1->getRemoteUri().toString(), "udp4://127.0.0.1:20071");
+  BOOST_CHECK_EQUAL(face1->getLocalUri().toString(), "udp4://127.0.0.1:20070");
+  BOOST_CHECK_EQUAL(face1->isLocal(), false);
+
+  face1->sendInterest(interest1);
+  face1->sendInterest(interest1);
+  face1->sendInterest(interest1);
+  face1->sendData    (data1    );
+
+  BOOST_CHECK_MESSAGE(limitedIo.run(4, time::seconds(4)) == LimitedIo::EXCEED_OPS,
+                      "UdpChannel error: cannot send or receive Interest/Data packets");
+
+
+  BOOST_REQUIRE_EQUAL(face1_receivedInterests.size(), 1);
+  BOOST_REQUIRE_EQUAL(face1_receivedDatas    .size(), 3);
+  BOOST_REQUIRE_EQUAL(face2_receivedInterests.size(), 3);
+  BOOST_REQUIRE_EQUAL(face2_receivedDatas    .size(), 1);
+
+  BOOST_CHECK_EQUAL(face1_receivedInterests[0].getName(), interest2.getName());
+  BOOST_CHECK_EQUAL(face1_receivedDatas    [0].getName(), data2.getName());
+  BOOST_CHECK_EQUAL(face2_receivedInterests[0].getName(), interest1.getName());
+  BOOST_CHECK_EQUAL(face2_receivedDatas    [0].getName(), data1.getName());
+
+
+
+  //checking if the connection accepting mechanism works properly.
+
+  face2->sendData    (data3    );
+  face2->sendInterest(interest3);
+
+  BOOST_CHECK_MESSAGE(limitedIo.run(2, time::seconds(1)) == LimitedIo::EXCEED_OPS,
+                      "UdpChannel error: cannot send or receive Interest/Data packets");
+
+  BOOST_REQUIRE_EQUAL(face1_receivedInterests.size(), 2);
+  BOOST_REQUIRE_EQUAL(face1_receivedDatas    .size(), 4);
+
+  BOOST_CHECK_EQUAL(face1_receivedInterests[1].getName(), interest3.getName());
+  BOOST_CHECK_EQUAL(face1_receivedDatas    [3].getName(), data3.getName());
+
+  const FaceCounters& counters1 = face1->getCounters();
+  BOOST_CHECK_EQUAL(counters1.getNInInterests() , 2);
+  BOOST_CHECK_EQUAL(counters1.getNInDatas()     , 4);
+  BOOST_CHECK_EQUAL(counters1.getNOutInterests(), 3);
+  BOOST_CHECK_EQUAL(counters1.getNOutDatas()    , 1);
+
+  const FaceCounters& counters2 = face2->getCounters();
+  BOOST_CHECK_EQUAL(counters2.getNInInterests() , 3);
+  BOOST_CHECK_EQUAL(counters2.getNInDatas()     , 1);
+  BOOST_CHECK_EQUAL(counters2.getNOutInterests(), 2);
+  BOOST_CHECK_EQUAL(counters2.getNOutDatas()    , 4);
+}
+
+BOOST_FIXTURE_TEST_CASE(EndToEnd6, EndToEndFixture)
+{
+  UdpFactory factory;
+
+  factory.createChannel("::1", "20071");
+
+  factory.createFace(FaceUri("udp://[::1]:20070"),
+                     bind(&EndToEndFixture::channel2_onFaceCreated, this, _1),
+                     bind(&EndToEndFixture::channel2_onConnectFailed, this, _1));
+
+
+  BOOST_CHECK_MESSAGE(limitedIo.run(1, time::seconds(1)) == LimitedIo::EXCEED_OPS,
+                      "UdpChannel error: cannot connect or cannot accept connection");
+
+  BOOST_REQUIRE(static_cast<bool>(face2));
+  BOOST_CHECK_EQUAL(face2->getRemoteUri().toString(), "udp6://[::1]:20070");
+  BOOST_CHECK_EQUAL(face2->getLocalUri().toString(), "udp6://[::1]:20071");
+  BOOST_CHECK_EQUAL(face2->isLocal(), false);
+  // face1 is not created yet
+
+  shared_ptr<UdpChannel> channel1 = factory.createChannel("::1", "20070");
+  channel1->listen(bind(&EndToEndFixture::channel1_onFaceCreated,   this, _1),
+                   bind(&EndToEndFixture::channel1_onConnectFailed, this, _1));
+
+  Interest interest1("ndn:/TpnzGvW9R");
+  Data     data1    ("ndn:/KfczhUqVix");
+  data1.setContent(0, 0);
+  Interest interest2("ndn:/QWiIMfj5sL");
+  Data     data2    ("ndn:/XNBV796f");
+  data2.setContent(0, 0);
+  Interest interest3("ndn:/QWiIhjgkj5sL");
+  Data     data3    ("ndn:/XNBV794f");
+  data3.setContent(0, 0);
+
+
+  ndn::SignatureSha256WithRsa fakeSignature;
+  fakeSignature.setValue(ndn::dataBlock(tlv::SignatureValue,
+                                        reinterpret_cast<const uint8_t*>(0),
+                                        0));
+
+  // set fake signature on data1 and data2
+  data1.setSignature(fakeSignature);
+  data2.setSignature(fakeSignature);
+  data3.setSignature(fakeSignature);
+
+  face2->sendInterest(interest2);
+  face2->sendData    (data2    );
+
+  BOOST_CHECK_MESSAGE(limitedIo.run(3,//2 send + 1 listen return
+                      time::seconds(1)) == LimitedIo::EXCEED_OPS,
+                      "UdpChannel error: cannot send or receive Interest/Data packets");
+
+  BOOST_REQUIRE(static_cast<bool>(face1));
+  BOOST_CHECK_EQUAL(face1->getRemoteUri().toString(), "udp6://[::1]:20071");
+  BOOST_CHECK_EQUAL(face1->getLocalUri().toString(), "udp6://[::1]:20070");
+  BOOST_CHECK_EQUAL(face1->isLocal(), false);
+
+  face1->sendInterest(interest1);
+  face1->sendData    (data1    );
+
+  BOOST_CHECK_MESSAGE(limitedIo.run(2, time::seconds(1)) == LimitedIo::EXCEED_OPS,
+                      "UdpChannel error: cannot send or receive Interest/Data packets");
+
+
+  BOOST_REQUIRE_EQUAL(face1_receivedInterests.size(), 1);
+  BOOST_REQUIRE_EQUAL(face1_receivedDatas    .size(), 1);
+  BOOST_REQUIRE_EQUAL(face2_receivedInterests.size(), 1);
+  BOOST_REQUIRE_EQUAL(face2_receivedDatas    .size(), 1);
+
+  BOOST_CHECK_EQUAL(face1_receivedInterests[0].getName(), interest2.getName());
+  BOOST_CHECK_EQUAL(face1_receivedDatas    [0].getName(), data2.getName());
+  BOOST_CHECK_EQUAL(face2_receivedInterests[0].getName(), interest1.getName());
+  BOOST_CHECK_EQUAL(face2_receivedDatas    [0].getName(), data1.getName());
+
+
+
+  //checking if the connection accepting mechanism works properly.
+
+  face2->sendData    (data3    );
+  face2->sendInterest(interest3);
+
+  BOOST_CHECK_MESSAGE(limitedIo.run(2, time::seconds(1)) == LimitedIo::EXCEED_OPS,
+                      "UdpChannel error: cannot send or receive Interest/Data packets");
+
+  BOOST_REQUIRE_EQUAL(face1_receivedInterests.size(), 2);
+  BOOST_REQUIRE_EQUAL(face1_receivedDatas    .size(), 2);
+
+  BOOST_CHECK_EQUAL(face1_receivedInterests[1].getName(), interest3.getName());
+  BOOST_CHECK_EQUAL(face1_receivedDatas    [1].getName(), data3.getName());
+}
+
+BOOST_FIXTURE_TEST_CASE(MultipleAccepts, EndToEndFixture)
+{
+  Interest interest1("ndn:/TpnzGvW9R");
+  Interest interest2("ndn:/QWiIMfj5sL");
+
+  UdpFactory factory;
+
+  shared_ptr<UdpChannel> channel1 = factory.createChannel("127.0.0.1", "20070");
+  shared_ptr<UdpChannel> channel2 = factory.createChannel("127.0.0.1", "20071");
+
+  channel1->listen(bind(&EndToEndFixture::channel_onFaceCreated,   this, _1),
+                   bind(&EndToEndFixture::channel_onConnectFailed, this, _1));
+
+  channel2->connect("127.0.0.1", "20070",
+                    bind(&EndToEndFixture::channel2_onFaceCreated, this, _1),
+                    bind(&EndToEndFixture::channel_onConnectFailed, this, _1));
+
+
+  BOOST_CHECK_MESSAGE(limitedIo.run(1, time::seconds(4)) == LimitedIo::EXCEED_OPS,
+                      "UdpChannel error: cannot connect or cannot accept connection");
+
+  BOOST_CHECK_EQUAL(faces.size(), 1);
+
+  shared_ptr<UdpChannel> channel3 = factory.createChannel("127.0.0.1", "20072");
+  channel3->connect("127.0.0.1", "20070",
+                    bind(&EndToEndFixture::channel3_onFaceCreated, this, _1),
+                    bind(&EndToEndFixture::channel_onConnectFailed, this, _1));
+
+  shared_ptr<UdpChannel> channel4 = factory.createChannel("127.0.0.1", "20073");
+
+  BOOST_CHECK_NE(channel3, channel4);
+
+  scheduler::schedule(time::milliseconds(500),
+           bind(&UdpChannel::connect, channel4, "127.0.0.1", "20070",
+                // does not work without static_cast
+                static_cast<UdpChannel::FaceCreatedCallback>(
+                    bind(&EndToEndFixture::channel_onFaceCreated, this, _1)),
+                static_cast<UdpChannel::ConnectFailedCallback>(
+                    bind(&EndToEndFixture::channel_onConnectFailed, this, _1))));
+
+  scheduler::schedule(time::milliseconds(400), bind(&EndToEndFixture::checkFaceList, this, 2));
+
+  BOOST_CHECK_MESSAGE(limitedIo.run(2,// 2 connects
+                      time::seconds(4)) == LimitedIo::EXCEED_OPS,
+                      "UdpChannel error: cannot connect or cannot accept multiple connections");
+
+  BOOST_CHECK_EQUAL(faces.size(), 3);
+
+
+  face2->sendInterest(interest1);
+  BOOST_CHECK_MESSAGE(limitedIo.run(1, time::seconds(1)) == LimitedIo::EXCEED_OPS,
+                      "UdpChannel error: cannot send or receive Interest/Data packets");
+
+  BOOST_CHECK_EQUAL(faces.size(), 4);
+
+  face3->sendInterest(interest2);
+  BOOST_CHECK_MESSAGE(limitedIo.run(1, time::seconds(1)) == LimitedIo::EXCEED_OPS,
+                      "UdpChannel error: cannot send or receive Interest/Data packets");
+
+  //channel1 should have created 2 faces, one when face2 sent an interest, one when face3 sent an interest
+  BOOST_CHECK_EQUAL(faces.size(), 5);
+  BOOST_CHECK_THROW(channel1->listen(bind(&EndToEndFixture::channel_onFaceCreated,     this, _1),
+                                     bind(&EndToEndFixture::channel_onConnectFailedOk, this, _1)),
+                    UdpChannel::Error);
+}
+
+//Test commented because it required to be run in a machine that can resolve ipv6 query
+//BOOST_FIXTURE_TEST_CASE(EndToEndIpv6, EndToEndFixture)
+//{
+//  UdpFactory factory = UdpFactory();
+//  Scheduler scheduler(g_io); // to limit the amount of time the test may take
+//
+//  EventId abortEvent =
+//  scheduler.scheduleEvent(time::seconds(1),
+//                          bind(&EndToEndFixture::abortTestCase, this,
+//                              "UdpChannel error: cannot connect or cannot accept connection"));
+//
+//  limitedIoRemaining = 1;
+//
+//  shared_ptr<UdpChannel> channel1 = factory.createChannel("::1", "20070");
+//  shared_ptr<UdpChannel> channel2 = factory.createChannel("::1", "20071");
+//
+//  channel1->listen(bind(&EndToEndFixture::channel1_onFaceCreated,   this, _1),
+//                   bind(&EndToEndFixture::channel1_onConnectFailed, this, _1));
+//
+//  channel2->connect("::1", "20070",
+//                    bind(&EndToEndFixture::channel2_onFaceCreated, this, _1),
+//                    bind(&EndToEndFixture::channel2_onConnectFailed, this, _1));
+//  g_io.run();
+//  g_io.reset();
+//  scheduler.cancelEvent(abortEvent);
+//
+//  BOOST_REQUIRE(static_cast<bool>(face2));
+//
+//  abortEvent =
+//  scheduler.scheduleEvent(time::seconds(1),
+//                          bind(&EndToEndFixture::abortTestCase, this,
+//                               "UdpChannel error: cannot send or receive Interest/Data packets"));
+//
+//  Interest interest1("ndn:/TpnzGvW9R");
+//  Data     data1    ("ndn:/KfczhUqVix");
+//  data1.setContent(0, 0);
+//  Interest interest2("ndn:/QWiIMfj5sL");
+//  Data     data2    ("ndn:/XNBV796f");
+//  data2.setContent(0, 0);
+//  Interest interest3("ndn:/QWiIhjgkj5sL");
+//  Data     data3    ("ndn:/XNBV794f");
+//  data3.setContent(0, 0);
+//
+//  ndn::SignatureSha256WithRsa fakeSignature;
+//  fakeSignature.setValue(ndn::dataBlock(tlv::SignatureValue, reinterpret_cast<const uint8_t*>(0), 0));
+//
+//  // set fake signature on data1 and data2
+//  data1.setSignature(fakeSignature);
+//  data2.setSignature(fakeSignature);
+//  data3.setSignature(fakeSignature);
+//
+//  face2->sendInterest(interest2);
+//  face2->sendData    (data2    );
+//
+//  limitedIoRemaining = 3; //2 send + 1 listen return
+//  g_io.run();
+//  g_io.reset();
+//  scheduler.cancelEvent(abortEvent);
+//
+//  BOOST_REQUIRE(static_cast<bool>(face1));
+//
+//  face1->sendInterest(interest1);
+//  face1->sendData    (data1    );
+//
+//  abortEvent =
+//  scheduler.scheduleEvent(time::seconds(1),
+//                          bind(&EndToEndFixture::abortTestCase, this,
+//                               "UdpChannel error: cannot send or receive Interest/Data packets"));
+//  limitedIoRemaining = 2;
+//  g_io.run();
+//  g_io.reset();
+//  scheduler.cancelEvent(abortEvent);
+//
+//  BOOST_REQUIRE_EQUAL(face1_receivedInterests.size(), 1);
+//  BOOST_REQUIRE_EQUAL(face1_receivedDatas    .size(), 1);
+//  BOOST_REQUIRE_EQUAL(face2_receivedInterests.size(), 1);
+//  BOOST_REQUIRE_EQUAL(face2_receivedDatas    .size(), 1);
+//
+//  BOOST_CHECK_EQUAL(face1_receivedInterests[0].getName(), interest2.getName());
+//  BOOST_CHECK_EQUAL(face1_receivedDatas    [0].getName(), data2.getName());
+//  BOOST_CHECK_EQUAL(face2_receivedInterests[0].getName(), interest1.getName());
+//  BOOST_CHECK_EQUAL(face2_receivedDatas    [0].getName(), data1.getName());
+//
+//  //checking if the connection accepting mechanism works properly.
+//
+//  face2->sendData    (data3    );
+//  face2->sendInterest(interest3);
+//
+//  abortEvent =
+//  scheduler.scheduleEvent(time::seconds(1),
+//                          bind(&EndToEndFixture::abortTestCase, this,
+//                               "UdpChannel error: cannot send or receive Interest/Data packets"));
+//  limitedIoRemaining = 2;
+//  g_io.run();
+//  g_io.reset();
+//  scheduler.cancelEvent(abortEvent);
+//
+//  BOOST_REQUIRE_EQUAL(face1_receivedInterests.size(), 2);
+//  BOOST_REQUIRE_EQUAL(face1_receivedDatas    .size(), 2);
+//
+//  BOOST_CHECK_EQUAL(face1_receivedInterests[1].getName(), interest3.getName());
+//  BOOST_CHECK_EQUAL(face1_receivedDatas    [1].getName(), data3.getName());
+//
+//}
+
+
+//Test commented because it required to be run in a machine that can resolve ipv6 query
+//BOOST_FIXTURE_TEST_CASE(MultipleAcceptsIpv6, EndToEndFixture)
+//{
+//  Interest interest1("ndn:/TpnzGvW9R");
+//  Interest interest2("ndn:/QWiIMfj5sL");
+//  Interest interest3("ndn:/QWiIhjgkj5sL");
+//
+//  UdpFactory factory = UdpFactory();
+//  Scheduler scheduler(g_io); // to limit the amount of time the test may take
+//
+//  EventId abortEvent =
+//  scheduler.scheduleEvent(time::seconds(4),
+//                          bind(&EndToEndFixture::abortTestCase, this,
+//                               "UdpChannel error: cannot connect or cannot accept connection"));
+//
+//  shared_ptr<UdpChannel> channel1 = factory.createChannel("::1", "20070");
+//  shared_ptr<UdpChannel> channel2 = factory.createChannel("::1", "20071");
+//
+//  channel1->listen(bind(&EndToEndFixture::channel_onFaceCreated,   this, _1),
+//                   bind(&EndToEndFixture::channel_onConnectFailed, this, _1));
+//
+//  channel2->connect("::1", "20070",
+//                    bind(&EndToEndFixture::channel2_onFaceCreated, this, _1),
+//                    bind(&EndToEndFixture::channel_onConnectFailed, this, _1));
+//
+//  limitedIoRemaining = 1;
+//  g_io.run();
+//  g_io.reset();
+//  scheduler.cancelEvent(abortEvent);
+//
+//  BOOST_CHECK_EQUAL(faces.size(), 1);
+//
+//  shared_ptr<UdpChannel> channel3 = factory.createChannel("::1", "20072");
+//  channel3->connect("::1", "20070",
+//                    bind(&EndToEndFixture::channel3_onFaceCreated, this, _1),
+//                    bind(&EndToEndFixture::channel_onConnectFailed, this, _1));
+//
+//  shared_ptr<UdpChannel> channel4 = factory.createChannel("::1", "20073");
+//
+//  BOOST_CHECK_NE(channel3, channel4);
+//
+//  scheduler
+//  .scheduleEvent(time::milliseconds(500),
+//                 bind(&UdpChannel::connect, channel4,
+//                      "::1", "20070",
+//                      static_cast<UdpChannel::FaceCreatedCallback>(bind(&EndToEndFixture::
+//                                                                        channel_onFaceCreated, this, _1)),
+//                      static_cast<UdpChannel::ConnectFailedCallback>(bind(&EndToEndFixture::
+//                                                                          channel_onConnectFailed, this, _1))));
+//
+//  limitedIoRemaining = 2; // 2 connects
+//  abortEvent =
+//  scheduler.scheduleEvent(time::seconds(4),
+//                          bind(&EndToEndFixture::abortTestCase, this,
+//                               "UdpChannel error: cannot connect or cannot accept multiple connections"));
+//
+//  scheduler.scheduleEvent(time::seconds(0.4),
+//                          bind(&EndToEndFixture::checkFaceList, this, 2));
+//
+//  g_io.run();
+//  g_io.reset();
+//
+//  BOOST_CHECK_EQUAL(faces.size(), 3);
+//
+//
+//  face2->sendInterest(interest1);
+//  abortEvent =
+//  scheduler.scheduleEvent(time::seconds(1),
+//                          bind(&EndToEndFixture::abortTestCase, this,
+//                               "UdpChannel error: cannot send or receive Interest/Data packets"));
+//  limitedIoRemaining = 1;
+//  g_io.run();
+//  g_io.reset();
+//  scheduler.cancelEvent(abortEvent);
+//
+//  BOOST_CHECK_EQUAL(faces.size(), 4);
+//
+//  face3->sendInterest(interest2);
+//  abortEvent =
+//  scheduler.scheduleEvent(time::seconds(1),
+//                          bind(&EndToEndFixture::abortTestCase, this,
+//                               "UdpChannel error: cannot send or receive Interest/Data packets"));
+//  limitedIoRemaining = 1;
+//  g_io.run();
+//  g_io.reset();
+//  scheduler.cancelEvent(abortEvent);
+//
+//  //channel1 should have created 2 faces, one when face2 sent an interest, one when face3 sent an interest
+//  BOOST_CHECK_EQUAL(faces.size(), 5);
+//  BOOST_CHECK_THROW(channel1->listen(bind(&EndToEndFixture::channel_onFaceCreated,
+//                                          this,
+//                                          _1),
+//                                      bind(&EndToEndFixture::channel_onConnectFailedOk,
+//                                          this,
+//                                          _1)),
+//                    UdpChannel::Error);
+//}
+
+
+BOOST_FIXTURE_TEST_CASE(FaceClosing, EndToEndFixture)
+{
+  UdpFactory factory = UdpFactory();
+
+  shared_ptr<UdpChannel> channel1 = factory.createChannel("127.0.0.1", "20070");
+  shared_ptr<UdpChannel> channel2 = factory.createChannel("127.0.0.1", "20071");
+
+  channel1->listen(bind(&EndToEndFixture::channel1_onFaceCreated,   this, _1),
+                   bind(&EndToEndFixture::channel1_onConnectFailed, this, _1));
+
+  channel2->connect("127.0.0.1", "20070",
+                    bind(&EndToEndFixture::channel2_onFaceCreated, this, _1),
+                    bind(&EndToEndFixture::channel2_onConnectFailed, this, _1));
+
+  BOOST_CHECK_MESSAGE(limitedIo.run(1, time::seconds(4)) == LimitedIo::EXCEED_OPS,
+                      "UdpChannel error: cannot connect or cannot accept connection");
+
+  BOOST_CHECK_EQUAL(channel2->size(), 1);
+
+  BOOST_CHECK(static_cast<bool>(face2));
+
+  // Face::close must be invoked during io run to be counted as an op
+  scheduler::schedule(time::milliseconds(100), bind(&Face::close, face2));
+
+  BOOST_CHECK_MESSAGE(limitedIo.run(1, time::seconds(4)) == LimitedIo::EXCEED_OPS,
+                      "FaceClosing error: cannot properly close faces");
+
+  BOOST_CHECK(!static_cast<bool>(face2));
+
+  BOOST_CHECK_EQUAL(channel2->size(), 0);
+}
+
+BOOST_FIXTURE_TEST_CASE(ClosingIdleFace, EndToEndFixture)
+{
+  Interest interest1("ndn:/TpnzGvW9R");
+  Interest interest2("ndn:/QWiIMfj5sL");
+
+  UdpFactory factory;
+
+  shared_ptr<UdpChannel> channel1 = factory.createChannel("127.0.0.1", "20070",
+                                                          time::seconds(2));
+  shared_ptr<UdpChannel> channel2 = factory.createChannel("127.0.0.1", "20071",
+                                                          time::seconds(2));
+
+  channel1->listen(bind(&EndToEndFixture::channel1_onFaceCreated,   this, _1),
+                   bind(&EndToEndFixture::channel1_onConnectFailed, this, _1));
+
+  channel2->connect("127.0.0.1", "20070",
+                    bind(&EndToEndFixture::channel2_onFaceCreated, this, _1),
+                    bind(&EndToEndFixture::channel2_onConnectFailed, this, _1));
+
+  BOOST_CHECK_MESSAGE(limitedIo.run(1, time::seconds(4)) == LimitedIo::EXCEED_OPS,
+                      "UdpChannel error: cannot connect or cannot accept connection");
+
+  face2->sendInterest(interest1);
+  BOOST_CHECK(!face2->isOnDemand());
+
+  BOOST_CHECK_MESSAGE(limitedIo.run(2,//1 send + 1 listen return
+                                      time::seconds(1)) == LimitedIo::EXCEED_OPS,
+                      "UdpChannel error: cannot send or receive Interest/Data packets");
+
+  BOOST_CHECK_EQUAL(faces.size(), 2);
+  BOOST_CHECK_MESSAGE(limitedIo.run(1, time::seconds(2)) == LimitedIo::EXCEED_TIME,
+                      "Idle face should be still open because has been used recently");
+  BOOST_CHECK_EQUAL(faces.size(), 2);
+  BOOST_CHECK_MESSAGE(limitedIo.run(1, time::seconds(4)) == LimitedIo::EXCEED_OPS,
+                      "Closing idle face error: face should be closed by now");
+
+  //the face on listen should be closed now
+  BOOST_CHECK_EQUAL(channel1->size(), 0);
+  //checking that face2 has not been closed
+  BOOST_CHECK_EQUAL(channel2->size(), 1);
+  BOOST_REQUIRE(static_cast<bool>(face2));
+
+  face2->sendInterest(interest2);
+  BOOST_CHECK_MESSAGE(limitedIo.run(2,//1 send + 1 listen return
+                                      time::seconds(1)) == LimitedIo::EXCEED_OPS,
+                      "UdpChannel error: cannot send or receive Interest/Data packets");
+  //channel1 should have created a new face by now
+  BOOST_CHECK_EQUAL(channel1->size(), 1);
+  BOOST_CHECK_EQUAL(channel2->size(), 1);
+  BOOST_REQUIRE(static_cast<bool>(face1));
+  BOOST_CHECK(face1->isOnDemand());
+
+  channel1->connect("127.0.0.1", "20071",
+                    bind(&EndToEndFixture::channel1_onFaceCreatedNoCheck, this, _1),
+                    bind(&EndToEndFixture::channel1_onConnectFailed, this, _1));
+
+  BOOST_CHECK_MESSAGE(limitedIo.run(1,//1 connect
+                                      time::seconds(1)) == LimitedIo::EXCEED_OPS,
+                      "UdpChannel error: cannot connect");
+
+  BOOST_CHECK(!face1->isOnDemand());
+
+  //the connect should have set face1 as permanent face,
+  //but it shouln't have created any additional faces
+  BOOST_CHECK_EQUAL(channel1->size(), 1);
+  BOOST_CHECK_EQUAL(channel2->size(), 1);
+  BOOST_CHECK_MESSAGE(limitedIo.run(1, time::seconds(4)) == LimitedIo::EXCEED_TIME,
+                      "Idle face should be still open because it's permanent now");
+  //both faces are permanent, nothing should have changed
+  BOOST_CHECK_EQUAL(channel1->size(), 1);
+  BOOST_CHECK_EQUAL(channel2->size(), 1);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+} // namespace nfd
diff --git a/tests/daemon/face/unix-stream.cpp b/tests/daemon/face/unix-stream.cpp
new file mode 100644
index 0000000..4181aa1
--- /dev/null
+++ b/tests/daemon/face/unix-stream.cpp
@@ -0,0 +1,423 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014  Regents of the University of California,
+ *                     Arizona Board of Regents,
+ *                     Colorado State University,
+ *                     University Pierre & Marie Curie, Sorbonne University,
+ *                     Washington University in St. Louis,
+ *                     Beijing Institute of Technology
+ *
+ * This file is part of NFD (Named Data Networking Forwarding Daemon).
+ * See AUTHORS.md for complete list of NFD authors and contributors.
+ *
+ * NFD 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.
+ *
+ * NFD 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
+ * NFD, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+#include "face/unix-stream-factory.hpp"
+
+#include "tests/test-common.hpp"
+#include "tests/limited-io.hpp"
+
+namespace nfd {
+namespace tests {
+
+using namespace boost::asio::local;
+
+#define CHANNEL_PATH1 "unix-stream-test.1.sock"
+#define CHANNEL_PATH2 "unix-stream-test.2.sock"
+
+BOOST_FIXTURE_TEST_SUITE(FaceUnixStream, BaseFixture)
+
+BOOST_AUTO_TEST_CASE(ChannelMap)
+{
+  UnixStreamFactory factory;
+
+  shared_ptr<UnixStreamChannel> channel1 = factory.createChannel(CHANNEL_PATH1);
+  shared_ptr<UnixStreamChannel> channel1a = factory.createChannel(CHANNEL_PATH1);
+  BOOST_CHECK_EQUAL(channel1, channel1a);
+  std::string channel1uri = channel1->getUri().toString();
+  BOOST_CHECK_EQUAL(channel1uri.find("unix:///"), 0); // third '/' is the path separator
+  BOOST_CHECK_EQUAL(channel1uri.rfind(CHANNEL_PATH1),
+                    channel1uri.size() - std::string(CHANNEL_PATH1).size());
+
+  shared_ptr<UnixStreamChannel> channel2 = factory.createChannel(CHANNEL_PATH2);
+  BOOST_CHECK_NE(channel1, channel2);
+}
+
+class EndToEndFixture : protected BaseFixture
+{
+public:
+  void
+  client_onConnect(const boost::system::error_code& error)
+  {
+    BOOST_CHECK_MESSAGE(!error, error.message());
+
+    limitedIo.afterOp();
+  }
+
+  void
+  channel1_onFaceCreated(const shared_ptr<Face>& newFace)
+  {
+    BOOST_CHECK(!static_cast<bool>(face1));
+    face1 = static_pointer_cast<UnixStreamFace>(newFace);
+    face1->onReceiveInterest +=
+      bind(&EndToEndFixture::face1_onReceiveInterest, this, _1);
+    face1->onReceiveData +=
+      bind(&EndToEndFixture::face1_onReceiveData, this, _1);
+
+    limitedIo.afterOp();
+  }
+
+  void
+  channel1_onConnectFailed(const std::string& reason)
+  {
+    BOOST_CHECK_MESSAGE(false, reason);
+
+    limitedIo.afterOp();
+  }
+
+  void
+  face1_onReceiveInterest(const Interest& interest)
+  {
+    face1_receivedInterests.push_back(interest);
+
+    limitedIo.afterOp();
+  }
+
+  void
+  face1_onReceiveData(const Data& data)
+  {
+    face1_receivedDatas.push_back(data);
+
+    limitedIo.afterOp();
+  }
+
+  void
+  face2_onReceiveInterest(const Interest& interest)
+  {
+    face2_receivedInterests.push_back(interest);
+
+    limitedIo.afterOp();
+  }
+
+  void
+  face2_onReceiveData(const Data& data)
+  {
+    face2_receivedDatas.push_back(data);
+
+    limitedIo.afterOp();
+  }
+
+  void
+  channel_onFaceCreated(const shared_ptr<Face>& newFace)
+  {
+    faces.push_back(static_pointer_cast<UnixStreamFace>(newFace));
+
+    limitedIo.afterOp();
+  }
+
+  void
+  channel_onConnectFailed(const std::string& reason)
+  {
+    BOOST_CHECK_MESSAGE(false, reason);
+
+    limitedIo.afterOp();
+  }
+
+protected:
+  LimitedIo limitedIo;
+
+  shared_ptr<UnixStreamFace> face1;
+  std::vector<Interest> face1_receivedInterests;
+  std::vector<Data> face1_receivedDatas;
+  shared_ptr<UnixStreamFace> face2;
+  std::vector<Interest> face2_receivedInterests;
+  std::vector<Data> face2_receivedDatas;
+
+  std::list< shared_ptr<UnixStreamFace> > faces;
+};
+
+
+BOOST_FIXTURE_TEST_CASE(EndToEnd, EndToEndFixture)
+{
+  UnixStreamFactory factory;
+
+  shared_ptr<UnixStreamChannel> channel1 = factory.createChannel(CHANNEL_PATH1);
+  channel1->listen(bind(&EndToEndFixture::channel1_onFaceCreated,   this, _1),
+                   bind(&EndToEndFixture::channel1_onConnectFailed, this, _1));
+
+  shared_ptr<stream_protocol::socket> client =
+      make_shared<stream_protocol::socket>(boost::ref(g_io));
+  client->async_connect(stream_protocol::endpoint(CHANNEL_PATH1),
+                        bind(&EndToEndFixture::client_onConnect, this, _1));
+
+  BOOST_CHECK_MESSAGE(limitedIo.run(2, time::seconds(1)) == LimitedIo::EXCEED_OPS,
+                      "UnixStreamChannel error: cannot connect or cannot accept connection");
+
+  BOOST_REQUIRE(static_cast<bool>(face1));
+
+  BOOST_CHECK_EQUAL(face1->getRemoteUri().getScheme(), "fd");
+  BOOST_CHECK_NO_THROW(boost::lexical_cast<int>(face1->getRemoteUri().getHost()));
+  std::string face1localUri = face1->getLocalUri().toString();
+  BOOST_CHECK_EQUAL(face1localUri.find("unix:///"), 0); // third '/' is the path separator
+  BOOST_CHECK_EQUAL(face1localUri.rfind(CHANNEL_PATH1),
+                    face1localUri.size() - std::string(CHANNEL_PATH1).size());
+
+  face2 = make_shared<UnixStreamFace>(client);
+  face2->onReceiveInterest +=
+    bind(&EndToEndFixture::face2_onReceiveInterest, this, _1);
+  face2->onReceiveData +=
+    bind(&EndToEndFixture::face2_onReceiveData, this, _1);
+
+  Interest interest1("ndn:/TpnzGvW9R");
+  Data     data1    ("ndn:/KfczhUqVix");
+  data1.setContent(0, 0);
+  Interest interest2("ndn:/QWiIMfj5sL");
+  Data     data2    ("ndn:/XNBV796f");
+  data2.setContent(0, 0);
+
+  ndn::SignatureSha256WithRsa fakeSignature;
+  fakeSignature.setValue(ndn::dataBlock(tlv::SignatureValue, reinterpret_cast<const uint8_t*>(0), 0));
+
+  // set fake signature on data1 and data2
+  data1.setSignature(fakeSignature);
+  data2.setSignature(fakeSignature);
+
+  face1->sendInterest(interest1);
+  face1->sendInterest(interest1);
+  face1->sendInterest(interest1);
+  face1->sendData    (data1    );
+  face2->sendInterest(interest2);
+  face2->sendData    (data2    );
+  face2->sendData    (data2    );
+  face2->sendData    (data2    );
+
+  BOOST_CHECK_MESSAGE(limitedIo.run(8, time::seconds(1)) == LimitedIo::EXCEED_OPS,
+                      "UnixStreamChannel error: cannot send or receive Interest/Data packets");
+
+  BOOST_REQUIRE_EQUAL(face1_receivedInterests.size(), 1);
+  BOOST_REQUIRE_EQUAL(face1_receivedDatas    .size(), 3);
+  BOOST_REQUIRE_EQUAL(face2_receivedInterests.size(), 3);
+  BOOST_REQUIRE_EQUAL(face2_receivedDatas    .size(), 1);
+
+  BOOST_CHECK_EQUAL(face1_receivedInterests[0].getName(), interest2.getName());
+  BOOST_CHECK_EQUAL(face1_receivedDatas    [0].getName(), data2.getName());
+  BOOST_CHECK_EQUAL(face2_receivedInterests[0].getName(), interest1.getName());
+  BOOST_CHECK_EQUAL(face2_receivedDatas    [0].getName(), data1.getName());
+
+  const FaceCounters& counters1 = face1->getCounters();
+  BOOST_CHECK_EQUAL(counters1.getNInInterests() , 1);
+  BOOST_CHECK_EQUAL(counters1.getNInDatas()     , 3);
+  BOOST_CHECK_EQUAL(counters1.getNOutInterests(), 3);
+  BOOST_CHECK_EQUAL(counters1.getNOutDatas()    , 1);
+
+  const FaceCounters& counters2 = face2->getCounters();
+  BOOST_CHECK_EQUAL(counters2.getNInInterests() , 3);
+  BOOST_CHECK_EQUAL(counters2.getNInDatas()     , 1);
+  BOOST_CHECK_EQUAL(counters2.getNOutInterests(), 1);
+  BOOST_CHECK_EQUAL(counters2.getNOutDatas()    , 3);
+}
+
+BOOST_FIXTURE_TEST_CASE(MultipleAccepts, EndToEndFixture)
+{
+  UnixStreamFactory factory;
+
+  shared_ptr<UnixStreamChannel> channel = factory.createChannel(CHANNEL_PATH1);
+  channel->listen(bind(&EndToEndFixture::channel_onFaceCreated,   this, _1),
+                  bind(&EndToEndFixture::channel_onConnectFailed, this, _1));
+
+  shared_ptr<stream_protocol::socket> client1 =
+      make_shared<stream_protocol::socket>(boost::ref(g_io));
+  client1->async_connect(stream_protocol::endpoint(CHANNEL_PATH1),
+                         bind(&EndToEndFixture::client_onConnect, this, _1));
+
+  BOOST_CHECK_MESSAGE(limitedIo.run(2, time::seconds(1)) == LimitedIo::EXCEED_OPS,
+                      "UnixStreamChannel error: cannot connect or cannot accept connection");
+
+  BOOST_CHECK_EQUAL(faces.size(), 1);
+
+  shared_ptr<stream_protocol::socket> client2 =
+      make_shared<stream_protocol::socket>(boost::ref(g_io));
+  client2->async_connect(stream_protocol::endpoint(CHANNEL_PATH1),
+                         bind(&EndToEndFixture::client_onConnect, this, _1));
+
+  BOOST_CHECK_MESSAGE(limitedIo.run(2, time::seconds(1)) == LimitedIo::EXCEED_OPS,
+                      "UnixStreamChannel error: cannot accept multiple connections");
+
+  BOOST_CHECK_EQUAL(faces.size(), 2);
+
+  // now close one of the faces
+  faces.front()->close();
+
+  // we should still be able to send/receive with the other one
+  face1 = faces.back();
+  face1->onReceiveInterest += bind(&EndToEndFixture::face1_onReceiveInterest, this, _1);
+  face1->onReceiveData += bind(&EndToEndFixture::face1_onReceiveData, this, _1);
+
+  face2 = make_shared<UnixStreamFace>(client2);
+  face2->onReceiveInterest += bind(&EndToEndFixture::face2_onReceiveInterest, this, _1);
+  face2->onReceiveData += bind(&EndToEndFixture::face2_onReceiveData, this, _1);
+
+  Interest interest1("ndn:/TpnzGvW9R");
+  Data     data1    ("ndn:/KfczhUqVix");
+  data1.setContent(0, 0);
+  Interest interest2("ndn:/QWiIMfj5sL");
+  Data     data2    ("ndn:/XNBV796f");
+  data2.setContent(0, 0);
+
+  ndn::SignatureSha256WithRsa fakeSignature;
+  fakeSignature.setValue(ndn::dataBlock(tlv::SignatureValue, reinterpret_cast<const uint8_t*>(0), 0));
+
+  // set fake signature on data1 and data2
+  data1.setSignature(fakeSignature);
+  data2.setSignature(fakeSignature);
+
+  face1->sendInterest(interest1);
+  face1->sendData    (data1    );
+  face2->sendInterest(interest2);
+  face2->sendData    (data2    );
+
+  BOOST_CHECK_MESSAGE(limitedIo.run(4, time::seconds(1)) == LimitedIo::EXCEED_OPS,
+                      "UnixStreamChannel error: cannot send or receive Interest/Data packets");
+
+  BOOST_REQUIRE_EQUAL(face1_receivedInterests.size(), 1);
+  BOOST_REQUIRE_EQUAL(face1_receivedDatas    .size(), 1);
+  BOOST_REQUIRE_EQUAL(face2_receivedInterests.size(), 1);
+  BOOST_REQUIRE_EQUAL(face2_receivedDatas    .size(), 1);
+
+  BOOST_CHECK_EQUAL(face1_receivedInterests[0].getName(), interest2.getName());
+  BOOST_CHECK_EQUAL(face1_receivedDatas    [0].getName(), data2.getName());
+  BOOST_CHECK_EQUAL(face2_receivedInterests[0].getName(), interest1.getName());
+  BOOST_CHECK_EQUAL(face2_receivedDatas    [0].getName(), data1.getName());
+}
+
+static inline void
+noOp()
+{
+}
+
+BOOST_FIXTURE_TEST_CASE(UnixStreamFaceLocalControlHeader, EndToEndFixture)
+{
+  UnixStreamFactory factory;
+
+  shared_ptr<UnixStreamChannel> channel1 = factory.createChannel(CHANNEL_PATH1);
+  channel1->listen(bind(&EndToEndFixture::channel1_onFaceCreated,   this, _1),
+                   bind(&EndToEndFixture::channel1_onConnectFailed, this, _1));
+
+  shared_ptr<stream_protocol::socket> client =
+      make_shared<stream_protocol::socket>(boost::ref(g_io));
+  client->async_connect(stream_protocol::endpoint(CHANNEL_PATH1),
+                        bind(&EndToEndFixture::client_onConnect, this, _1));
+
+  BOOST_CHECK_MESSAGE(limitedIo.run(2, time::seconds(1)) == LimitedIo::EXCEED_OPS,
+                      "UnixStreamChannel error: cannot connect or cannot accept connection");
+
+  BOOST_REQUIRE(static_cast<bool>(face1));
+
+  face2 = make_shared<UnixStreamFace>(client);
+  face2->onReceiveInterest +=
+    bind(&EndToEndFixture::face2_onReceiveInterest, this, _1);
+  face2->onReceiveData +=
+    bind(&EndToEndFixture::face2_onReceiveData, this, _1);
+
+  Interest interest1("ndn:/TpnzGvW9R");
+  Data     data1    ("ndn:/KfczhUqVix");
+  data1.setContent(0, 0);
+  Interest interest2("ndn:/QWiIMfj5sL");
+  Data     data2    ("ndn:/XNBV796f");
+  data2.setContent(0, 0);
+
+  ndn::SignatureSha256WithRsa fakeSignature;
+  fakeSignature.setValue(ndn::dataBlock(tlv::SignatureValue, reinterpret_cast<const uint8_t*>(0), 0));
+
+  // set fake signature on data1 and data2
+  data1.setSignature(fakeSignature);
+  data2.setSignature(fakeSignature);
+
+  face1->setLocalControlHeaderFeature(LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID);
+  face1->setLocalControlHeaderFeature(LOCAL_CONTROL_FEATURE_NEXT_HOP_FACE_ID);
+
+  BOOST_CHECK(face1->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID));
+  BOOST_CHECK(face1->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_NEXT_HOP_FACE_ID));
+
+  face2->setLocalControlHeaderFeature(LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID);
+  face2->setLocalControlHeaderFeature(LOCAL_CONTROL_FEATURE_NEXT_HOP_FACE_ID);
+
+  BOOST_CHECK(face2->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID));
+  BOOST_CHECK(face2->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_NEXT_HOP_FACE_ID));
+
+  ////////////////////////////////////////////////////////
+
+  interest1.setIncomingFaceId(11);
+  interest1.setNextHopFaceId(111);
+
+  face1->sendInterest(interest1);
+
+  data1.setIncomingFaceId(22);
+  data1.getLocalControlHeader().setNextHopFaceId(222);
+
+  face1->sendData    (data1);
+
+  //
+
+  BOOST_CHECK_MESSAGE(limitedIo.run(2, time::seconds(1)) == LimitedIo::EXCEED_OPS,
+                      "UnixStreamChannel error: cannot send or receive Interest/Data packets");
+
+  BOOST_REQUIRE_EQUAL(face2_receivedInterests.size(), 1);
+  BOOST_REQUIRE_EQUAL(face2_receivedDatas    .size(), 1);
+
+  // sending allows only IncomingFaceId, receiving allows only NextHopFaceId
+  BOOST_CHECK_EQUAL(face2_receivedInterests[0].getLocalControlHeader().hasIncomingFaceId(), false);
+  BOOST_CHECK_EQUAL(face2_receivedInterests[0].getLocalControlHeader().hasNextHopFaceId(), false);
+
+  BOOST_CHECK_EQUAL(face2_receivedDatas[0].getLocalControlHeader().hasIncomingFaceId(), false);
+  BOOST_CHECK_EQUAL(face2_receivedDatas[0].getLocalControlHeader().hasNextHopFaceId(), false);
+
+  ////////////////////////////////////////////////////////
+
+  using namespace boost::asio;
+
+  std::vector<const_buffer> interestWithHeader;
+  Block iHeader  = interest1.getLocalControlHeader().wireEncode(interest1, true, true);
+  Block iPayload = interest1.wireEncode();
+  interestWithHeader.push_back(buffer(iHeader.wire(),  iHeader.size()));
+  interestWithHeader.push_back(buffer(iPayload.wire(), iPayload.size()));
+
+  std::vector<const_buffer> dataWithHeader;
+  Block dHeader  = data1.getLocalControlHeader().wireEncode(data1, true, true);
+  Block dPayload = data1.wireEncode();
+  dataWithHeader.push_back(buffer(dHeader.wire(),  dHeader.size()));
+  dataWithHeader.push_back(buffer(dPayload.wire(), dPayload.size()));
+
+  //
+
+  client->async_send(interestWithHeader, bind(&noOp));
+  client->async_send(dataWithHeader, bind(&noOp));
+
+  BOOST_CHECK_MESSAGE(limitedIo.run(2, time::seconds(1)) == LimitedIo::EXCEED_OPS,
+                      "UnixStreamChannel error: cannot send or receive Interest/Data packets");
+
+  BOOST_REQUIRE_EQUAL(face1_receivedInterests.size(), 1);
+  BOOST_REQUIRE_EQUAL(face1_receivedDatas    .size(), 1);
+
+  BOOST_CHECK_EQUAL(face1_receivedInterests[0].getLocalControlHeader().hasIncomingFaceId(), false);
+  BOOST_CHECK_EQUAL(face1_receivedInterests[0].getLocalControlHeader().hasNextHopFaceId(), true);
+  BOOST_CHECK_EQUAL(face1_receivedInterests[0].getNextHopFaceId(), 111);
+
+  BOOST_CHECK_EQUAL(face1_receivedDatas[0].getLocalControlHeader().hasIncomingFaceId(), false);
+  BOOST_CHECK_EQUAL(face1_receivedDatas[0].getLocalControlHeader().hasNextHopFaceId(), false);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+} // namespace nfd