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
diff --git a/tests/daemon/fw/broadcast-strategy.cpp b/tests/daemon/fw/broadcast-strategy.cpp
new file mode 100644
index 0000000..cde9b44
--- /dev/null
+++ b/tests/daemon/fw/broadcast-strategy.cpp
@@ -0,0 +1,129 @@
+/* -*- 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 "fw/broadcast-strategy.hpp"
+#include "strategy-tester.hpp"
+#include "tests/daemon/face/dummy-face.hpp"
+
+#include "tests/test-common.hpp"
+
+namespace nfd {
+namespace tests {
+
+BOOST_FIXTURE_TEST_SUITE(FwBroadcastStrategy, BaseFixture)
+
+BOOST_AUTO_TEST_CASE(Forward2)
+{
+ Forwarder forwarder;
+ typedef StrategyTester<fw::BroadcastStrategy> BroadcastStrategyTester;
+ BroadcastStrategyTester strategy(forwarder);
+
+ shared_ptr<DummyFace> face1 = make_shared<DummyFace>();
+ shared_ptr<DummyFace> face2 = make_shared<DummyFace>();
+ shared_ptr<DummyFace> face3 = make_shared<DummyFace>();
+ forwarder.addFace(face1);
+ forwarder.addFace(face2);
+ forwarder.addFace(face3);
+
+ Fib& fib = forwarder.getFib();
+ shared_ptr<fib::Entry> fibEntry = fib.insert(Name()).first;
+ fibEntry->addNextHop(face1, 0);
+ fibEntry->addNextHop(face2, 0);
+ fibEntry->addNextHop(face3, 0);
+
+ shared_ptr<Interest> interest = makeInterest("ndn:/H0D6i5fc");
+ Pit& pit = forwarder.getPit();
+ shared_ptr<pit::Entry> pitEntry = pit.insert(*interest).first;
+ pitEntry->insertOrUpdateInRecord(face3, *interest);
+
+ strategy.afterReceiveInterest(*face3, *interest, fibEntry, pitEntry);
+ BOOST_CHECK_EQUAL(strategy.m_rejectPendingInterestHistory.size(), 0);
+ BOOST_CHECK_EQUAL(strategy.m_sendInterestHistory.size(), 2);
+ bool hasFace1 = false;
+ bool hasFace2 = false;
+ for (std::vector<BroadcastStrategyTester::SendInterestArgs>::iterator it =
+ strategy.m_sendInterestHistory.begin();
+ it != strategy.m_sendInterestHistory.end(); ++it) {
+ if (it->get<1>() == face1) {
+ hasFace1 = true;
+ }
+ if (it->get<1>() == face2) {
+ hasFace2 = true;
+ }
+ }
+ BOOST_CHECK(hasFace1 && hasFace2);
+}
+
+BOOST_AUTO_TEST_CASE(RejectScope)
+{
+ Forwarder forwarder;
+ typedef StrategyTester<fw::BroadcastStrategy> BroadcastStrategyTester;
+ BroadcastStrategyTester strategy(forwarder);
+
+ shared_ptr<DummyFace> face1 = make_shared<DummyFace>();
+ shared_ptr<DummyFace> face2 = make_shared<DummyFace>();
+ forwarder.addFace(face1);
+ forwarder.addFace(face2);
+
+ Fib& fib = forwarder.getFib();
+ shared_ptr<fib::Entry> fibEntry = fib.insert("ndn:/localhop/uS09bub6tm").first;
+ fibEntry->addNextHop(face2, 0);
+
+ shared_ptr<Interest> interest = makeInterest("ndn:/localhop/uS09bub6tm/eG3MMoP6z");
+ Pit& pit = forwarder.getPit();
+ shared_ptr<pit::Entry> pitEntry = pit.insert(*interest).first;
+ pitEntry->insertOrUpdateInRecord(face1, *interest);
+
+ strategy.afterReceiveInterest(*face1, *interest, fibEntry, pitEntry);
+ BOOST_CHECK_EQUAL(strategy.m_rejectPendingInterestHistory.size(), 1);
+ BOOST_CHECK_EQUAL(strategy.m_sendInterestHistory.size(), 0);
+}
+
+BOOST_AUTO_TEST_CASE(RejectLoopback)
+{
+ Forwarder forwarder;
+ typedef StrategyTester<fw::BroadcastStrategy> BroadcastStrategyTester;
+ BroadcastStrategyTester strategy(forwarder);
+
+ shared_ptr<DummyFace> face1 = make_shared<DummyFace>();
+ forwarder.addFace(face1);
+
+ Fib& fib = forwarder.getFib();
+ shared_ptr<fib::Entry> fibEntry = fib.insert(Name()).first;
+ fibEntry->addNextHop(face1, 0);
+
+ shared_ptr<Interest> interest = makeInterest("ndn:/H0D6i5fc");
+ Pit& pit = forwarder.getPit();
+ shared_ptr<pit::Entry> pitEntry = pit.insert(*interest).first;
+ pitEntry->insertOrUpdateInRecord(face1, *interest);
+
+ strategy.afterReceiveInterest(*face1, *interest, fibEntry, pitEntry);
+ BOOST_CHECK_EQUAL(strategy.m_rejectPendingInterestHistory.size(), 1);
+ BOOST_CHECK_EQUAL(strategy.m_sendInterestHistory.size(), 0);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+} // namespace nfd
diff --git a/tests/daemon/fw/client-control-strategy.cpp b/tests/daemon/fw/client-control-strategy.cpp
new file mode 100644
index 0000000..c76fb52
--- /dev/null
+++ b/tests/daemon/fw/client-control-strategy.cpp
@@ -0,0 +1,96 @@
+/* -*- 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 "fw/client-control-strategy.hpp"
+#include "strategy-tester.hpp"
+#include "tests/daemon/face/dummy-face.hpp"
+
+#include "tests/test-common.hpp"
+
+namespace nfd {
+namespace tests {
+
+BOOST_FIXTURE_TEST_SUITE(FwClientControlStrategy, BaseFixture)
+
+BOOST_AUTO_TEST_CASE(Forward3)
+{
+ Forwarder forwarder;
+ typedef StrategyTester<fw::ClientControlStrategy> ClientControlStrategyTester;
+ ClientControlStrategyTester strategy(forwarder);
+
+ shared_ptr<DummyFace> face1 = make_shared<DummyFace>();
+ shared_ptr<DummyFace> face2 = make_shared<DummyFace>();
+ shared_ptr<DummyFace> face3 = make_shared<DummyFace>();
+ shared_ptr<DummyLocalFace> face4 = make_shared<DummyLocalFace>();
+ face4->setLocalControlHeaderFeature(LOCAL_CONTROL_FEATURE_NEXT_HOP_FACE_ID);
+ forwarder.addFace(face1);
+ forwarder.addFace(face2);
+ forwarder.addFace(face3);
+ forwarder.addFace(face4);
+
+ Fib& fib = forwarder.getFib();
+ shared_ptr<fib::Entry> fibEntry = fib.insert(Name()).first;
+ fibEntry->addNextHop(face2, 0);
+
+ Pit& pit = forwarder.getPit();
+
+ // Interest with valid NextHopFaceId
+ shared_ptr<Interest> interest1 = makeInterest("ndn:/0z8r6yDDe");
+ interest1->setNextHopFaceId(face1->getId());
+ shared_ptr<pit::Entry> pitEntry1 = pit.insert(*interest1).first;
+ pitEntry1->insertOrUpdateInRecord(face4, *interest1);
+
+ strategy.m_sendInterestHistory.clear();
+ strategy.afterReceiveInterest(*face4, *interest1, fibEntry, pitEntry1);
+ BOOST_REQUIRE_EQUAL(strategy.m_sendInterestHistory.size(), 1);
+ BOOST_CHECK_EQUAL(strategy.m_sendInterestHistory[0].get<1>(), face1);
+
+ // Interest without NextHopFaceId
+ shared_ptr<Interest> interest2 = makeInterest("ndn:/y6JQADGVz");
+ shared_ptr<pit::Entry> pitEntry2 = pit.insert(*interest2).first;
+ pitEntry2->insertOrUpdateInRecord(face4, *interest2);
+
+ strategy.m_sendInterestHistory.clear();
+ strategy.afterReceiveInterest(*face4, *interest2, fibEntry, pitEntry2);
+ BOOST_REQUIRE_EQUAL(strategy.m_sendInterestHistory.size(), 1);
+ BOOST_CHECK_EQUAL(strategy.m_sendInterestHistory[0].get<1>(), face2);
+
+ // Interest with invalid NextHopFaceId
+ shared_ptr<Interest> interest3 = makeInterest("ndn:/0z8r6yDDe");
+ interest3->setNextHopFaceId(face3->getId());
+ shared_ptr<pit::Entry> pitEntry3 = pit.insert(*interest3).first;
+ pitEntry3->insertOrUpdateInRecord(face4, *interest3);
+
+ face3->close(); // face3 is closed and its FaceId becomes invalid
+ strategy.m_sendInterestHistory.clear();
+ strategy.m_rejectPendingInterestHistory.clear();
+ strategy.afterReceiveInterest(*face4, *interest3, fibEntry, pitEntry3);
+ BOOST_REQUIRE_EQUAL(strategy.m_sendInterestHistory.size(), 0);
+ BOOST_REQUIRE_EQUAL(strategy.m_rejectPendingInterestHistory.size(), 1);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+} // namespace nfd
diff --git a/tests/daemon/fw/dummy-strategy.hpp b/tests/daemon/fw/dummy-strategy.hpp
new file mode 100644
index 0000000..67784fc
--- /dev/null
+++ b/tests/daemon/fw/dummy-strategy.hpp
@@ -0,0 +1,86 @@
+/* -*- 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_FW_DUMMY_STRATEGY_HPP
+#define NFD_TESTS_NFD_FW_DUMMY_STRATEGY_HPP
+
+#include "fw/strategy.hpp"
+
+namespace nfd {
+namespace tests {
+
+/** \brief strategy for unit testing
+ *
+ * Triggers on DummyStrategy are recorded but does nothing
+ */
+class DummyStrategy : public fw::Strategy
+{
+public:
+ DummyStrategy(Forwarder& forwarder, const Name& name)
+ : Strategy(forwarder, name)
+ {
+ }
+
+ virtual void
+ afterReceiveInterest(const Face& inFace,
+ const Interest& interest,
+ shared_ptr<fib::Entry> fibEntry,
+ shared_ptr<pit::Entry> pitEntry)
+ {
+ ++m_afterReceiveInterest_count;
+
+ if (static_cast<bool>(m_interestOutFace)) {
+ this->sendInterest(pitEntry, m_interestOutFace);
+ }
+ else {
+ this->rejectPendingInterest(pitEntry);
+ }
+ }
+
+ virtual void
+ beforeSatisfyPendingInterest(shared_ptr<pit::Entry> pitEntry,
+ const Face& inFace, const Data& data)
+ {
+ ++m_beforeSatisfyPendingInterest_count;
+ }
+
+ virtual void
+ beforeExpirePendingInterest(shared_ptr<pit::Entry> pitEntry)
+ {
+ ++m_beforeExpirePendingInterest_count;
+ }
+
+public:
+ int m_afterReceiveInterest_count;
+ int m_beforeSatisfyPendingInterest_count;
+ int m_beforeExpirePendingInterest_count;
+
+ /// outFace to use in afterReceiveInterest, nullptr to reject
+ shared_ptr<Face> m_interestOutFace;
+};
+
+} // namespace tests
+} // namespace nfd
+
+#endif // NFD_TESTS_NFD_FW_DUMMY_STRATEGY_HPP
diff --git a/tests/daemon/fw/face-table.cpp b/tests/daemon/fw/face-table.cpp
new file mode 100644
index 0000000..daf9256
--- /dev/null
+++ b/tests/daemon/fw/face-table.cpp
@@ -0,0 +1,139 @@
+/* -*- 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 "fw/face-table.hpp"
+#include "fw/forwarder.hpp"
+
+#include "tests/test-common.hpp"
+#include "tests/daemon/face/dummy-face.hpp"
+
+namespace nfd {
+namespace tests {
+
+BOOST_FIXTURE_TEST_SUITE(FwFaceTable, BaseFixture)
+
+static inline void
+saveFaceId(std::vector<FaceId>& faceIds, shared_ptr<Face> face)
+{
+ faceIds.push_back(face->getId());
+}
+
+BOOST_AUTO_TEST_CASE(AddRemove)
+{
+ Forwarder forwarder;
+
+ FaceTable& faceTable = forwarder.getFaceTable();
+ std::vector<FaceId> onAddHistory;
+ std::vector<FaceId> onRemoveHistory;
+ faceTable.onAdd += bind(&saveFaceId, boost::ref(onAddHistory ), _1);
+ faceTable.onRemove += bind(&saveFaceId, boost::ref(onRemoveHistory), _1);
+
+ shared_ptr<Face> face1 = make_shared<DummyFace>();
+ shared_ptr<Face> face2 = make_shared<DummyFace>();
+
+ BOOST_CHECK_EQUAL(face1->getId(), INVALID_FACEID);
+ BOOST_CHECK_EQUAL(face2->getId(), INVALID_FACEID);
+
+ forwarder.addFace(face1);
+ forwarder.addFace(face2);
+
+ BOOST_CHECK_NE(face1->getId(), INVALID_FACEID);
+ BOOST_CHECK_NE(face2->getId(), INVALID_FACEID);
+ BOOST_CHECK_NE(face1->getId(), face2->getId());
+
+ FaceId oldId1 = face1->getId();
+ faceTable.add(face1);
+ BOOST_CHECK_EQUAL(face1->getId(), oldId1);
+ BOOST_CHECK_EQUAL(faceTable.size(), 2);
+
+ BOOST_REQUIRE_EQUAL(onAddHistory.size(), 2);
+ BOOST_CHECK_EQUAL(onAddHistory[0], face1->getId());
+ BOOST_CHECK_EQUAL(onAddHistory[1], face2->getId());
+
+ face1->close();
+
+ BOOST_CHECK_EQUAL(face1->getId(), INVALID_FACEID);
+
+ BOOST_REQUIRE_EQUAL(onRemoveHistory.size(), 1);
+ BOOST_CHECK_EQUAL(onRemoveHistory[0], onAddHistory[0]);
+}
+
+BOOST_AUTO_TEST_CASE(Enumerate)
+{
+ Forwarder forwarder;
+ FaceTable& faceTable = forwarder.getFaceTable();
+
+ shared_ptr<Face> face1 = make_shared<DummyFace>();
+ shared_ptr<Face> face2 = make_shared<DummyFace>();
+ bool hasFace1 = false, hasFace2 = false;
+
+ BOOST_CHECK_EQUAL(faceTable.size(), 0);
+ BOOST_CHECK_EQUAL(std::distance(faceTable.begin(), faceTable.end()), faceTable.size());
+
+ faceTable.add(face1);
+ BOOST_CHECK_EQUAL(faceTable.size(), 1);
+ BOOST_CHECK_EQUAL(std::distance(faceTable.begin(), faceTable.end()), faceTable.size());
+ hasFace1 = hasFace2 = false;
+ for (FaceTable::const_iterator it = faceTable.begin(); it != faceTable.end(); ++it) {
+ if (*it == face1) {
+ hasFace1 = true;
+ }
+ }
+ BOOST_CHECK(hasFace1);
+
+ faceTable.add(face2);
+ BOOST_CHECK_EQUAL(faceTable.size(), 2);
+ BOOST_CHECK_EQUAL(std::distance(faceTable.begin(), faceTable.end()), faceTable.size());
+ hasFace1 = hasFace2 = false;
+ for (FaceTable::const_iterator it = faceTable.begin(); it != faceTable.end(); ++it) {
+ if (*it == face1) {
+ hasFace1 = true;
+ }
+ if (*it == face2) {
+ hasFace2 = true;
+ }
+ }
+ BOOST_CHECK(hasFace1);
+ BOOST_CHECK(hasFace2);
+
+ face1->close();
+ BOOST_CHECK_EQUAL(faceTable.size(), 1);
+ BOOST_CHECK_EQUAL(std::distance(faceTable.begin(), faceTable.end()), faceTable.size());
+ hasFace1 = hasFace2 = false;
+ for (FaceTable::const_iterator it = faceTable.begin(); it != faceTable.end(); ++it) {
+ if (*it == face1) {
+ hasFace1 = true;
+ }
+ if (*it == face2) {
+ hasFace2 = true;
+ }
+ }
+ BOOST_CHECK(!hasFace1);
+ BOOST_CHECK(hasFace2);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+} // namespace nfd
diff --git a/tests/daemon/fw/forwarder.cpp b/tests/daemon/fw/forwarder.cpp
new file mode 100644
index 0000000..0151460
--- /dev/null
+++ b/tests/daemon/fw/forwarder.cpp
@@ -0,0 +1,352 @@
+/* -*- 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 "fw/forwarder.hpp"
+#include "tests/daemon/face/dummy-face.hpp"
+#include "dummy-strategy.hpp"
+
+#include "tests/test-common.hpp"
+#include "tests/limited-io.hpp"
+
+namespace nfd {
+namespace tests {
+
+BOOST_FIXTURE_TEST_SUITE(FwForwarder, BaseFixture)
+
+BOOST_AUTO_TEST_CASE(SimpleExchange)
+{
+ Forwarder forwarder;
+
+ Name nameA ("ndn:/A");
+ Name nameAB ("ndn:/A/B");
+ Name nameABC("ndn:/A/B/C");
+ shared_ptr<Interest> interestAB = makeInterest(nameAB);
+ interestAB->setInterestLifetime(time::seconds(4));
+ shared_ptr<Data> dataABC = makeData(nameABC);
+
+ shared_ptr<DummyFace> face1 = make_shared<DummyFace>();
+ shared_ptr<DummyFace> face2 = make_shared<DummyFace>();
+ face1->afterSend += bind(&boost::asio::io_service::stop, &g_io);
+ face2->afterSend += bind(&boost::asio::io_service::stop, &g_io);
+ forwarder.addFace(face1);
+ forwarder.addFace(face2);
+
+ Fib& fib = forwarder.getFib();
+ shared_ptr<fib::Entry> fibEntry = fib.insert(Name("ndn:/A")).first;
+ fibEntry->addNextHop(face2, 0);
+
+ BOOST_CHECK_EQUAL(forwarder.getCounters().getNInInterests (), 0);
+ BOOST_CHECK_EQUAL(forwarder.getCounters().getNOutInterests(), 0);
+ face1->receiveInterest(*interestAB);
+ g_io.run();
+ g_io.reset();
+ BOOST_REQUIRE_EQUAL(face2->m_sentInterests.size(), 1);
+ BOOST_CHECK(face2->m_sentInterests[0].getName().equals(nameAB));
+ BOOST_CHECK_EQUAL(face2->m_sentInterests[0].getIncomingFaceId(), face1->getId());
+ BOOST_CHECK_EQUAL(forwarder.getCounters().getNInInterests (), 1);
+ BOOST_CHECK_EQUAL(forwarder.getCounters().getNOutInterests(), 1);
+
+ BOOST_CHECK_EQUAL(forwarder.getCounters().getNInDatas (), 0);
+ BOOST_CHECK_EQUAL(forwarder.getCounters().getNOutDatas(), 0);
+ face2->receiveData(*dataABC);
+ g_io.run();
+ g_io.reset();
+ BOOST_REQUIRE_EQUAL(face1->m_sentDatas.size(), 1);
+ BOOST_CHECK(face1->m_sentDatas[0].getName().equals(nameABC));
+ BOOST_CHECK_EQUAL(face1->m_sentDatas[0].getIncomingFaceId(), face2->getId());
+ BOOST_CHECK_EQUAL(forwarder.getCounters().getNInDatas (), 1);
+ BOOST_CHECK_EQUAL(forwarder.getCounters().getNOutDatas(), 1);
+}
+
+class ScopeLocalhostIncomingTestForwarder : public Forwarder
+{
+public:
+ ScopeLocalhostIncomingTestForwarder()
+ {
+ }
+
+ virtual void
+ onDataUnsolicited(Face& inFace, const Data& data)
+ {
+ ++m_onDataUnsolicited_count;
+ }
+
+protected:
+ virtual void
+ dispatchToStrategy(shared_ptr<pit::Entry> pitEntry, function<void(fw::Strategy*)> f)
+ {
+ ++m_dispatchToStrategy_count;
+ }
+
+public:
+ int m_dispatchToStrategy_count;
+ int m_onDataUnsolicited_count;
+};
+
+BOOST_AUTO_TEST_CASE(ScopeLocalhostIncoming)
+{
+ ScopeLocalhostIncomingTestForwarder forwarder;
+ shared_ptr<Face> face1 = make_shared<DummyLocalFace>();
+ shared_ptr<Face> face2 = make_shared<DummyFace>();
+ forwarder.addFace(face1);
+ forwarder.addFace(face2);
+
+ // local face, /localhost: OK
+ forwarder.m_dispatchToStrategy_count = 0;
+ shared_ptr<Interest> i1 = makeInterest("/localhost/A1");
+ forwarder.onIncomingInterest(*face1, *i1);
+ BOOST_CHECK_EQUAL(forwarder.m_dispatchToStrategy_count, 1);
+
+ // non-local face, /localhost: violate
+ forwarder.m_dispatchToStrategy_count = 0;
+ shared_ptr<Interest> i2 = makeInterest("/localhost/A2");
+ forwarder.onIncomingInterest(*face2, *i2);
+ BOOST_CHECK_EQUAL(forwarder.m_dispatchToStrategy_count, 0);
+
+ // local face, non-/localhost: OK
+ forwarder.m_dispatchToStrategy_count = 0;
+ shared_ptr<Interest> i3 = makeInterest("/A3");
+ forwarder.onIncomingInterest(*face1, *i3);
+ BOOST_CHECK_EQUAL(forwarder.m_dispatchToStrategy_count, 1);
+
+ // non-local face, non-/localhost: OK
+ forwarder.m_dispatchToStrategy_count = 0;
+ shared_ptr<Interest> i4 = makeInterest("/A4");
+ forwarder.onIncomingInterest(*face2, *i4);
+ BOOST_CHECK_EQUAL(forwarder.m_dispatchToStrategy_count, 1);
+
+ // local face, /localhost: OK
+ forwarder.m_onDataUnsolicited_count = 0;
+ shared_ptr<Data> d1 = makeData("/localhost/B1");
+ forwarder.onIncomingData(*face1, *d1);
+ BOOST_CHECK_EQUAL(forwarder.m_onDataUnsolicited_count, 1);
+
+ // non-local face, /localhost: OK
+ forwarder.m_onDataUnsolicited_count = 0;
+ shared_ptr<Data> d2 = makeData("/localhost/B2");
+ forwarder.onIncomingData(*face2, *d2);
+ BOOST_CHECK_EQUAL(forwarder.m_onDataUnsolicited_count, 0);
+
+ // local face, non-/localhost: OK
+ forwarder.m_onDataUnsolicited_count = 0;
+ shared_ptr<Data> d3 = makeData("/B3");
+ forwarder.onIncomingData(*face1, *d3);
+ BOOST_CHECK_EQUAL(forwarder.m_onDataUnsolicited_count, 1);
+
+ // non-local face, non-/localhost: OK
+ forwarder.m_onDataUnsolicited_count = 0;
+ shared_ptr<Data> d4 = makeData("/B4");
+ forwarder.onIncomingData(*face2, *d4);
+ BOOST_CHECK_EQUAL(forwarder.m_onDataUnsolicited_count, 1);
+}
+
+BOOST_AUTO_TEST_CASE(ScopeLocalhostOutgoing)
+{
+ Forwarder forwarder;
+ shared_ptr<DummyLocalFace> face1 = make_shared<DummyLocalFace>();
+ shared_ptr<DummyFace> face2 = make_shared<DummyFace>();
+ shared_ptr<Face> face3 = make_shared<DummyLocalFace>();
+ forwarder.addFace(face1);
+ forwarder.addFace(face2);
+ forwarder.addFace(face3);
+ Pit& pit = forwarder.getPit();
+
+ // local face, /localhost: OK
+ shared_ptr<Interest> interestA1 = makeInterest("/localhost/A1");
+ shared_ptr<pit::Entry> pitA1 = pit.insert(*interestA1).first;
+ pitA1->insertOrUpdateInRecord(face3, *interestA1);
+ face1->m_sentInterests.clear();
+ forwarder.onOutgoingInterest(pitA1, *face1);
+ BOOST_CHECK_EQUAL(face1->m_sentInterests.size(), 1);
+
+ // non-local face, /localhost: violate
+ shared_ptr<Interest> interestA2 = makeInterest("/localhost/A2");
+ shared_ptr<pit::Entry> pitA2 = pit.insert(*interestA2).first;
+ pitA2->insertOrUpdateInRecord(face3, *interestA2);
+ face2->m_sentInterests.clear();
+ forwarder.onOutgoingInterest(pitA2, *face2);
+ BOOST_CHECK_EQUAL(face2->m_sentInterests.size(), 0);
+
+ // local face, non-/localhost: OK
+ shared_ptr<Interest> interestA3 = makeInterest("/A3");
+ shared_ptr<pit::Entry> pitA3 = pit.insert(*interestA3).first;
+ pitA3->insertOrUpdateInRecord(face3, *interestA3);
+ face1->m_sentInterests.clear();
+ forwarder.onOutgoingInterest(pitA3, *face1);
+ BOOST_CHECK_EQUAL(face1->m_sentInterests.size(), 1);
+
+ // non-local face, non-/localhost: OK
+ shared_ptr<Interest> interestA4 = makeInterest("/A4");
+ shared_ptr<pit::Entry> pitA4 = pit.insert(*interestA4).first;
+ pitA4->insertOrUpdateInRecord(face3, *interestA4);
+ face2->m_sentInterests.clear();
+ forwarder.onOutgoingInterest(pitA4, *face2);
+ BOOST_CHECK_EQUAL(face2->m_sentInterests.size(), 1);
+
+ // local face, /localhost: OK
+ face1->m_sentDatas.clear();
+ forwarder.onOutgoingData(Data("/localhost/B1"), *face1);
+ BOOST_CHECK_EQUAL(face1->m_sentDatas.size(), 1);
+
+ // non-local face, /localhost: OK
+ face2->m_sentDatas.clear();
+ forwarder.onOutgoingData(Data("/localhost/B2"), *face2);
+ BOOST_CHECK_EQUAL(face2->m_sentDatas.size(), 0);
+
+ // local face, non-/localhost: OK
+ face1->m_sentDatas.clear();
+ forwarder.onOutgoingData(Data("/B3"), *face1);
+ BOOST_CHECK_EQUAL(face1->m_sentDatas.size(), 1);
+
+ // non-local face, non-/localhost: OK
+ face2->m_sentDatas.clear();
+ forwarder.onOutgoingData(Data("/B4"), *face2);
+ BOOST_CHECK_EQUAL(face2->m_sentDatas.size(), 1);
+}
+
+BOOST_AUTO_TEST_CASE(ScopeLocalhopOutgoing)
+{
+ Forwarder forwarder;
+ shared_ptr<DummyLocalFace> face1 = make_shared<DummyLocalFace>();
+ shared_ptr<DummyFace> face2 = make_shared<DummyFace>();
+ shared_ptr<DummyLocalFace> face3 = make_shared<DummyLocalFace>();
+ shared_ptr<DummyFace> face4 = make_shared<DummyFace>();
+ forwarder.addFace(face1);
+ forwarder.addFace(face2);
+ forwarder.addFace(face3);
+ forwarder.addFace(face4);
+ Pit& pit = forwarder.getPit();
+
+ // from local face, to local face: OK
+ shared_ptr<Interest> interest1 = makeInterest("/localhop/1");
+ shared_ptr<pit::Entry> pit1 = pit.insert(*interest1).first;
+ pit1->insertOrUpdateInRecord(face1, *interest1);
+ face3->m_sentInterests.clear();
+ forwarder.onOutgoingInterest(pit1, *face3);
+ BOOST_CHECK_EQUAL(face3->m_sentInterests.size(), 1);
+
+ // from non-local face, to local face: OK
+ shared_ptr<Interest> interest2 = makeInterest("/localhop/2");
+ shared_ptr<pit::Entry> pit2 = pit.insert(*interest2).first;
+ pit2->insertOrUpdateInRecord(face2, *interest2);
+ face3->m_sentInterests.clear();
+ forwarder.onOutgoingInterest(pit2, *face3);
+ BOOST_CHECK_EQUAL(face3->m_sentInterests.size(), 1);
+
+ // from local face, to non-local face: OK
+ shared_ptr<Interest> interest3 = makeInterest("/localhop/3");
+ shared_ptr<pit::Entry> pit3 = pit.insert(*interest3).first;
+ pit3->insertOrUpdateInRecord(face1, *interest3);
+ face4->m_sentInterests.clear();
+ forwarder.onOutgoingInterest(pit3, *face4);
+ BOOST_CHECK_EQUAL(face4->m_sentInterests.size(), 1);
+
+ // from non-local face, to non-local face: violate
+ shared_ptr<Interest> interest4 = makeInterest("/localhop/4");
+ shared_ptr<pit::Entry> pit4 = pit.insert(*interest4).first;
+ pit4->insertOrUpdateInRecord(face2, *interest4);
+ face4->m_sentInterests.clear();
+ forwarder.onOutgoingInterest(pit4, *face4);
+ BOOST_CHECK_EQUAL(face4->m_sentInterests.size(), 0);
+
+ // from local face and non-local face, to local face: OK
+ shared_ptr<Interest> interest5 = makeInterest("/localhop/5");
+ shared_ptr<pit::Entry> pit5 = pit.insert(*interest5).first;
+ pit5->insertOrUpdateInRecord(face1, *interest5);
+ pit5->insertOrUpdateInRecord(face2, *interest5);
+ face3->m_sentInterests.clear();
+ forwarder.onOutgoingInterest(pit5, *face3);
+ BOOST_CHECK_EQUAL(face3->m_sentInterests.size(), 1);
+
+ // from local face and non-local face, to non-local face: OK
+ shared_ptr<Interest> interest6 = makeInterest("/localhop/6");
+ shared_ptr<pit::Entry> pit6 = pit.insert(*interest6).first;
+ pit6->insertOrUpdateInRecord(face1, *interest6);
+ pit6->insertOrUpdateInRecord(face2, *interest6);
+ face4->m_sentInterests.clear();
+ forwarder.onOutgoingInterest(pit6, *face4);
+ BOOST_CHECK_EQUAL(face4->m_sentInterests.size(), 1);
+}
+
+BOOST_AUTO_TEST_CASE(StrategyDispatch)
+{
+ LimitedIo limitedIo;
+ Forwarder forwarder;
+ shared_ptr<Face> face1 = make_shared<DummyFace>();
+ shared_ptr<Face> face2 = make_shared<DummyFace>();
+ forwarder.addFace(face1);
+ forwarder.addFace(face2);
+
+ StrategyChoice& strategyChoice = forwarder.getStrategyChoice();
+ shared_ptr<DummyStrategy> strategyP = make_shared<DummyStrategy>(
+ boost::ref(forwarder), "ndn:/strategyP");
+ shared_ptr<DummyStrategy> strategyQ = make_shared<DummyStrategy>(
+ boost::ref(forwarder), "ndn:/strategyQ");
+ strategyChoice.install(strategyP);
+ strategyChoice.install(strategyQ);
+ strategyChoice.insert("ndn:/" , strategyP->getName());
+ strategyChoice.insert("ndn:/B", strategyQ->getName());
+
+ shared_ptr<Interest> interest1 = makeInterest("ndn:/A/1");
+ strategyP->m_afterReceiveInterest_count = 0;
+ strategyP->m_interestOutFace = face2;
+ forwarder.onInterest(*face1, *interest1);
+ BOOST_CHECK_EQUAL(strategyP->m_afterReceiveInterest_count, 1);
+
+ shared_ptr<Interest> interest2 = makeInterest("ndn:/B/2");
+ strategyQ->m_afterReceiveInterest_count = 0;
+ strategyQ->m_interestOutFace = face2;
+ forwarder.onInterest(*face1, *interest2);
+ BOOST_CHECK_EQUAL(strategyQ->m_afterReceiveInterest_count, 1);
+
+ limitedIo.run(LimitedIo::UNLIMITED_OPS, time::milliseconds(5));
+
+ shared_ptr<Data> data1 = makeData("ndn:/A/1/a");
+ strategyP->m_beforeSatisfyPendingInterest_count = 0;
+ forwarder.onData(*face2, *data1);
+ BOOST_CHECK_EQUAL(strategyP->m_beforeSatisfyPendingInterest_count, 1);
+
+ shared_ptr<Data> data2 = makeData("ndn:/B/2/b");
+ strategyQ->m_beforeSatisfyPendingInterest_count = 0;
+ forwarder.onData(*face2, *data2);
+ BOOST_CHECK_EQUAL(strategyQ->m_beforeSatisfyPendingInterest_count, 1);
+
+ shared_ptr<Interest> interest3 = makeInterest("ndn:/A/3");
+ interest3->setInterestLifetime(time::milliseconds(30));
+ forwarder.onInterest(*face1, *interest3);
+ shared_ptr<Interest> interest4 = makeInterest("ndn:/B/4");
+ interest4->setInterestLifetime(time::milliseconds(5000));
+ forwarder.onInterest(*face1, *interest4);
+
+ strategyP->m_beforeExpirePendingInterest_count = 0;
+ strategyQ->m_beforeExpirePendingInterest_count = 0;
+ limitedIo.run(LimitedIo::UNLIMITED_OPS, time::milliseconds(100));
+ BOOST_CHECK_EQUAL(strategyP->m_beforeExpirePendingInterest_count, 1);
+ BOOST_CHECK_EQUAL(strategyQ->m_beforeExpirePendingInterest_count, 0);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+} // namespace nfd
diff --git a/tests/daemon/fw/ncc-strategy.cpp b/tests/daemon/fw/ncc-strategy.cpp
new file mode 100644
index 0000000..29db3b3
--- /dev/null
+++ b/tests/daemon/fw/ncc-strategy.cpp
@@ -0,0 +1,110 @@
+/* -*- 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 "fw/ncc-strategy.hpp"
+#include "strategy-tester.hpp"
+#include "tests/daemon/face/dummy-face.hpp"
+#include "tests/limited-io.hpp"
+
+#include "tests/test-common.hpp"
+
+namespace nfd {
+namespace tests {
+
+BOOST_FIXTURE_TEST_SUITE(FwNccStrategy, BaseFixture)
+
+// NccStrategy is fairly complex.
+// The most important property is:
+// it remembers which upstream is the fastest to return Data,
+// and favors this upstream in subsequent Interests.
+BOOST_AUTO_TEST_CASE(FavorRespondingUpstream)
+{
+ LimitedIo limitedIo;
+ Forwarder forwarder;
+ typedef StrategyTester<fw::NccStrategy> NccStrategyTester;
+ shared_ptr<NccStrategyTester> strategy = make_shared<NccStrategyTester>(boost::ref(forwarder));
+ strategy->onAction += bind(&LimitedIo::afterOp, &limitedIo);
+
+ shared_ptr<DummyFace> face1 = make_shared<DummyFace>();
+ shared_ptr<DummyFace> face2 = make_shared<DummyFace>();
+ shared_ptr<DummyFace> face3 = make_shared<DummyFace>();
+ forwarder.addFace(face1);
+ forwarder.addFace(face2);
+ forwarder.addFace(face3);
+
+ Fib& fib = forwarder.getFib();
+ shared_ptr<fib::Entry> fibEntry = fib.insert(Name()).first;
+ fibEntry->addNextHop(face1, 10);
+ fibEntry->addNextHop(face2, 20);
+
+ StrategyChoice& strategyChoice = forwarder.getStrategyChoice();
+ strategyChoice.install(strategy);
+ strategyChoice.insert(Name(), strategy->getName());
+
+ Pit& pit = forwarder.getPit();
+
+ // first Interest: strategy knows nothing and follows routing
+ shared_ptr<Interest> interest1p = makeInterest("ndn:/0Jm1ajrW/%00");
+ Interest& interest1 = *interest1p;
+ interest1.setInterestLifetime(time::milliseconds(8000));
+ shared_ptr<pit::Entry> pitEntry1 = pit.insert(interest1).first;
+
+ pitEntry1->insertOrUpdateInRecord(face3, interest1);
+ strategy->afterReceiveInterest(*face3, interest1, fibEntry, pitEntry1);
+
+ // forwards to face1 because routing says it's best
+ limitedIo.run(LimitedIo::UNLIMITED_OPS, time::milliseconds(1));
+ BOOST_REQUIRE_EQUAL(strategy->m_sendInterestHistory.size(), 1);
+ BOOST_CHECK_EQUAL(strategy->m_sendInterestHistory[0].get<1>(), face1);
+
+ // forwards to face2 because face1 doesn't respond
+ limitedIo.run(LimitedIo::UNLIMITED_OPS, time::milliseconds(500));
+ BOOST_REQUIRE_EQUAL(strategy->m_sendInterestHistory.size(), 2);
+ BOOST_CHECK_EQUAL(strategy->m_sendInterestHistory[1].get<1>(), face2);
+
+ // face2 responds
+ shared_ptr<Data> data1p = makeData("ndn:/0Jm1ajrW/%00");
+ Data& data1 = *data1p;
+ strategy->beforeSatisfyPendingInterest(pitEntry1, *face2, data1);
+ limitedIo.run(LimitedIo::UNLIMITED_OPS, time::milliseconds(500));
+
+ // second Interest: strategy knows face2 is best
+ shared_ptr<Interest> interest2p = makeInterest("ndn:/0Jm1ajrW/%00%01");
+ Interest& interest2 = *interest2p;
+ interest2.setInterestLifetime(time::milliseconds(8000));
+ shared_ptr<pit::Entry> pitEntry2 = pit.insert(interest2).first;
+
+ pitEntry2->insertOrUpdateInRecord(face3, interest2);
+ strategy->afterReceiveInterest(*face3, interest2, fibEntry, pitEntry2);
+
+ // forwards to face2 because it responds previously
+ limitedIo.run(LimitedIo::UNLIMITED_OPS, time::milliseconds(1));
+ BOOST_REQUIRE_GE(strategy->m_sendInterestHistory.size(), 3);
+ BOOST_CHECK_EQUAL(strategy->m_sendInterestHistory[2].get<1>(), face2);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+} // namespace nfd
diff --git a/tests/daemon/fw/strategy-tester.hpp b/tests/daemon/fw/strategy-tester.hpp
new file mode 100644
index 0000000..e3debef
--- /dev/null
+++ b/tests/daemon/fw/strategy-tester.hpp
@@ -0,0 +1,89 @@
+/* -*- 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_FW_STRATEGY_TESTER_HPP
+#define NFD_TESTS_NFD_FW_STRATEGY_TESTER_HPP
+
+#include <boost/tuple/tuple_comparison.hpp>
+#include "fw/strategy.hpp"
+
+namespace nfd {
+namespace tests {
+
+/** \class StrategyTester
+ * \brief extends strategy S for unit testing
+ *
+ * Actions invoked by S are recorded but not passed to forwarder
+ */
+template<typename S>
+class StrategyTester : public S
+{
+public:
+ explicit
+ StrategyTester(Forwarder& forwarder)
+ : S(forwarder, Name(S::STRATEGY_NAME).append("tester"))
+ {
+ }
+
+ /// fires after each Action
+ EventEmitter<> onAction;
+
+protected:
+ virtual void
+ sendInterest(shared_ptr<pit::Entry> pitEntry,shared_ptr<Face> outFace);
+
+ virtual void
+ rejectPendingInterest(shared_ptr<pit::Entry> pitEntry);
+
+public:
+ typedef boost::tuple<shared_ptr<pit::Entry>, shared_ptr<Face> > SendInterestArgs;
+ std::vector<SendInterestArgs> m_sendInterestHistory;
+
+ typedef boost::tuple<shared_ptr<pit::Entry> > RejectPendingInterestArgs;
+ std::vector<RejectPendingInterestArgs> m_rejectPendingInterestHistory;
+};
+
+
+template<typename S>
+inline void
+StrategyTester<S>::sendInterest(shared_ptr<pit::Entry> pitEntry,
+ shared_ptr<Face> outFace)
+{
+ m_sendInterestHistory.push_back(SendInterestArgs(pitEntry, outFace));
+ pitEntry->insertOrUpdateOutRecord(outFace, pitEntry->getInterest());
+ onAction();
+}
+
+template<typename S>
+inline void
+StrategyTester<S>::rejectPendingInterest(shared_ptr<pit::Entry> pitEntry)
+{
+ m_rejectPendingInterestHistory.push_back(RejectPendingInterestArgs(pitEntry));
+ onAction();
+}
+
+} // namespace tests
+} // namespace nfd
+
+#endif // NFD_TESTS_NFD_FW_STRATEGY_TESTER_HPP
diff --git a/tests/daemon/mgmt/command-validator.cpp b/tests/daemon/mgmt/command-validator.cpp
new file mode 100644
index 0000000..cb8031a
--- /dev/null
+++ b/tests/daemon/mgmt/command-validator.cpp
@@ -0,0 +1,600 @@
+/* -*- 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 "mgmt/command-validator.hpp"
+#include "core/config-file.hpp"
+
+#include "tests/test-common.hpp"
+
+#include <ndn-cpp-dev/util/command-interest-generator.hpp>
+#include <ndn-cpp-dev/util/io.hpp>
+#include <boost/filesystem.hpp>
+#include <fstream>
+
+namespace nfd {
+
+namespace tests {
+
+NFD_LOG_INIT("CommandValidatorTest");
+
+BOOST_FIXTURE_TEST_SUITE(MgmtCommandValidator, BaseFixture)
+
+// authorizations
+// {
+// authorize
+// {
+// certfile "tests/daemon/mgmt/cert1.ndncert"
+// privileges
+// {
+// fib
+// stats
+// }
+// }
+
+// authorize
+// {
+// certfile "tests/daemon/mgmt/cert2.ndncert"
+// privileges
+// {
+// faces
+// }
+// }
+// }
+
+const std::string CONFIG =
+"authorizations\n"
+"{\n"
+" authorize\n"
+" {\n"
+" certfile \"tests/daemon/mgmt/cert1.ndncert\"\n"
+" privileges\n"
+" {\n"
+" fib\n"
+" stats\n"
+" }\n"
+" }\n"
+" authorize\n"
+" {\n"
+" certfile \"tests/daemon/mgmt/cert2.ndncert\"\n"
+" privileges\n"
+" {\n"
+" faces\n"
+" }\n"
+" }\n"
+ "}\n";
+
+const boost::filesystem::path CONFIG_PATH =
+ boost::filesystem::current_path() /= std::string("unit-test-nfd.conf");
+
+class CommandValidatorTester
+{
+public:
+
+ CommandValidatorTester()
+ : m_validated(false),
+ m_validationFailed(false)
+ {
+
+ }
+
+ void
+ generateIdentity(const Name& prefix)
+ {
+ m_identityName = prefix;
+ m_identityName.appendVersion();
+
+ const Name certName = m_keys.createIdentity(m_identityName);
+
+ m_certificate = m_keys.getCertificate(certName);
+ }
+
+ void
+ saveIdentityToFile(const char* filename)
+ {
+ std::ofstream out;
+ out.open(filename);
+
+ BOOST_REQUIRE(out.is_open());
+ BOOST_REQUIRE(static_cast<bool>(m_certificate));
+
+ ndn::io::save<ndn::IdentityCertificate>(*m_certificate, out);
+
+ out.close();
+ }
+
+ const Name&
+ getIdentityName() const
+ {
+ BOOST_REQUIRE_NE(m_identityName, Name());
+ return m_identityName;
+ }
+
+ const Name&
+ getPublicKeyName() const
+ {
+ BOOST_REQUIRE(static_cast<bool>(m_certificate));
+ return m_certificate->getPublicKeyName();
+ }
+
+ void
+ onValidated(const shared_ptr<const Interest>& interest)
+ {
+ // NFD_LOG_DEBUG("validated command");
+ m_validated = true;
+ }
+
+ void
+ onValidationFailed(const shared_ptr<const Interest>& interest, const std::string& info)
+ {
+ NFD_LOG_DEBUG("validation failed: " << info);
+ m_validationFailed = true;
+ }
+
+ bool
+ commandValidated() const
+ {
+ return m_validated;
+ }
+
+ bool
+ commandValidationFailed() const
+ {
+ return m_validationFailed;
+ }
+
+ void
+ resetValidation()
+ {
+ m_validated = false;
+ m_validationFailed = false;
+ }
+
+ ~CommandValidatorTester()
+ {
+ m_keys.deleteIdentity(m_identityName);
+ }
+
+private:
+ bool m_validated;
+ bool m_validationFailed;
+
+ ndn::KeyChain m_keys;
+ Name m_identityName;
+ shared_ptr<ndn::IdentityCertificate> m_certificate;
+};
+
+class TwoValidatorFixture : public BaseFixture
+{
+public:
+ TwoValidatorFixture()
+ {
+ m_tester1.generateIdentity("/test/CommandValidator/TwoKeys/id1");
+ m_tester1.saveIdentityToFile("tests/daemon/mgmt/cert1.ndncert");
+
+ m_tester2.generateIdentity("/test/CommandValidator/TwoKeys/id2");
+ m_tester2.saveIdentityToFile("tests/daemon/mgmt/cert2.ndncert");
+ }
+
+ ~TwoValidatorFixture()
+ {
+ boost::system::error_code error;
+ boost::filesystem::remove("tests/daemon/mgmt/cert1.ndncert", error);
+ boost::filesystem::remove("tests/daemon/mgmt/cert2.ndncert", error);
+ }
+
+protected:
+ CommandValidatorTester m_tester1;
+ CommandValidatorTester m_tester2;
+};
+
+BOOST_FIXTURE_TEST_CASE(TwoKeys, TwoValidatorFixture)
+{
+ shared_ptr<Interest> fibCommand = make_shared<Interest>("/localhost/nfd/fib/insert");
+ shared_ptr<Interest> statsCommand = make_shared<Interest>("/localhost/nfd/stats/dosomething");
+ shared_ptr<Interest> facesCommand = make_shared<Interest>("/localhost/nfd/faces/create");
+
+ ndn::CommandInterestGenerator generator;
+ generator.generateWithIdentity(*fibCommand, m_tester1.getIdentityName());
+ generator.generateWithIdentity(*statsCommand, m_tester1.getIdentityName());
+ generator.generateWithIdentity(*facesCommand, m_tester2.getIdentityName());
+
+ ConfigFile config;
+ CommandValidator validator;
+ validator.addSupportedPrivilege("faces");
+ validator.addSupportedPrivilege("fib");
+ validator.addSupportedPrivilege("stats");
+
+ validator.setConfigFile(config);
+
+ config.parse(CONFIG, false, CONFIG_PATH.native());
+
+ validator.validate(*fibCommand,
+ bind(&CommandValidatorTester::onValidated, boost::ref(m_tester1), _1),
+ bind(&CommandValidatorTester::onValidationFailed, boost::ref(m_tester1), _1, _2));
+
+ BOOST_REQUIRE(m_tester1.commandValidated());
+ m_tester1.resetValidation();
+
+ validator.validate(*statsCommand,
+ bind(&CommandValidatorTester::onValidated, boost::ref(m_tester1), _1),
+ bind(&CommandValidatorTester::onValidationFailed, boost::ref(m_tester1), _1, _2));
+
+ BOOST_REQUIRE(m_tester1.commandValidated());
+
+ validator.validate(*facesCommand,
+ bind(&CommandValidatorTester::onValidated, boost::ref(m_tester2), _1),
+ bind(&CommandValidatorTester::onValidationFailed, boost::ref(m_tester2), _1, _2));
+
+ BOOST_REQUIRE(m_tester2.commandValidated());
+ m_tester2.resetValidation();
+
+ // use cert2 for fib command (authorized for cert1 only)
+ shared_ptr<Interest> unauthorizedFibCommand = make_shared<Interest>("/localhost/nfd/fib/insert");
+ generator.generateWithIdentity(*unauthorizedFibCommand, m_tester2.getIdentityName());
+
+ validator.validate(*unauthorizedFibCommand,
+ bind(&CommandValidatorTester::onValidated, boost::ref(m_tester2), _1),
+ bind(&CommandValidatorTester::onValidationFailed, boost::ref(m_tester2), _1, _2));
+
+ BOOST_REQUIRE(m_tester2.commandValidationFailed());
+}
+
+BOOST_FIXTURE_TEST_CASE(TwoKeysDryRun, TwoValidatorFixture)
+{
+ CommandValidatorTester tester1;
+ tester1.generateIdentity("/test/CommandValidator/TwoKeys/id1");
+ tester1.saveIdentityToFile("tests/daemon/mgmt/cert1.ndncert");
+
+ CommandValidatorTester tester2;
+ tester2.generateIdentity("/test/CommandValidator/TwoKeys/id2");
+ tester2.saveIdentityToFile("tests/daemon/mgmt/cert2.ndncert");
+
+ shared_ptr<Interest> fibCommand = make_shared<Interest>("/localhost/nfd/fib/insert");
+ shared_ptr<Interest> statsCommand = make_shared<Interest>("/localhost/nfd/stats/dosomething");
+ shared_ptr<Interest> facesCommand = make_shared<Interest>("/localhost/nfd/faces/create");
+
+ ndn::CommandInterestGenerator generator;
+ generator.generateWithIdentity(*fibCommand, m_tester1.getIdentityName());
+ generator.generateWithIdentity(*statsCommand, m_tester1.getIdentityName());
+ generator.generateWithIdentity(*facesCommand, m_tester2.getIdentityName());
+
+ ConfigFile config;
+ CommandValidator validator;
+ validator.addSupportedPrivilege("faces");
+ validator.addSupportedPrivilege("fib");
+ validator.addSupportedPrivilege("stats");
+
+ validator.setConfigFile(config);
+
+ config.parse(CONFIG, true, CONFIG_PATH.native());
+
+ validator.validate(*fibCommand,
+ bind(&CommandValidatorTester::onValidated, boost::ref(m_tester1), _1),
+ bind(&CommandValidatorTester::onValidationFailed, boost::ref(m_tester1), _1, _2));
+
+ BOOST_REQUIRE(m_tester1.commandValidationFailed());
+ m_tester1.resetValidation();
+
+ validator.validate(*statsCommand,
+ bind(&CommandValidatorTester::onValidated, boost::ref(m_tester1), _1),
+ bind(&CommandValidatorTester::onValidationFailed, boost::ref(m_tester1), _1, _2));
+
+ BOOST_REQUIRE(m_tester1.commandValidationFailed());
+
+ validator.validate(*facesCommand,
+ bind(&CommandValidatorTester::onValidated, boost::ref(m_tester2), _1),
+ bind(&CommandValidatorTester::onValidationFailed, boost::ref(m_tester2), _1, _2));
+
+ BOOST_REQUIRE(m_tester2.commandValidationFailed());
+ m_tester2.resetValidation();
+
+ // use cert2 for fib command (authorized for cert1 only)
+ shared_ptr<Interest> unauthorizedFibCommand = make_shared<Interest>("/localhost/nfd/fib/insert");
+ generator.generateWithIdentity(*unauthorizedFibCommand, m_tester2.getIdentityName());
+
+ validator.validate(*unauthorizedFibCommand,
+ bind(&CommandValidatorTester::onValidated, boost::ref(m_tester2), _1),
+ bind(&CommandValidatorTester::onValidationFailed, boost::ref(m_tester2), _1, _2));
+
+ BOOST_REQUIRE(m_tester2.commandValidationFailed());
+}
+
+BOOST_AUTO_TEST_CASE(NoAuthorizeSections)
+{
+ const std::string NO_AUTHORIZE_CONFIG =
+ "authorizations\n"
+ "{\n"
+ "}\n";
+
+ ConfigFile config;
+ CommandValidator validator;
+
+ validator.setConfigFile(config);
+ BOOST_CHECK_THROW(config.parse(NO_AUTHORIZE_CONFIG, false, CONFIG_PATH.native()), ConfigFile::Error);
+}
+
+BOOST_AUTO_TEST_CASE(NoPrivilegesSections)
+{
+ const std::string NO_PRIVILEGES_CONFIG =
+ "authorizations\n"
+ "{\n"
+ " authorize\n"
+ " {\n"
+ " certfile \"tests/daemon/mgmt/cert1.ndncert\"\n"
+ " }\n"
+ "}\n";
+
+ ConfigFile config;
+ CommandValidator validator;
+
+ validator.setConfigFile(config);
+
+ BOOST_CHECK_THROW(config.parse(NO_PRIVILEGES_CONFIG, false, CONFIG_PATH.native()), ConfigFile::Error);
+}
+
+BOOST_AUTO_TEST_CASE(InvalidCertfile)
+{
+ const std::string INVALID_CERT_CONFIG =
+ "authorizations\n"
+ "{\n"
+ " authorize\n"
+ " {\n"
+ " certfile \"tests/daemon/mgmt/notacertfile.ndncert\"\n"
+ " privileges\n"
+ " {\n"
+ " fib\n"
+ " stats\n"
+ " }\n"
+ " }\n"
+ "}\n";
+
+ ConfigFile config;
+ CommandValidator validator;
+
+ validator.setConfigFile(config);
+ BOOST_CHECK_THROW(config.parse(INVALID_CERT_CONFIG, false, CONFIG_PATH.native()), ConfigFile::Error);
+}
+
+BOOST_AUTO_TEST_CASE(NoCertfile)
+{
+ const std::string NO_CERT_CONFIG =
+ "authorizations\n"
+ "{\n"
+ " authorize\n"
+ " {\n"
+ " privileges\n"
+ " {\n"
+ " fib\n"
+ " stats\n"
+ " }\n"
+ " }\n"
+ "}\n";
+
+
+ ConfigFile config;
+ CommandValidator validator;
+
+ validator.setConfigFile(config);
+ BOOST_CHECK_THROW(config.parse(NO_CERT_CONFIG, false, CONFIG_PATH.native()), ConfigFile::Error);
+}
+
+BOOST_AUTO_TEST_CASE(MalformedCert)
+{
+ const std::string MALFORMED_CERT_CONFIG =
+ "authorizations\n"
+ "{\n"
+ " authorize\n"
+ " {\n"
+ " certfile \"tests/daemon/mgmt/malformed.ndncert\"\n"
+ " privileges\n"
+ " {\n"
+ " fib\n"
+ " stats\n"
+ " }\n"
+ " }\n"
+ "}\n";
+
+
+ ConfigFile config;
+ CommandValidator validator;
+
+ validator.setConfigFile(config);
+ BOOST_CHECK_THROW(config.parse(MALFORMED_CERT_CONFIG, false, CONFIG_PATH.native()), ConfigFile::Error);
+}
+
+bool
+validateErrorMessage(const std::string& expectedMessage, const ConfigFile::Error& error)
+{
+ bool gotExpected = error.what() == expectedMessage;
+ if (!gotExpected)
+ {
+ NFD_LOG_WARN("\ncaught exception: " << error.what()
+ << "\n\nexpected exception: " << expectedMessage);
+ }
+ return gotExpected;
+}
+
+BOOST_AUTO_TEST_CASE(NoAuthorizeSectionsDryRun)
+{
+ const std::string NO_AUTHORIZE_CONFIG =
+ "authorizations\n"
+ "{\n"
+ "}\n";
+
+ ConfigFile config;
+ CommandValidator validator;
+
+ validator.setConfigFile(config);
+ BOOST_CHECK_EXCEPTION(config.parse(NO_AUTHORIZE_CONFIG, true, CONFIG_PATH.native()),
+ ConfigFile::Error,
+ bind(&validateErrorMessage,
+ "No authorize sections found", _1));
+}
+
+BOOST_FIXTURE_TEST_CASE(NoPrivilegesSectionsDryRun, TwoValidatorFixture)
+{
+ const std::string NO_PRIVILEGES_CONFIG =
+ "authorizations\n"
+ "{\n"
+ " authorize\n"
+ " {\n"
+ " certfile \"tests/daemon/mgmt/cert1.ndncert\"\n"
+ " }\n"
+ " authorize\n"
+ " {\n"
+ " certfile \"tests/daemon/mgmt/cert2.ndncert\"\n"
+ " }\n"
+ "}\n";
+
+ ConfigFile config;
+ CommandValidator validator;
+
+ validator.setConfigFile(config);
+
+ std::stringstream expectedError;
+ expectedError << "No privileges section found for certificate file tests/daemon/mgmt/cert1.ndncert "
+ << "(" << m_tester1.getPublicKeyName().toUri() << ")\n"
+ << "No privileges section found for certificate file tests/daemon/mgmt/cert2.ndncert "
+ << "(" << m_tester2.getPublicKeyName().toUri() << ")";
+
+ BOOST_CHECK_EXCEPTION(config.parse(NO_PRIVILEGES_CONFIG, true, CONFIG_PATH.native()),
+ ConfigFile::Error,
+ bind(&validateErrorMessage, expectedError.str(), _1));
+}
+
+BOOST_AUTO_TEST_CASE(InvalidCertfileDryRun)
+{
+ using namespace boost::filesystem;
+
+ const std::string INVALID_KEY_CONFIG =
+ "authorizations\n"
+ "{\n"
+ " authorize\n"
+ " {\n"
+ " certfile \"tests/daemon/mgmt/notacertfile.ndncert\"\n"
+ " privileges\n"
+ " {\n"
+ " fib\n"
+ " stats\n"
+ " }\n"
+ " }\n"
+ " authorize\n"
+ " {\n"
+ " certfile \"tests/daemon/mgmt/stillnotacertfile.ndncert\"\n"
+ " privileges\n"
+ " {\n"
+ " }\n"
+ " }\n"
+ "}\n";
+
+ ConfigFile config;
+ CommandValidator validator;
+
+ validator.setConfigFile(config);
+
+ std::stringstream error;
+ error << "Unable to open certificate file "
+ << absolute("tests/daemon/mgmt/notacertfile.ndncert").native() << "\n"
+ << "Unable to open certificate file "
+ << absolute("tests/daemon/mgmt/stillnotacertfile.ndncert").native();
+
+ BOOST_CHECK_EXCEPTION(config.parse(INVALID_KEY_CONFIG, true, CONFIG_PATH.native()),
+ ConfigFile::Error,
+ bind(&validateErrorMessage, error.str(), _1));
+}
+
+BOOST_AUTO_TEST_CASE(NoCertfileDryRun)
+{
+ const std::string NO_CERT_CONFIG =
+ "authorizations\n"
+ "{\n"
+ " authorize\n"
+ " {\n"
+ " privileges\n"
+ " {\n"
+ " fib\n"
+ " stats\n"
+ " }\n"
+ " }\n"
+ " authorize\n"
+ " {\n"
+ " }\n"
+ "}\n";
+
+
+ ConfigFile config;
+ CommandValidator validator;
+
+ validator.setConfigFile(config);
+ BOOST_CHECK_EXCEPTION(config.parse(NO_CERT_CONFIG, true, CONFIG_PATH.native()),
+ ConfigFile::Error,
+ bind(&validateErrorMessage,
+ "No certfile specified\n"
+ "No certfile specified", _1));
+}
+
+BOOST_AUTO_TEST_CASE(MalformedCertDryRun)
+{
+ using namespace boost::filesystem;
+
+ const std::string MALFORMED_CERT_CONFIG =
+ "authorizations\n"
+ "{\n"
+ " authorize\n"
+ " {\n"
+ " certfile \"tests/daemon/mgmt/malformed.ndncert\"\n"
+ " privileges\n"
+ " {\n"
+ " fib\n"
+ " stats\n"
+ " }\n"
+ " }\n"
+ " authorize\n"
+ " {\n"
+ " certfile \"tests/daemon/mgmt/malformed.ndncert\"\n"
+ " }\n"
+ "}\n";
+
+
+ ConfigFile config;
+ CommandValidator validator;
+
+ validator.setConfigFile(config);
+
+ std::stringstream error;
+ error << "Malformed certificate file "
+ << absolute("tests/daemon/mgmt/malformed.ndncert").native() << "\n"
+ << "Malformed certificate file "
+ << absolute("tests/daemon/mgmt/malformed.ndncert").native();
+
+ BOOST_CHECK_EXCEPTION(config.parse(MALFORMED_CERT_CONFIG, true, CONFIG_PATH.native()),
+ ConfigFile::Error,
+ bind(&validateErrorMessage, error.str(), _1));
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+
+} // namespace nfd
diff --git a/tests/daemon/mgmt/face-flags.cpp b/tests/daemon/mgmt/face-flags.cpp
new file mode 100644
index 0000000..0aa6c09
--- /dev/null
+++ b/tests/daemon/mgmt/face-flags.cpp
@@ -0,0 +1,65 @@
+/* -*- 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 "mgmt/face-flags.hpp"
+
+#include "tests/test-common.hpp"
+#include "tests/daemon/face/dummy-face.hpp"
+
+namespace nfd {
+namespace tests {
+
+BOOST_FIXTURE_TEST_SUITE(MgmtFaceFlags, BaseFixture)
+
+template<typename DummyFaceBase>
+class DummyOnDemandFace : public DummyFaceBase
+{
+public:
+ DummyOnDemandFace()
+ {
+ this->setOnDemand(true);
+ }
+};
+
+BOOST_AUTO_TEST_CASE(Get)
+{
+ DummyFace face1;
+ BOOST_CHECK_EQUAL(getFaceFlags(face1), 0);
+
+ DummyLocalFace face2;
+ BOOST_CHECK_EQUAL(getFaceFlags(face2), static_cast<uint64_t>(ndn::nfd::FACE_IS_LOCAL));
+
+ DummyOnDemandFace<DummyFace> face3;
+ BOOST_CHECK_EQUAL(getFaceFlags(face3), static_cast<uint64_t>(ndn::nfd::FACE_IS_ON_DEMAND));
+
+ DummyOnDemandFace<DummyLocalFace> face4;
+ BOOST_CHECK_EQUAL(getFaceFlags(face4), static_cast<uint64_t>(ndn::nfd::FACE_IS_LOCAL |
+ ndn::nfd::FACE_IS_ON_DEMAND));
+
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+} // namespace nfd
diff --git a/tests/daemon/mgmt/face-manager.cpp b/tests/daemon/mgmt/face-manager.cpp
new file mode 100644
index 0000000..e799944
--- /dev/null
+++ b/tests/daemon/mgmt/face-manager.cpp
@@ -0,0 +1,1666 @@
+/* -*- 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 "mgmt/face-manager.hpp"
+#include "mgmt/internal-face.hpp"
+#include "mgmt/face-status-publisher.hpp"
+
+#include "face/face.hpp"
+#include "../face/dummy-face.hpp"
+#include "fw/face-table.hpp"
+#include "fw/forwarder.hpp"
+
+#include "common.hpp"
+#include "tests/test-common.hpp"
+#include "validation-common.hpp"
+#include "face-status-publisher-common.hpp"
+
+#include <ndn-cpp-dev/encoding/tlv.hpp>
+#include <ndn-cpp-dev/management/nfd-face-event-notification.hpp>
+
+namespace nfd {
+namespace tests {
+
+NFD_LOG_INIT("FaceManagerTest");
+
+class FaceManagerTestFace : public DummyFace
+{
+public:
+
+ FaceManagerTestFace()
+ : m_closeFired(false)
+ {
+
+ }
+
+ virtual
+ ~FaceManagerTestFace()
+ {
+
+ }
+
+ virtual void
+ close()
+ {
+ m_closeFired = true;
+ }
+
+ bool
+ didCloseFire() const
+ {
+ return m_closeFired;
+ }
+
+private:
+ bool m_closeFired;
+};
+
+class TestFaceTable : public FaceTable
+{
+public:
+ TestFaceTable(Forwarder& forwarder)
+ : FaceTable(forwarder),
+ m_addFired(false),
+ m_getFired(false),
+ m_dummy(make_shared<FaceManagerTestFace>())
+ {
+
+ }
+
+ virtual
+ ~TestFaceTable()
+ {
+
+ }
+
+ virtual void
+ add(shared_ptr<Face> face)
+ {
+ m_addFired = true;
+ }
+
+ virtual shared_ptr<Face>
+ get(FaceId id) const
+ {
+ m_getFired = true;
+ return m_dummy;
+ }
+
+ bool
+ didAddFire() const
+ {
+ return m_addFired;
+ }
+
+ bool
+ didGetFire() const
+ {
+ return m_getFired;
+ }
+
+ void
+ reset()
+ {
+ m_addFired = false;
+ m_getFired = false;
+ }
+
+ shared_ptr<FaceManagerTestFace>&
+ getDummyFace()
+ {
+ return m_dummy;
+ }
+
+private:
+ bool m_addFired;
+ mutable bool m_getFired;
+ shared_ptr<FaceManagerTestFace> m_dummy;
+};
+
+
+class TestFaceTableFixture : public BaseFixture
+{
+public:
+ TestFaceTableFixture()
+ : m_faceTable(m_forwarder)
+ {
+
+ }
+
+ virtual
+ ~TestFaceTableFixture()
+ {
+
+ }
+
+protected:
+ Forwarder m_forwarder;
+ TestFaceTable m_faceTable;
+};
+
+class TestFaceManagerCommon
+{
+public:
+ TestFaceManagerCommon()
+ : m_face(make_shared<InternalFace>()),
+ m_callbackFired(false)
+ {
+
+ }
+
+ virtual
+ ~TestFaceManagerCommon()
+ {
+
+ }
+
+ shared_ptr<InternalFace>&
+ getFace()
+ {
+ return m_face;
+ }
+
+ void
+ validateControlResponseCommon(const Data& response,
+ const Name& expectedName,
+ uint32_t expectedCode,
+ const std::string& expectedText,
+ ControlResponse& control)
+ {
+ m_callbackFired = true;
+ Block controlRaw = response.getContent().blockFromValue();
+
+ control.wireDecode(controlRaw);
+
+ // NFD_LOG_DEBUG("received control response"
+ // << " Name: " << response.getName()
+ // << " code: " << control.getCode()
+ // << " text: " << control.getText());
+
+ BOOST_CHECK_EQUAL(response.getName(), expectedName);
+ BOOST_CHECK_EQUAL(control.getCode(), expectedCode);
+ BOOST_CHECK_EQUAL(control.getText(), expectedText);
+ }
+
+ void
+ validateControlResponse(const Data& response,
+ const Name& expectedName,
+ uint32_t expectedCode,
+ const std::string& expectedText)
+ {
+ ControlResponse control;
+ validateControlResponseCommon(response, expectedName,
+ expectedCode, expectedText, control);
+
+ if (!control.getBody().empty())
+ {
+ BOOST_FAIL("found unexpected control response body");
+ }
+ }
+
+ void
+ validateControlResponse(const Data& response,
+ const Name& expectedName,
+ uint32_t expectedCode,
+ const std::string& expectedText,
+ const Block& expectedBody)
+ {
+ ControlResponse control;
+ validateControlResponseCommon(response, expectedName,
+ expectedCode, expectedText, control);
+
+ BOOST_REQUIRE(!control.getBody().empty());
+ BOOST_REQUIRE(control.getBody().value_size() == expectedBody.value_size());
+
+ BOOST_CHECK(memcmp(control.getBody().value(), expectedBody.value(),
+ expectedBody.value_size()) == 0);
+
+ }
+
+ bool
+ didCallbackFire() const
+ {
+ return m_callbackFired;
+ }
+
+ void
+ resetCallbackFired()
+ {
+ m_callbackFired = false;
+ }
+
+protected:
+ shared_ptr<InternalFace> m_face;
+
+private:
+ bool m_callbackFired;
+};
+
+class FaceManagerFixture : public TestFaceTableFixture, public TestFaceManagerCommon
+{
+public:
+ FaceManagerFixture()
+ : m_manager(m_faceTable, m_face)
+ {
+ m_manager.setConfigFile(m_config);
+ }
+
+ virtual
+ ~FaceManagerFixture()
+ {
+
+ }
+
+ void
+ parseConfig(const std::string configuration, bool isDryRun)
+ {
+ m_config.parse(configuration, isDryRun, "dummy-config");
+ }
+
+ FaceManager&
+ getManager()
+ {
+ return m_manager;
+ }
+
+ void
+ addInterestRule(const std::string& regex,
+ ndn::IdentityCertificate& certificate)
+ {
+ m_manager.addInterestRule(regex, certificate);
+ }
+
+ bool
+ didFaceTableAddFire() const
+ {
+ return m_faceTable.didAddFire();
+ }
+
+ bool
+ didFaceTableGetFire() const
+ {
+ return m_faceTable.didGetFire();
+ }
+
+ void
+ resetFaceTable()
+ {
+ m_faceTable.reset();
+ }
+
+private:
+ FaceManager m_manager;
+ ConfigFile m_config;
+};
+
+BOOST_FIXTURE_TEST_SUITE(MgmtFaceManager, FaceManagerFixture)
+
+bool
+isExpectedException(const ConfigFile::Error& error, const std::string& expectedMessage)
+{
+ if (error.what() != expectedMessage)
+ {
+ NFD_LOG_ERROR("expected: " << expectedMessage << "\tgot: " << error.what());
+ }
+ return error.what() == expectedMessage;
+}
+
+#ifdef HAVE_UNIX_SOCKETS
+
+BOOST_AUTO_TEST_CASE(TestProcessSectionUnix)
+{
+ const std::string CONFIG =
+ "face_system\n"
+ "{\n"
+ " unix\n"
+ " {\n"
+ " listen yes\n"
+ " path /tmp/nfd.sock\n"
+ " }\n"
+ "}\n";
+ BOOST_TEST_CHECKPOINT("Calling parse");
+ BOOST_CHECK_NO_THROW(parseConfig(CONFIG, false));
+}
+
+BOOST_AUTO_TEST_CASE(TestProcessSectionUnixDryRun)
+{
+ const std::string CONFIG =
+ "face_system\n"
+ "{\n"
+ " unix\n"
+ " {\n"
+ " listen yes\n"
+ " path /var/run/nfd.sock\n"
+ " }\n"
+ "}\n";
+
+ BOOST_CHECK_NO_THROW(parseConfig(CONFIG, true));
+}
+
+BOOST_AUTO_TEST_CASE(TestProcessSectionUnixBadListen)
+{
+ const std::string CONFIG =
+ "face_system\n"
+ "{\n"
+ " unix\n"
+ " {\n"
+ " listen hello\n"
+ " }\n"
+ "}\n";
+ BOOST_CHECK_EXCEPTION(parseConfig(CONFIG, false), ConfigFile::Error,
+ bind(&isExpectedException, _1,
+ "Invalid value for option \"listen\" in \"unix\" section"));
+}
+
+BOOST_AUTO_TEST_CASE(TestProcessSectionUnixUnknownOption)
+{
+ const std::string CONFIG =
+ "face_system\n"
+ "{\n"
+ " unix\n"
+ " {\n"
+ " hello\n"
+ " }\n"
+ "}\n";
+ BOOST_CHECK_EXCEPTION(parseConfig(CONFIG, false), ConfigFile::Error,
+ bind(&isExpectedException, _1,
+ "Unrecognized option \"hello\" in \"unix\" section"));
+}
+
+#endif // HAVE_UNIX_SOCKETS
+
+
+BOOST_AUTO_TEST_CASE(TestProcessSectionTcp)
+{
+ const std::string CONFIG =
+ "face_system\n"
+ "{\n"
+ " tcp\n"
+ " {\n"
+ " listen yes\n"
+ " port 6363\n"
+ " enable_v4 yes\n"
+ " enable_v6 yes\n"
+ " }\n"
+ "}\n";
+ try
+ {
+ parseConfig(CONFIG, false);
+ }
+ catch (const std::runtime_error& e)
+ {
+ const std::string reason = e.what();
+ if (reason.find("Address in use") != std::string::npos)
+ {
+ BOOST_FAIL(reason);
+ }
+ }
+}
+
+BOOST_AUTO_TEST_CASE(TestProcessSectionTcpDryRun)
+{
+ const std::string CONFIG =
+ "face_system\n"
+ "{\n"
+ " tcp\n"
+ " {\n"
+ " listen yes\n"
+ " port 6363\n"
+ " enable_v4 yes\n"
+ " enable_v6 yes\n"
+ " }\n"
+ "}\n";
+ BOOST_CHECK_NO_THROW(parseConfig(CONFIG, true));
+}
+
+BOOST_AUTO_TEST_CASE(TestProcessSectionTcpBadListen)
+{
+ const std::string CONFIG =
+ "face_system\n"
+ "{\n"
+ " tcp\n"
+ " {\n"
+ " listen hello\n"
+ " }\n"
+ "}\n";
+ BOOST_CHECK_EXCEPTION(parseConfig(CONFIG, false), ConfigFile::Error,
+ bind(&isExpectedException, _1,
+ "Invalid value for option \"listen\" in \"tcp\" section"));
+}
+
+BOOST_AUTO_TEST_CASE(TestProcessSectionTcpChannelsDisabled)
+{
+ const std::string CONFIG =
+ "face_system\n"
+ "{\n"
+ " tcp\n"
+ " {\n"
+ " port 6363\n"
+ " enable_v4 no\n"
+ " enable_v6 no\n"
+ " }\n"
+ "}\n";
+ BOOST_CHECK_EXCEPTION(parseConfig(CONFIG, false), ConfigFile::Error,
+ bind(&isExpectedException, _1,
+ "IPv4 and IPv6 channels have been disabled."
+ " Remove \"tcp\" section to disable TCP channels or"
+ " re-enable at least one channel type."));
+}
+
+BOOST_AUTO_TEST_CASE(TestProcessSectionTcpUnknownOption)
+{
+ const std::string CONFIG =
+ "face_system\n"
+ "{\n"
+ " tcp\n"
+ " {\n"
+ " hello\n"
+ " }\n"
+ "}\n";
+ BOOST_CHECK_EXCEPTION(parseConfig(CONFIG, false), ConfigFile::Error,
+ bind(&isExpectedException, _1,
+ "Unrecognized option \"hello\" in \"tcp\" section"));
+}
+
+BOOST_AUTO_TEST_CASE(TestProcessSectionUdp)
+{
+ const std::string CONFIG =
+ "face_system\n"
+ "{\n"
+ " udp\n"
+ " {\n"
+ " port 6363\n"
+ " enable_v4 yes\n"
+ " enable_v6 yes\n"
+ " idle_timeout 30\n"
+ " keep_alive_interval 25\n"
+ " mcast yes\n"
+ " mcast_port 56363\n"
+ " mcast_group 224.0.23.170\n"
+ " }\n"
+ "}\n";
+ BOOST_CHECK_NO_THROW(parseConfig(CONFIG, false));
+}
+
+BOOST_AUTO_TEST_CASE(TestProcessSectionUdpDryRun)
+{
+ const std::string CONFIG =
+ "face_system\n"
+ "{\n"
+ " udp\n"
+ " {\n"
+ " port 6363\n"
+ " idle_timeout 30\n"
+ " keep_alive_interval 25\n"
+ " mcast yes\n"
+ " mcast_port 56363\n"
+ " mcast_group 224.0.23.170\n"
+ " }\n"
+ "}\n";
+ BOOST_CHECK_NO_THROW(parseConfig(CONFIG, true));
+}
+
+BOOST_AUTO_TEST_CASE(TestProcessSectionUdpBadIdleTimeout)
+{
+ const std::string CONFIG =
+ "face_system\n"
+ "{\n"
+ " udp\n"
+ " {\n"
+ " idle_timeout hello\n"
+ " }\n"
+ "}\n";
+
+ BOOST_CHECK_EXCEPTION(parseConfig(CONFIG, false), ConfigFile::Error,
+ bind(&isExpectedException, _1,
+ "Invalid value for option \"idle_timeout\" in \"udp\" section"));
+}
+
+BOOST_AUTO_TEST_CASE(TestProcessSectionUdpBadMcast)
+{
+ const std::string CONFIG =
+ "face_system\n"
+ "{\n"
+ " udp\n"
+ " {\n"
+ " mcast hello\n"
+ " }\n"
+ "}\n";
+
+ BOOST_CHECK_EXCEPTION(parseConfig(CONFIG, false), ConfigFile::Error,
+ bind(&isExpectedException, _1,
+ "Invalid value for option \"mcast\" in \"udp\" section"));
+}
+
+BOOST_AUTO_TEST_CASE(TestProcessSectionUdpBadMcastGroup)
+{
+ const std::string CONFIG =
+ "face_system\n"
+ "{\n"
+ " udp\n"
+ " {\n"
+ " mcast no\n"
+ " mcast_port 50\n"
+ " mcast_group hello\n"
+ " }\n"
+ "}\n";
+
+ BOOST_CHECK_EXCEPTION(parseConfig(CONFIG, false), ConfigFile::Error,
+ bind(&isExpectedException, _1,
+ "Invalid value for option \"mcast_group\" in \"udp\" section"));
+}
+
+BOOST_AUTO_TEST_CASE(TestProcessSectionUdpBadMcastGroupV6)
+{
+ const std::string CONFIG =
+ "face_system\n"
+ "{\n"
+ " udp\n"
+ " {\n"
+ " mcast no\n"
+ " mcast_port 50\n"
+ " mcast_group ::1\n"
+ " }\n"
+ "}\n";
+
+ BOOST_CHECK_EXCEPTION(parseConfig(CONFIG, false), ConfigFile::Error,
+ bind(&isExpectedException, _1,
+ "Invalid value for option \"mcast_group\" in \"udp\" section"));
+}
+
+BOOST_AUTO_TEST_CASE(TestProcessSectionUdpChannelsDisabled)
+{
+ const std::string CONFIG =
+ "face_system\n"
+ "{\n"
+ " udp\n"
+ " {\n"
+ " port 6363\n"
+ " enable_v4 no\n"
+ " enable_v6 no\n"
+ " idle_timeout 30\n"
+ " keep_alive_interval 25\n"
+ " mcast yes\n"
+ " mcast_port 56363\n"
+ " mcast_group 224.0.23.170\n"
+ " }\n"
+ "}\n";
+ BOOST_CHECK_EXCEPTION(parseConfig(CONFIG, false), ConfigFile::Error,
+ bind(&isExpectedException, _1,
+ "IPv4 and IPv6 channels have been disabled."
+ " Remove \"udp\" section to disable UDP channels or"
+ " re-enable at least one channel type."));
+}
+
+BOOST_AUTO_TEST_CASE(TestProcessSectionUdpConflictingMcast)
+{
+ const std::string CONFIG =
+ "face_system\n"
+ "{\n"
+ " udp\n"
+ " {\n"
+ " port 6363\n"
+ " enable_v4 no\n"
+ " enable_v6 yes\n"
+ " idle_timeout 30\n"
+ " keep_alive_interval 25\n"
+ " mcast yes\n"
+ " mcast_port 56363\n"
+ " mcast_group 224.0.23.170\n"
+ " }\n"
+ "}\n";
+ BOOST_CHECK_EXCEPTION(parseConfig(CONFIG, false), ConfigFile::Error,
+ bind(&isExpectedException, _1,
+ "IPv4 multicast requested, but IPv4 channels"
+ " have been disabled (conflicting configuration options set)"));
+}
+
+
+
+BOOST_AUTO_TEST_CASE(TestProcessSectionUdpUnknownOption)
+{
+ const std::string CONFIG =
+ "face_system\n"
+ "{\n"
+ " udp\n"
+ " {\n"
+ " hello\n"
+ " }\n"
+ "}\n";
+ BOOST_CHECK_EXCEPTION(parseConfig(CONFIG, false), ConfigFile::Error,
+ bind(&isExpectedException, _1,
+ "Unrecognized option \"hello\" in \"udp\" section"));
+}
+
+#ifdef HAVE_LIBPCAP
+
+BOOST_AUTO_TEST_CASE(TestProcessSectionEther)
+{
+
+ const std::string CONFIG =
+ "face_system\n"
+ "{\n"
+ " ether\n"
+ " {\n"
+ " mcast yes\n"
+ " mcast_group 01:00:5E:00:17:AA\n"
+ " }\n"
+ "}\n";
+
+ BOOST_CHECK_NO_THROW(parseConfig(CONFIG, false));
+}
+
+BOOST_AUTO_TEST_CASE(TestProcessSectionEtherDryRun)
+{
+ const std::string CONFIG =
+ "face_system\n"
+ "{\n"
+ " ether\n"
+ " {\n"
+ " mcast yes\n"
+ " mcast_group 01:00:5E:00:17:AA\n"
+ " }\n"
+ "}\n";
+
+ BOOST_CHECK_NO_THROW(parseConfig(CONFIG, true));
+}
+
+BOOST_AUTO_TEST_CASE(TestProcessSectionEtherBadMcast)
+{
+ const std::string CONFIG =
+ "face_system\n"
+ "{\n"
+ " ether\n"
+ " {\n"
+ " mcast hello\n"
+ " }\n"
+ "}\n";
+
+ BOOST_CHECK_EXCEPTION(parseConfig(CONFIG, false), ConfigFile::Error,
+ bind(&isExpectedException, _1,
+ "Invalid value for option \"mcast\" in \"ether\" section"));
+}
+
+BOOST_AUTO_TEST_CASE(TestProcessSectionEtherBadMcastGroup)
+{
+ const std::string CONFIG =
+ "face_system\n"
+ "{\n"
+ " ether\n"
+ " {\n"
+ " mcast yes\n"
+ " mcast_group\n"
+ " }\n"
+ "}\n";
+
+ BOOST_CHECK_EXCEPTION(parseConfig(CONFIG, false), ConfigFile::Error,
+ bind(&isExpectedException, _1,
+ "Invalid value for option \"mcast_group\" in \"ether\" section"));
+}
+
+BOOST_AUTO_TEST_CASE(TestProcessSectionEtherUnknownOption)
+{
+ const std::string CONFIG =
+ "face_system\n"
+ "{\n"
+ " ether\n"
+ " {\n"
+ " hello\n"
+ " }\n"
+ "}\n";
+ BOOST_CHECK_EXCEPTION(parseConfig(CONFIG, false), ConfigFile::Error,
+ bind(&isExpectedException, _1,
+ "Unrecognized option \"hello\" in \"ether\" section"));
+}
+
+#endif
+
+BOOST_AUTO_TEST_CASE(TestFireInterestFilter)
+{
+ shared_ptr<Interest> command(make_shared<Interest>("/localhost/nfd/faces"));
+
+ getFace()->onReceiveData +=
+ bind(&FaceManagerFixture::validateControlResponse, this, _1,
+ command->getName(), 400, "Malformed command");
+
+ getFace()->sendInterest(*command);
+ g_io.run_one();
+
+ BOOST_REQUIRE(didCallbackFire());
+}
+
+BOOST_AUTO_TEST_CASE(MalformedCommmand)
+{
+ shared_ptr<Interest> command(make_shared<Interest>("/localhost/nfd/faces"));
+
+ getFace()->onReceiveData +=
+ bind(&FaceManagerFixture::validateControlResponse, this, _1,
+ command->getName(), 400, "Malformed command");
+
+ getManager().onFaceRequest(*command);
+
+ BOOST_REQUIRE(didCallbackFire());
+}
+
+BOOST_AUTO_TEST_CASE(UnsignedCommand)
+{
+ ControlParameters parameters;
+ parameters.setUri("tcp://127.0.0.1");
+
+ Block encodedParameters(parameters.wireEncode());
+
+ Name commandName("/localhost/nfd/faces");
+ commandName.append("create");
+ commandName.append(encodedParameters);
+
+ shared_ptr<Interest> command(make_shared<Interest>(commandName));
+
+ getFace()->onReceiveData +=
+ bind(&FaceManagerFixture::validateControlResponse, this, _1,
+ command->getName(), 401, "Signature required");
+
+ getManager().onFaceRequest(*command);
+
+ BOOST_REQUIRE(didCallbackFire());
+}
+
+BOOST_FIXTURE_TEST_CASE(UnauthorizedCommand, UnauthorizedCommandFixture<FaceManagerFixture>)
+{
+ ControlParameters parameters;
+ parameters.setUri("tcp://127.0.0.1");
+
+ Block encodedParameters(parameters.wireEncode());
+
+ Name commandName("/localhost/nfd/faces");
+ commandName.append("create");
+ commandName.append(encodedParameters);
+
+ shared_ptr<Interest> command(make_shared<Interest>(commandName));
+ generateCommand(*command);
+
+ getFace()->onReceiveData +=
+ bind(&FaceManagerFixture::validateControlResponse, this, _1,
+ command->getName(), 403, "Unauthorized command");
+
+ getManager().onFaceRequest(*command);
+
+ BOOST_REQUIRE(didCallbackFire());
+}
+
+template <typename T> class AuthorizedCommandFixture : public CommandFixture<T>
+{
+public:
+ AuthorizedCommandFixture()
+ {
+ const std::string regex = "^<localhost><nfd><faces>";
+ T::addInterestRule(regex, *CommandFixture<T>::m_certificate);
+ }
+
+ virtual
+ ~AuthorizedCommandFixture()
+ {
+
+ }
+};
+
+BOOST_FIXTURE_TEST_CASE(UnsupportedCommand, AuthorizedCommandFixture<FaceManagerFixture>)
+{
+ ControlParameters parameters;
+
+ Block encodedParameters(parameters.wireEncode());
+
+ Name commandName("/localhost/nfd/faces");
+ commandName.append("unsupported");
+ commandName.append(encodedParameters);
+
+ shared_ptr<Interest> command(make_shared<Interest>(commandName));
+ generateCommand(*command);
+
+ getFace()->onReceiveData +=
+ bind(&FaceManagerFixture::validateControlResponse, this, _1,
+ command->getName(), 501, "Unsupported command");
+
+ getManager().onFaceRequest(*command);
+
+ BOOST_REQUIRE(didCallbackFire());
+}
+
+class ValidatedFaceRequestFixture : public TestFaceTableFixture,
+ public TestFaceManagerCommon,
+ public FaceManager
+{
+public:
+
+ ValidatedFaceRequestFixture()
+ : FaceManager(TestFaceTableFixture::m_faceTable, TestFaceManagerCommon::m_face),
+ m_createFaceFired(false),
+ m_destroyFaceFired(false)
+ {
+
+ }
+
+ virtual void
+ createFace(const Interest& request,
+ ControlParameters& parameters)
+ {
+ m_createFaceFired = true;
+ }
+
+ virtual void
+ destroyFace(const Interest& request,
+ ControlParameters& parameters)
+ {
+ m_destroyFaceFired = true;
+ }
+
+ virtual
+ ~ValidatedFaceRequestFixture()
+ {
+
+ }
+
+ bool
+ didCreateFaceFire() const
+ {
+ return m_createFaceFired;
+ }
+
+ bool
+ didDestroyFaceFire() const
+ {
+ return m_destroyFaceFired;
+ }
+
+private:
+ bool m_createFaceFired;
+ bool m_destroyFaceFired;
+};
+
+BOOST_FIXTURE_TEST_CASE(ValidatedFaceRequestBadOptionParse,
+ AuthorizedCommandFixture<ValidatedFaceRequestFixture>)
+{
+ Name commandName("/localhost/nfd/faces");
+ commandName.append("create");
+ commandName.append("NotReallyParameters");
+
+ shared_ptr<Interest> command(make_shared<Interest>(commandName));
+ generateCommand(*command);
+
+ getFace()->onReceiveData +=
+ bind(&ValidatedFaceRequestFixture::validateControlResponse, this, _1,
+ command->getName(), 400, "Malformed command");
+
+ onValidatedFaceRequest(command);
+
+ BOOST_REQUIRE(didCallbackFire());
+}
+
+BOOST_FIXTURE_TEST_CASE(ValidatedFaceRequestCreateFace,
+ AuthorizedCommandFixture<ValidatedFaceRequestFixture>)
+{
+ ControlParameters parameters;
+ parameters.setUri("tcp://127.0.0.1");
+
+ Block encodedParameters(parameters.wireEncode());
+
+ Name commandName("/localhost/nfd/faces");
+ commandName.append("create");
+ commandName.append(encodedParameters);
+
+ shared_ptr<Interest> command(make_shared<Interest>(commandName));
+ generateCommand(*command);
+
+ onValidatedFaceRequest(command);
+ BOOST_CHECK(didCreateFaceFire());
+}
+
+BOOST_FIXTURE_TEST_CASE(ValidatedFaceRequestDestroyFace,
+ AuthorizedCommandFixture<ValidatedFaceRequestFixture>)
+{
+ ControlParameters parameters;
+ parameters.setUri("tcp://127.0.0.1");
+
+ Block encodedParameters(parameters.wireEncode());
+
+ Name commandName("/localhost/nfd/faces");
+ commandName.append("destroy");
+ commandName.append(encodedParameters);
+
+ shared_ptr<Interest> command(make_shared<Interest>(commandName));
+ generateCommand(*command);
+
+ onValidatedFaceRequest(command);
+ BOOST_CHECK(didDestroyFaceFire());
+}
+
+class FaceTableFixture
+{
+public:
+ FaceTableFixture()
+ : m_faceTable(m_forwarder)
+ {
+ }
+
+ virtual
+ ~FaceTableFixture()
+ {
+ }
+
+protected:
+ Forwarder m_forwarder;
+ FaceTable m_faceTable;
+};
+
+class LocalControlFixture : public FaceTableFixture,
+ public TestFaceManagerCommon,
+ public FaceManager
+{
+public:
+ LocalControlFixture()
+ : FaceManager(FaceTableFixture::m_faceTable, TestFaceManagerCommon::m_face)
+ {
+ }
+};
+
+BOOST_FIXTURE_TEST_CASE(LocalControlInFaceId,
+ AuthorizedCommandFixture<LocalControlFixture>)
+{
+ shared_ptr<LocalFace> dummy = make_shared<DummyLocalFace>();
+ BOOST_REQUIRE(dummy->isLocal());
+ FaceTableFixture::m_faceTable.add(dummy);
+
+ ControlParameters parameters;
+ parameters.setLocalControlFeature(LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID);
+
+ Block encodedParameters(parameters.wireEncode());
+
+ Name enable("/localhost/nfd/faces/enable-local-control");
+ enable.append(encodedParameters);
+
+ shared_ptr<Interest> enableCommand(make_shared<Interest>(enable));
+ enableCommand->setIncomingFaceId(dummy->getId());
+
+ generateCommand(*enableCommand);
+
+ TestFaceManagerCommon::m_face->onReceiveData +=
+ bind(&LocalControlFixture::validateControlResponse, this, _1,
+ enableCommand->getName(), 200, "Success", encodedParameters);
+
+ onValidatedFaceRequest(enableCommand);
+
+ BOOST_REQUIRE(didCallbackFire());
+ BOOST_REQUIRE(dummy->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID));
+ BOOST_CHECK(!dummy->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_NEXT_HOP_FACE_ID));
+
+ TestFaceManagerCommon::m_face->onReceiveData.clear();
+ resetCallbackFired();
+
+ Name disable("/localhost/nfd/faces/disable-local-control");
+ disable.append(encodedParameters);
+
+ shared_ptr<Interest> disableCommand(make_shared<Interest>(disable));
+ disableCommand->setIncomingFaceId(dummy->getId());
+
+ generateCommand(*disableCommand);
+
+ TestFaceManagerCommon::m_face->onReceiveData +=
+ bind(&LocalControlFixture::validateControlResponse, this, _1,
+ disableCommand->getName(), 200, "Success", encodedParameters);
+
+ onValidatedFaceRequest(disableCommand);
+
+ BOOST_REQUIRE(didCallbackFire());
+ BOOST_REQUIRE(!dummy->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID));
+ BOOST_CHECK(!dummy->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_NEXT_HOP_FACE_ID));
+}
+
+BOOST_FIXTURE_TEST_CASE(LocalControlInFaceIdFaceNotFound,
+ AuthorizedCommandFixture<LocalControlFixture>)
+{
+ shared_ptr<LocalFace> dummy = make_shared<DummyLocalFace>();
+ BOOST_REQUIRE(dummy->isLocal());
+ FaceTableFixture::m_faceTable.add(dummy);
+
+ ControlParameters parameters;
+ parameters.setLocalControlFeature(LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID);
+
+ Block encodedParameters(parameters.wireEncode());
+
+ Name enable("/localhost/nfd/faces/enable-local-control");
+ enable.append(encodedParameters);
+
+ shared_ptr<Interest> enableCommand(make_shared<Interest>(enable));
+ enableCommand->setIncomingFaceId(dummy->getId() + 100);
+
+ generateCommand(*enableCommand);
+
+ TestFaceManagerCommon::m_face->onReceiveData +=
+ bind(&LocalControlFixture::validateControlResponse, this, _1,
+ enableCommand->getName(), 410, "Face not found");
+
+ onValidatedFaceRequest(enableCommand);
+
+ BOOST_REQUIRE(didCallbackFire());
+ BOOST_REQUIRE(!dummy->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID));
+ BOOST_CHECK(!dummy->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_NEXT_HOP_FACE_ID));
+
+ TestFaceManagerCommon::m_face->onReceiveData.clear();
+ resetCallbackFired();
+
+ Name disable("/localhost/nfd/faces/disable-local-control");
+ disable.append(encodedParameters);
+
+ shared_ptr<Interest> disableCommand(make_shared<Interest>(disable));
+ disableCommand->setIncomingFaceId(dummy->getId() + 100);
+
+ generateCommand(*disableCommand);
+
+ TestFaceManagerCommon::m_face->onReceiveData +=
+ bind(&LocalControlFixture::validateControlResponse, this, _1,
+ disableCommand->getName(), 410, "Face not found");
+
+ onValidatedFaceRequest(disableCommand);
+
+ BOOST_REQUIRE(didCallbackFire());
+ BOOST_REQUIRE(!dummy->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID));
+ BOOST_CHECK(!dummy->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_NEXT_HOP_FACE_ID));
+}
+
+BOOST_FIXTURE_TEST_CASE(LocalControlMissingFeature,
+ AuthorizedCommandFixture<LocalControlFixture>)
+{
+ shared_ptr<LocalFace> dummy = make_shared<DummyLocalFace>();
+ BOOST_REQUIRE(dummy->isLocal());
+ FaceTableFixture::m_faceTable.add(dummy);
+
+ ControlParameters parameters;
+
+ Block encodedParameters(parameters.wireEncode());
+
+ Name enable("/localhost/nfd/faces/enable-local-control");
+ enable.append(encodedParameters);
+
+ shared_ptr<Interest> enableCommand(make_shared<Interest>(enable));
+ enableCommand->setIncomingFaceId(dummy->getId());
+
+ generateCommand(*enableCommand);
+
+ TestFaceManagerCommon::m_face->onReceiveData +=
+ bind(&LocalControlFixture::validateControlResponse, this, _1,
+ enableCommand->getName(), 400, "Malformed command");
+
+ onValidatedFaceRequest(enableCommand);
+
+ BOOST_REQUIRE(didCallbackFire());
+ BOOST_REQUIRE(!dummy->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID));
+ BOOST_REQUIRE(!dummy->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_NEXT_HOP_FACE_ID));
+
+ TestFaceManagerCommon::m_face->onReceiveData.clear();
+ resetCallbackFired();
+
+ Name disable("/localhost/nfd/faces/disable-local-control");
+ disable.append(encodedParameters);
+
+ shared_ptr<Interest> disableCommand(make_shared<Interest>(disable));
+ disableCommand->setIncomingFaceId(dummy->getId());
+
+ generateCommand(*disableCommand);
+
+ TestFaceManagerCommon::m_face->onReceiveData +=
+ bind(&LocalControlFixture::validateControlResponse, this, _1,
+ disableCommand->getName(), 400, "Malformed command");
+
+ onValidatedFaceRequest(disableCommand);
+
+ BOOST_REQUIRE(didCallbackFire());
+ BOOST_REQUIRE(!dummy->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID));
+ BOOST_REQUIRE(!dummy->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_NEXT_HOP_FACE_ID));
+}
+
+BOOST_FIXTURE_TEST_CASE(LocalControlInFaceIdNonLocal,
+ AuthorizedCommandFixture<LocalControlFixture>)
+{
+ shared_ptr<DummyFace> dummy = make_shared<DummyFace>();
+ BOOST_REQUIRE(!dummy->isLocal());
+ FaceTableFixture::m_faceTable.add(dummy);
+
+ ControlParameters parameters;
+ parameters.setLocalControlFeature(LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID);
+
+ Block encodedParameters(parameters.wireEncode());
+
+ Name enable("/localhost/nfd/faces/enable-local-control");
+ enable.append(encodedParameters);
+
+ shared_ptr<Interest> enableCommand(make_shared<Interest>(enable));
+ enableCommand->setIncomingFaceId(dummy->getId());
+
+ generateCommand(*enableCommand);
+
+ TestFaceManagerCommon::m_face->onReceiveData +=
+ bind(&LocalControlFixture::validateControlResponse, this, _1,
+ enableCommand->getName(), 412, "Face is non-local");
+
+ onValidatedFaceRequest(enableCommand);
+
+ BOOST_REQUIRE(didCallbackFire());
+
+ TestFaceManagerCommon::m_face->onReceiveData.clear();
+ resetCallbackFired();
+
+ Name disable("/localhost/nfd/faces/disable-local-control");
+ enable.append(encodedParameters);
+
+ shared_ptr<Interest> disableCommand(make_shared<Interest>(enable));
+ disableCommand->setIncomingFaceId(dummy->getId());
+
+ generateCommand(*disableCommand);
+
+ TestFaceManagerCommon::m_face->onReceiveData +=
+ bind(&LocalControlFixture::validateControlResponse, this, _1,
+ disableCommand->getName(), 412, "Face is non-local");
+
+ onValidatedFaceRequest(disableCommand);
+
+ BOOST_REQUIRE(didCallbackFire());
+}
+
+BOOST_FIXTURE_TEST_CASE(LocalControlNextHopFaceId,
+ AuthorizedCommandFixture<LocalControlFixture>)
+{
+ shared_ptr<LocalFace> dummy = make_shared<DummyLocalFace>();
+ BOOST_REQUIRE(dummy->isLocal());
+ FaceTableFixture::m_faceTable.add(dummy);
+
+ ControlParameters parameters;
+ parameters.setLocalControlFeature(LOCAL_CONTROL_FEATURE_NEXT_HOP_FACE_ID);
+
+ Block encodedParameters(parameters.wireEncode());
+
+ Name enable("/localhost/nfd/faces/enable-local-control");
+ enable.append(encodedParameters);
+
+ shared_ptr<Interest> enableCommand(make_shared<Interest>(enable));
+ enableCommand->setIncomingFaceId(dummy->getId());
+
+ generateCommand(*enableCommand);
+
+ TestFaceManagerCommon::m_face->onReceiveData +=
+ bind(&LocalControlFixture::validateControlResponse, this, _1,
+ enableCommand->getName(), 200, "Success", encodedParameters);
+
+ onValidatedFaceRequest(enableCommand);
+
+ BOOST_REQUIRE(didCallbackFire());
+ BOOST_REQUIRE(dummy->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_NEXT_HOP_FACE_ID));
+ BOOST_CHECK(!dummy->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID));
+
+
+ TestFaceManagerCommon::m_face->onReceiveData.clear();
+ resetCallbackFired();
+
+ Name disable("/localhost/nfd/faces/disable-local-control");
+ disable.append(encodedParameters);
+
+ shared_ptr<Interest> disableCommand(make_shared<Interest>(disable));
+ disableCommand->setIncomingFaceId(dummy->getId());
+
+ generateCommand(*disableCommand);
+
+ TestFaceManagerCommon::m_face->onReceiveData +=
+ bind(&LocalControlFixture::validateControlResponse, this, _1,
+ disableCommand->getName(), 200, "Success", encodedParameters);
+
+ onValidatedFaceRequest(disableCommand);
+
+ BOOST_REQUIRE(didCallbackFire());
+ BOOST_REQUIRE(!dummy->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_NEXT_HOP_FACE_ID));
+ BOOST_CHECK(!dummy->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID));
+}
+
+BOOST_FIXTURE_TEST_CASE(LocalControlNextHopFaceIdFaceNotFound,
+ AuthorizedCommandFixture<LocalControlFixture>)
+{
+ shared_ptr<LocalFace> dummy = make_shared<DummyLocalFace>();
+ BOOST_REQUIRE(dummy->isLocal());
+ FaceTableFixture::m_faceTable.add(dummy);
+
+ ControlParameters parameters;
+ parameters.setLocalControlFeature(LOCAL_CONTROL_FEATURE_NEXT_HOP_FACE_ID);
+
+ Block encodedParameters(parameters.wireEncode());
+
+ Name enable("/localhost/nfd/faces/enable-local-control");
+ enable.append(encodedParameters);
+
+ shared_ptr<Interest> enableCommand(make_shared<Interest>(enable));
+ enableCommand->setIncomingFaceId(dummy->getId() + 100);
+
+ generateCommand(*enableCommand);
+
+ TestFaceManagerCommon::m_face->onReceiveData +=
+ bind(&LocalControlFixture::validateControlResponse, this, _1,
+ enableCommand->getName(), 410, "Face not found");
+
+ onValidatedFaceRequest(enableCommand);
+
+ BOOST_REQUIRE(didCallbackFire());
+ BOOST_REQUIRE(!dummy->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_NEXT_HOP_FACE_ID));
+ BOOST_CHECK(!dummy->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID));
+
+
+ TestFaceManagerCommon::m_face->onReceiveData.clear();
+ resetCallbackFired();
+
+ Name disable("/localhost/nfd/faces/disable-local-control");
+ disable.append(encodedParameters);
+
+ shared_ptr<Interest> disableCommand(make_shared<Interest>(disable));
+ disableCommand->setIncomingFaceId(dummy->getId() + 100);
+
+ generateCommand(*disableCommand);
+
+ TestFaceManagerCommon::m_face->onReceiveData +=
+ bind(&LocalControlFixture::validateControlResponse, this, _1,
+ disableCommand->getName(), 410, "Face not found");
+
+ onValidatedFaceRequest(disableCommand);
+
+ BOOST_REQUIRE(didCallbackFire());
+ BOOST_REQUIRE(!dummy->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_NEXT_HOP_FACE_ID));
+ BOOST_CHECK(!dummy->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID));
+}
+
+BOOST_FIXTURE_TEST_CASE(LocalControlNextHopFaceIdNonLocal,
+ AuthorizedCommandFixture<LocalControlFixture>)
+{
+ shared_ptr<DummyFace> dummy = make_shared<DummyFace>();
+ BOOST_REQUIRE(!dummy->isLocal());
+ FaceTableFixture::m_faceTable.add(dummy);
+
+ ControlParameters parameters;
+ parameters.setLocalControlFeature(LOCAL_CONTROL_FEATURE_NEXT_HOP_FACE_ID);
+
+ Block encodedParameters(parameters.wireEncode());
+
+ Name enable("/localhost/nfd/faces/enable-local-control");
+ enable.append(encodedParameters);
+
+ shared_ptr<Interest> enableCommand(make_shared<Interest>(enable));
+ enableCommand->setIncomingFaceId(dummy->getId());
+
+ generateCommand(*enableCommand);
+
+ TestFaceManagerCommon::m_face->onReceiveData +=
+ bind(&LocalControlFixture::validateControlResponse, this, _1,
+ enableCommand->getName(), 412, "Face is non-local");
+
+ onValidatedFaceRequest(enableCommand);
+
+ BOOST_REQUIRE(didCallbackFire());
+
+ TestFaceManagerCommon::m_face->onReceiveData.clear();
+ resetCallbackFired();
+
+ Name disable("/localhost/nfd/faces/disable-local-control");
+ disable.append(encodedParameters);
+
+ shared_ptr<Interest> disableCommand(make_shared<Interest>(disable));
+ disableCommand->setIncomingFaceId(dummy->getId());
+
+ generateCommand(*disableCommand);
+
+ TestFaceManagerCommon::m_face->onReceiveData +=
+ bind(&LocalControlFixture::validateControlResponse, this, _1,
+ disableCommand->getName(), 412, "Face is non-local");
+
+ onValidatedFaceRequest(disableCommand);
+
+ BOOST_REQUIRE(didCallbackFire());
+}
+
+class FaceFixture : public FaceTableFixture,
+ public TestFaceManagerCommon,
+ public FaceManager
+{
+public:
+ FaceFixture()
+ : FaceManager(FaceTableFixture::m_faceTable,
+ TestFaceManagerCommon::m_face)
+ , m_receivedNotification(false)
+ {
+
+ }
+
+ virtual
+ ~FaceFixture()
+ {
+
+ }
+
+ void
+ callbackDispatch(const Data& response,
+ const Name& expectedName,
+ uint32_t expectedCode,
+ const std::string& expectedText,
+ const Block& expectedBody,
+ const ndn::nfd::FaceEventNotification& expectedFaceEvent)
+ {
+ Block payload = response.getContent().blockFromValue();
+ if (payload.type() == ndn::tlv::nfd::ControlResponse)
+ {
+ validateControlResponse(response, expectedName, expectedCode,
+ expectedText, expectedBody);
+ }
+ else if (payload.type() == ndn::tlv::nfd::FaceEventNotification)
+ {
+ validateFaceEvent(payload, expectedFaceEvent);
+ }
+ else
+ {
+ BOOST_FAIL("Received unknown message type: #" << payload.type());
+ }
+ }
+
+ void
+ callbackDispatch(const Data& response,
+ const Name& expectedName,
+ uint32_t expectedCode,
+ const std::string& expectedText,
+ const ndn::nfd::FaceEventNotification& expectedFaceEvent)
+ {
+ Block payload = response.getContent().blockFromValue();
+ if (payload.type() == ndn::tlv::nfd::ControlResponse)
+ {
+ validateControlResponse(response, expectedName,
+ expectedCode, expectedText);
+ }
+ else if (payload.type() == ndn::tlv::nfd::FaceEventNotification)
+ {
+ validateFaceEvent(payload, expectedFaceEvent);
+ }
+ else
+ {
+ BOOST_FAIL("Received unknown message type: #" << payload.type());
+ }
+ }
+
+ void
+ validateFaceEvent(const Block& wire,
+ const ndn::nfd::FaceEventNotification& expectedFaceEvent)
+ {
+
+ m_receivedNotification = true;
+
+ ndn::nfd::FaceEventNotification notification(wire);
+
+ BOOST_CHECK_EQUAL(notification.getKind(), expectedFaceEvent.getKind());
+ BOOST_CHECK_EQUAL(notification.getFaceId(), expectedFaceEvent.getFaceId());
+ BOOST_CHECK_EQUAL(notification.getRemoteUri(), expectedFaceEvent.getRemoteUri());
+ BOOST_CHECK_EQUAL(notification.getLocalUri(), expectedFaceEvent.getLocalUri());
+ }
+
+ bool
+ didReceiveNotication() const
+ {
+ return m_receivedNotification;
+ }
+
+protected:
+ bool m_receivedNotification;
+};
+
+BOOST_FIXTURE_TEST_CASE(CreateFaceBadUri, AuthorizedCommandFixture<FaceFixture>)
+{
+ ControlParameters parameters;
+ parameters.setUri("tcp:/127.0.0.1");
+
+ Block encodedParameters(parameters.wireEncode());
+
+ Name commandName("/localhost/nfd/faces");
+ commandName.append("create");
+ commandName.append(encodedParameters);
+
+ shared_ptr<Interest> command(make_shared<Interest>(commandName));
+ generateCommand(*command);
+
+ getFace()->onReceiveData +=
+ bind(&FaceFixture::validateControlResponse, this, _1,
+ command->getName(), 400, "Malformed command");
+
+ createFace(*command, parameters);
+
+ BOOST_REQUIRE(didCallbackFire());
+}
+
+BOOST_FIXTURE_TEST_CASE(CreateFaceMissingUri, AuthorizedCommandFixture<FaceFixture>)
+{
+ ControlParameters parameters;
+
+ Block encodedParameters(parameters.wireEncode());
+
+ Name commandName("/localhost/nfd/faces");
+ commandName.append("create");
+ commandName.append(encodedParameters);
+
+ shared_ptr<Interest> command(make_shared<Interest>(commandName));
+ generateCommand(*command);
+
+ getFace()->onReceiveData +=
+ bind(&FaceFixture::validateControlResponse, this, _1,
+ command->getName(), 400, "Malformed command");
+
+ createFace(*command, parameters);
+
+ BOOST_REQUIRE(didCallbackFire());
+}
+
+BOOST_FIXTURE_TEST_CASE(CreateFaceUnknownScheme, AuthorizedCommandFixture<FaceFixture>)
+{
+ ControlParameters parameters;
+ // this will be an unsupported protocol because no factories have been
+ // added to the face manager
+ parameters.setUri("tcp://127.0.0.1");
+
+ Block encodedParameters(parameters.wireEncode());
+
+ Name commandName("/localhost/nfd/faces");
+ commandName.append("create");
+ commandName.append(encodedParameters);
+
+ shared_ptr<Interest> command(make_shared<Interest>(commandName));
+ generateCommand(*command);
+
+ getFace()->onReceiveData +=
+ bind(&FaceFixture::validateControlResponse, this, _1,
+ command->getName(), 501, "Unsupported protocol");
+
+ createFace(*command, parameters);
+
+ BOOST_REQUIRE(didCallbackFire());
+}
+
+BOOST_FIXTURE_TEST_CASE(OnCreated, AuthorizedCommandFixture<FaceFixture>)
+{
+ ControlParameters parameters;
+ parameters.setUri("tcp://127.0.0.1");
+
+ Block encodedParameters(parameters.wireEncode());
+
+ Name commandName("/localhost/nfd/faces");
+ commandName.append("create");
+ commandName.append(encodedParameters);
+
+ shared_ptr<Interest> command(make_shared<Interest>(commandName));
+ generateCommand(*command);
+
+ ControlParameters resultParameters;
+ resultParameters.setUri("tcp://127.0.0.1");
+ resultParameters.setFaceId(1);
+
+ shared_ptr<DummyFace> dummy(make_shared<DummyFace>());
+
+ ndn::nfd::FaceEventNotification expectedFaceEvent;
+ expectedFaceEvent.setKind(ndn::nfd::FACE_EVENT_CREATED)
+ .setFaceId(1)
+ .setRemoteUri(dummy->getRemoteUri().toString())
+ .setLocalUri(dummy->getLocalUri().toString())
+ .setFlags(0);
+
+ Block encodedResultParameters(resultParameters.wireEncode());
+
+ getFace()->onReceiveData +=
+ bind(&FaceFixture::callbackDispatch, this, _1,
+ command->getName(), 200, "Success",
+ encodedResultParameters, expectedFaceEvent);
+
+ onCreated(command->getName(), parameters, dummy);
+
+ BOOST_REQUIRE(didCallbackFire());
+ BOOST_REQUIRE(didReceiveNotication());
+}
+
+BOOST_FIXTURE_TEST_CASE(OnConnectFailed, AuthorizedCommandFixture<FaceFixture>)
+{
+ ControlParameters parameters;
+ parameters.setUri("tcp://127.0.0.1");
+
+ Block encodedParameters(parameters.wireEncode());
+
+ Name commandName("/localhost/nfd/faces");
+ commandName.append("create");
+ commandName.append(encodedParameters);
+
+ shared_ptr<Interest> command(make_shared<Interest>(commandName));
+ generateCommand(*command);
+
+ getFace()->onReceiveData +=
+ bind(&FaceFixture::validateControlResponse, this, _1,
+ command->getName(), 408, "unit-test-reason");
+
+ onConnectFailed(command->getName(), "unit-test-reason");
+
+ BOOST_REQUIRE(didCallbackFire());
+ BOOST_CHECK_EQUAL(didReceiveNotication(), false);
+}
+
+
+BOOST_FIXTURE_TEST_CASE(DestroyFace, AuthorizedCommandFixture<FaceFixture>)
+{
+ shared_ptr<DummyFace> dummy(make_shared<DummyFace>());
+ FaceTableFixture::m_faceTable.add(dummy);
+
+ ControlParameters parameters;
+ parameters.setFaceId(dummy->getId());
+
+ Block encodedParameters(parameters.wireEncode());
+
+ Name commandName("/localhost/nfd/faces");
+ commandName.append("destroy");
+ commandName.append(encodedParameters);
+
+ shared_ptr<Interest> command(make_shared<Interest>(commandName));
+ generateCommand(*command);
+
+ ndn::nfd::FaceEventNotification expectedFaceEvent;
+ expectedFaceEvent.setKind(ndn::nfd::FACE_EVENT_DESTROYED)
+ .setFaceId(dummy->getId())
+ .setRemoteUri(dummy->getRemoteUri().toString())
+ .setLocalUri(dummy->getLocalUri().toString())
+ .setFlags(0);
+
+ getFace()->onReceiveData +=
+ bind(&FaceFixture::callbackDispatch, this, _1,
+ command->getName(), 200, "Success", boost::ref(encodedParameters), expectedFaceEvent);
+
+ destroyFace(*command, parameters);
+
+ BOOST_REQUIRE(didCallbackFire());
+ BOOST_REQUIRE(didReceiveNotication());
+}
+
+class FaceListFixture : public FaceStatusPublisherFixture
+{
+public:
+ FaceListFixture()
+ : m_manager(m_table, m_face)
+ {
+
+ }
+
+ virtual
+ ~FaceListFixture()
+ {
+
+ }
+
+protected:
+ FaceManager m_manager;
+};
+
+BOOST_FIXTURE_TEST_CASE(TestFaceList, FaceListFixture)
+
+{
+ Name commandName("/localhost/nfd/faces/list");
+ shared_ptr<Interest> command(make_shared<Interest>(commandName));
+
+ // MAX_SEGMENT_SIZE == 4400, FaceStatus size with filler counters is 55
+ // 55 divides 4400 (== 80), so only use 79 FaceStatuses and then two smaller ones
+ // to force a FaceStatus to span Data packets
+ for (int i = 0; i < 79; i++)
+ {
+ shared_ptr<TestCountersFace> dummy(make_shared<TestCountersFace>());
+
+ uint64_t filler = std::numeric_limits<uint64_t>::max() - 1;
+ dummy->setCounters(filler, filler, filler, filler);
+
+ m_referenceFaces.push_back(dummy);
+
+ add(dummy);
+ }
+
+ for (int i = 0; i < 2; i++)
+ {
+ shared_ptr<TestCountersFace> dummy(make_shared<TestCountersFace>());
+ uint64_t filler = std::numeric_limits<uint32_t>::max() - 1;
+ dummy->setCounters(filler, filler, filler, filler);
+
+ m_referenceFaces.push_back(dummy);
+
+ add(dummy);
+ }
+
+ ndn::EncodingBuffer buffer;
+
+ m_face->onReceiveData +=
+ bind(&FaceStatusPublisherFixture::decodeFaceStatusBlock, this, _1);
+
+ m_manager.listFaces(*command);
+ BOOST_REQUIRE(m_finished);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+} // namespace nfd
diff --git a/tests/daemon/mgmt/face-status-publisher-common.hpp b/tests/daemon/mgmt/face-status-publisher-common.hpp
new file mode 100644
index 0000000..11ed191
--- /dev/null
+++ b/tests/daemon/mgmt/face-status-publisher-common.hpp
@@ -0,0 +1,192 @@
+/* -*- 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_MGMT_FACE_STATUS_PUBLISHER_COMMON_HPP
+#define NFD_TESTS_NFD_MGMT_FACE_STATUS_PUBLISHER_COMMON_HPP
+
+#include "mgmt/face-status-publisher.hpp"
+#include "mgmt/app-face.hpp"
+#include "mgmt/internal-face.hpp"
+#include "mgmt/face-flags.hpp"
+#include "fw/forwarder.hpp"
+
+#include "tests/test-common.hpp"
+#include "tests/daemon/face/dummy-face.hpp"
+
+#include <ndn-cpp-dev/management/nfd-face-status.hpp>
+
+namespace nfd {
+namespace tests {
+
+class TestCountersFace : public DummyFace
+{
+public:
+
+ TestCountersFace()
+ {
+ }
+
+ virtual
+ ~TestCountersFace()
+ {
+ }
+
+ void
+ setCounters(FaceCounter nInInterests,
+ FaceCounter nInDatas,
+ FaceCounter nOutInterests,
+ FaceCounter nOutDatas)
+ {
+ FaceCounters& counters = getMutableCounters();
+ counters.getNInInterests() = nInInterests;
+ counters.getNInDatas() = nInDatas;
+ counters.getNOutInterests() = nOutInterests;
+ counters.getNOutDatas() = nOutDatas;
+ }
+
+
+};
+
+static inline uint64_t
+readNonNegativeIntegerType(const Block& block,
+ uint32_t type)
+{
+ if (block.type() == type)
+ {
+ return readNonNegativeInteger(block);
+ }
+ std::stringstream error;
+ error << "expected type " << type << " got " << block.type();
+ throw ndn::Tlv::Error(error.str());
+}
+
+static inline uint64_t
+checkedReadNonNegativeIntegerType(Block::element_const_iterator& i,
+ Block::element_const_iterator end,
+ uint32_t type)
+{
+ if (i != end)
+ {
+ const Block& block = *i;
+ ++i;
+ return readNonNegativeIntegerType(block, type);
+ }
+ throw ndn::Tlv::Error("Unexpected end of FaceStatus");
+}
+
+class FaceStatusPublisherFixture : public BaseFixture
+{
+public:
+
+ FaceStatusPublisherFixture()
+ : m_table(m_forwarder)
+ , m_face(make_shared<InternalFace>())
+ , m_publisher(m_table, m_face, "/localhost/nfd/FaceStatusPublisherFixture")
+ , m_finished(false)
+ {
+
+ }
+
+ virtual
+ ~FaceStatusPublisherFixture()
+ {
+
+ }
+
+ void
+ add(shared_ptr<Face> face)
+ {
+ m_table.add(face);
+ }
+
+ void
+ validateFaceStatus(const Block& statusBlock, const shared_ptr<Face>& reference)
+ {
+ ndn::nfd::FaceStatus status;
+ BOOST_REQUIRE_NO_THROW(status.wireDecode(statusBlock));
+ const FaceCounters& counters = reference->getCounters();
+
+ BOOST_CHECK_EQUAL(status.getFaceId(), reference->getId());
+ BOOST_CHECK_EQUAL(status.getRemoteUri(), reference->getRemoteUri().toString());
+ BOOST_CHECK_EQUAL(status.getLocalUri(), reference->getLocalUri().toString());
+ BOOST_CHECK_EQUAL(status.getFlags(), getFaceFlags(*reference));
+ BOOST_CHECK_EQUAL(status.getNInInterests(), counters.getNInInterests());
+ BOOST_CHECK_EQUAL(status.getNInDatas(), counters.getNInDatas());
+ BOOST_CHECK_EQUAL(status.getNOutInterests(), counters.getNOutInterests());
+ BOOST_CHECK_EQUAL(status.getNOutDatas(), counters.getNOutDatas());
+ }
+
+ void
+ decodeFaceStatusBlock(const Data& data)
+ {
+ Block payload = data.getContent();
+
+ m_buffer.appendByteArray(payload.value(), payload.value_size());
+
+ BOOST_CHECK_NO_THROW(data.getName()[-1].toSegment());
+ if (data.getFinalBlockId() != data.getName()[-1])
+ {
+ return;
+ }
+
+ // wrap the Face Statuses in a single Content TLV for easy parsing
+ m_buffer.prependVarNumber(m_buffer.size());
+ m_buffer.prependVarNumber(ndn::Tlv::Content);
+
+ ndn::Block parser(m_buffer.buf(), m_buffer.size());
+ parser.parse();
+
+ BOOST_REQUIRE_EQUAL(parser.elements_size(), m_referenceFaces.size());
+
+ std::list<shared_ptr<Face> >::const_iterator iReference = m_referenceFaces.begin();
+ for (Block::element_const_iterator i = parser.elements_begin();
+ i != parser.elements_end();
+ ++i)
+ {
+ if (i->type() != ndn::tlv::nfd::FaceStatus)
+ {
+ BOOST_FAIL("expected face status, got type #" << i->type());
+ }
+ validateFaceStatus(*i, *iReference);
+ ++iReference;
+ }
+ m_finished = true;
+ }
+
+protected:
+ Forwarder m_forwarder;
+ FaceTable m_table;
+ shared_ptr<InternalFace> m_face;
+ FaceStatusPublisher m_publisher;
+ ndn::EncodingBuffer m_buffer;
+ std::list<shared_ptr<Face> > m_referenceFaces;
+
+protected:
+ bool m_finished;
+};
+
+} // namespace tests
+} // namespace nfd
+
+#endif // NFD_TESTS_NFD_MGMT_FACE_STATUS_PUBLISHER_COMMON_HPP
diff --git a/tests/daemon/mgmt/face-status-publisher.cpp b/tests/daemon/mgmt/face-status-publisher.cpp
new file mode 100644
index 0000000..836ea2d
--- /dev/null
+++ b/tests/daemon/mgmt/face-status-publisher.cpp
@@ -0,0 +1,77 @@
+/* -*- 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-status-publisher-common.hpp"
+
+namespace nfd {
+namespace tests {
+
+NFD_LOG_INIT("FaceStatusPublisherTest");
+
+BOOST_FIXTURE_TEST_SUITE(MgmtFaceSatusPublisher, FaceStatusPublisherFixture)
+
+BOOST_AUTO_TEST_CASE(EncodingDecoding)
+{
+ Name commandName("/localhost/nfd/faces/list");
+ shared_ptr<Interest> command(make_shared<Interest>(commandName));
+
+ // MAX_SEGMENT_SIZE == 4400, FaceStatus size with filler counters is 55
+ // 55 divides 4400 (== 80), so only use 79 FaceStatuses and then two smaller ones
+ // to force a FaceStatus to span Data packets
+ for (int i = 0; i < 79; i++)
+ {
+ shared_ptr<TestCountersFace> dummy(make_shared<TestCountersFace>());
+
+ uint64_t filler = std::numeric_limits<uint64_t>::max() - 1;
+ dummy->setCounters(filler, filler, filler, filler);
+
+ m_referenceFaces.push_back(dummy);
+
+ add(dummy);
+ }
+
+ for (int i = 0; i < 2; i++)
+ {
+ shared_ptr<TestCountersFace> dummy(make_shared<TestCountersFace>());
+ uint64_t filler = std::numeric_limits<uint32_t>::max() - 1;
+ dummy->setCounters(filler, filler, filler, filler);
+
+ m_referenceFaces.push_back(dummy);
+
+ add(dummy);
+ }
+
+ ndn::EncodingBuffer buffer;
+
+ m_face->onReceiveData +=
+ bind(&FaceStatusPublisherFixture::decodeFaceStatusBlock, this, _1);
+
+ m_publisher.publish();
+ BOOST_REQUIRE(m_finished);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+} // namespace nfd
diff --git a/tests/daemon/mgmt/fib-enumeration-publisher-common.hpp b/tests/daemon/mgmt/fib-enumeration-publisher-common.hpp
new file mode 100644
index 0000000..6befe54
--- /dev/null
+++ b/tests/daemon/mgmt/fib-enumeration-publisher-common.hpp
@@ -0,0 +1,219 @@
+/* -*- 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_MGMT_FIB_ENUMERATION_PUBLISHER_COMMON_HPP
+#define NFD_TESTS_NFD_MGMT_FIB_ENUMERATION_PUBLISHER_COMMON_HPP
+
+#include "mgmt/fib-enumeration-publisher.hpp"
+
+#include "mgmt/app-face.hpp"
+#include "mgmt/internal-face.hpp"
+#include "table/fib.hpp"
+#include "table/name-tree.hpp"
+
+#include "tests/test-common.hpp"
+#include "../face/dummy-face.hpp"
+
+#include <ndn-cpp-dev/encoding/tlv.hpp>
+
+namespace nfd {
+namespace tests {
+
+static inline uint64_t
+readNonNegativeIntegerType(const Block& block,
+ uint32_t type)
+{
+ if (block.type() == type)
+ {
+ return readNonNegativeInteger(block);
+ }
+ std::stringstream error;
+ error << "Expected type " << type << " got " << block.type();
+ throw ndn::Tlv::Error(error.str());
+}
+
+static inline uint64_t
+checkedReadNonNegativeIntegerType(Block::element_const_iterator& i,
+ Block::element_const_iterator end,
+ uint32_t type)
+{
+ if (i != end)
+ {
+ const Block& block = *i;
+ ++i;
+ return readNonNegativeIntegerType(block, type);
+ }
+ std::stringstream error;
+ error << "Unexpected end of Block while attempting to read type #"
+ << type;
+ throw ndn::Tlv::Error(error.str());
+}
+
+class FibEnumerationPublisherFixture : public BaseFixture
+{
+public:
+
+ FibEnumerationPublisherFixture()
+ : m_fib(m_nameTree)
+ , m_face(make_shared<InternalFace>())
+ , m_publisher(m_fib, m_face, "/localhost/nfd/FibEnumerationPublisherFixture")
+ , m_finished(false)
+ {
+ }
+
+ virtual
+ ~FibEnumerationPublisherFixture()
+ {
+ }
+
+ bool
+ hasNextHopWithCost(const fib::NextHopList& nextHops,
+ FaceId faceId,
+ uint64_t cost)
+ {
+ for (fib::NextHopList::const_iterator i = nextHops.begin();
+ i != nextHops.end();
+ ++i)
+ {
+ if (i->getFace()->getId() == faceId && i->getCost() == cost)
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ bool
+ entryHasPrefix(const shared_ptr<fib::Entry> entry, const Name& prefix)
+ {
+ return entry->getPrefix() == prefix;
+ }
+
+ void
+ validateFibEntry(const Block& entry)
+ {
+ entry.parse();
+
+ Block::element_const_iterator i = entry.elements_begin();
+ BOOST_REQUIRE(i != entry.elements_end());
+
+
+ BOOST_REQUIRE(i->type() == ndn::Tlv::Name);
+ Name prefix(*i);
+ ++i;
+
+ std::set<shared_ptr<fib::Entry> >::const_iterator referenceIter =
+ std::find_if(m_referenceEntries.begin(), m_referenceEntries.end(),
+ boost::bind(&FibEnumerationPublisherFixture::entryHasPrefix,
+ this, _1, prefix));
+
+ BOOST_REQUIRE(referenceIter != m_referenceEntries.end());
+
+ const shared_ptr<fib::Entry>& reference = *referenceIter;
+ BOOST_REQUIRE_EQUAL(prefix, reference->getPrefix());
+
+ // 0 or more next hop records
+ size_t nRecords = 0;
+ const fib::NextHopList& referenceNextHops = reference->getNextHops();
+ for (; i != entry.elements_end(); ++i)
+ {
+ const ndn::Block& nextHopRecord = *i;
+ BOOST_REQUIRE(nextHopRecord.type() == ndn::tlv::nfd::NextHopRecord);
+ nextHopRecord.parse();
+
+ Block::element_const_iterator j = nextHopRecord.elements_begin();
+
+ FaceId faceId =
+ checkedReadNonNegativeIntegerType(j,
+ entry.elements_end(),
+ ndn::tlv::nfd::FaceId);
+
+ uint64_t cost =
+ checkedReadNonNegativeIntegerType(j,
+ entry.elements_end(),
+ ndn::tlv::nfd::Cost);
+
+ BOOST_REQUIRE(hasNextHopWithCost(referenceNextHops, faceId, cost));
+
+ BOOST_REQUIRE(j == nextHopRecord.elements_end());
+ nRecords++;
+ }
+ BOOST_REQUIRE_EQUAL(nRecords, referenceNextHops.size());
+
+ BOOST_REQUIRE(i == entry.elements_end());
+ m_referenceEntries.erase(referenceIter);
+ }
+
+ void
+ decodeFibEntryBlock(const Data& data)
+ {
+ Block payload = data.getContent();
+
+ m_buffer.appendByteArray(payload.value(), payload.value_size());
+
+ BOOST_CHECK_NO_THROW(data.getName()[-1].toSegment());
+ if (data.getFinalBlockId() != data.getName()[-1])
+ {
+ return;
+ }
+
+ // wrap the FIB Entry blocks in a single Content TLV for easy parsing
+ m_buffer.prependVarNumber(m_buffer.size());
+ m_buffer.prependVarNumber(ndn::Tlv::Content);
+
+ ndn::Block parser(m_buffer.buf(), m_buffer.size());
+ parser.parse();
+
+ BOOST_REQUIRE_EQUAL(parser.elements_size(), m_referenceEntries.size());
+
+ for (Block::element_const_iterator i = parser.elements_begin();
+ i != parser.elements_end();
+ ++i)
+ {
+ if (i->type() != ndn::tlv::nfd::FibEntry)
+ {
+ BOOST_FAIL("expected fib entry, got type #" << i->type());
+ }
+
+ validateFibEntry(*i);
+ }
+ m_finished = true;
+ }
+
+protected:
+ NameTree m_nameTree;
+ Fib m_fib;
+ shared_ptr<InternalFace> m_face;
+ FibEnumerationPublisher m_publisher;
+ ndn::EncodingBuffer m_buffer;
+ std::set<shared_ptr<fib::Entry> > m_referenceEntries;
+
+protected:
+ bool m_finished;
+};
+
+} // namespace tests
+} // namespace nfd
+
+#endif // NFD_TESTS_NFD_MGMT_FIB_ENUMERATION_PUBLISHER_COMMON_HPP
diff --git a/tests/daemon/mgmt/fib-enumeration-publisher.cpp b/tests/daemon/mgmt/fib-enumeration-publisher.cpp
new file mode 100644
index 0000000..725d51e
--- /dev/null
+++ b/tests/daemon/mgmt/fib-enumeration-publisher.cpp
@@ -0,0 +1,89 @@
+/* -*- 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 "mgmt/fib-enumeration-publisher.hpp"
+
+#include "mgmt/app-face.hpp"
+#include "mgmt/internal-face.hpp"
+
+#include "tests/test-common.hpp"
+#include "../face/dummy-face.hpp"
+
+#include "fib-enumeration-publisher-common.hpp"
+
+#include <ndn-cpp-dev/encoding/tlv.hpp>
+
+namespace nfd {
+namespace tests {
+
+NFD_LOG_INIT("TestFibEnumerationPublisher");
+
+
+
+BOOST_FIXTURE_TEST_SUITE(MgmtFibEnumeration, FibEnumerationPublisherFixture)
+
+BOOST_AUTO_TEST_CASE(TestFibEnumerationPublisher)
+{
+ for (int i = 0; i < 87; i++)
+ {
+ Name prefix("/test");
+ prefix.appendSegment(i);
+
+ shared_ptr<DummyFace> dummy1(make_shared<DummyFace>());
+ shared_ptr<DummyFace> dummy2(make_shared<DummyFace>());
+
+ shared_ptr<fib::Entry> entry = m_fib.insert(prefix).first;
+ entry->addNextHop(dummy1, std::numeric_limits<uint64_t>::max() - 1);
+ entry->addNextHop(dummy2, std::numeric_limits<uint64_t>::max() - 2);
+
+ m_referenceEntries.insert(entry);
+ }
+ for (int i = 0; i < 2; i++)
+ {
+ Name prefix("/test2");
+ prefix.appendSegment(i);
+
+ shared_ptr<DummyFace> dummy1(make_shared<DummyFace>());
+ shared_ptr<DummyFace> dummy2(make_shared<DummyFace>());
+
+ shared_ptr<fib::Entry> entry = m_fib.insert(prefix).first;
+ entry->addNextHop(dummy1, std::numeric_limits<uint8_t>::max() - 1);
+ entry->addNextHop(dummy2, std::numeric_limits<uint8_t>::max() - 2);
+
+ m_referenceEntries.insert(entry);
+ }
+
+ ndn::EncodingBuffer buffer;
+
+ m_face->onReceiveData +=
+ bind(&FibEnumerationPublisherFixture::decodeFibEntryBlock, this, _1);
+
+ m_publisher.publish();
+ BOOST_REQUIRE(m_finished);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+} // namespace nfd
diff --git a/tests/daemon/mgmt/fib-manager.cpp b/tests/daemon/mgmt/fib-manager.cpp
new file mode 100644
index 0000000..b38c3b8
--- /dev/null
+++ b/tests/daemon/mgmt/fib-manager.cpp
@@ -0,0 +1,920 @@
+/* -*- 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 "mgmt/fib-manager.hpp"
+#include "table/fib.hpp"
+#include "table/fib-nexthop.hpp"
+#include "face/face.hpp"
+#include "mgmt/internal-face.hpp"
+#include "tests/daemon/face/dummy-face.hpp"
+
+#include "validation-common.hpp"
+#include "tests/test-common.hpp"
+
+#include "fib-enumeration-publisher-common.hpp"
+
+namespace nfd {
+namespace tests {
+
+NFD_LOG_INIT("FibManagerTest");
+
+class FibManagerFixture : public FibEnumerationPublisherFixture
+{
+public:
+
+ virtual
+ ~FibManagerFixture()
+ {
+ }
+
+ shared_ptr<Face>
+ getFace(FaceId id)
+ {
+ if (id > 0 && static_cast<size_t>(id) <= m_faces.size())
+ {
+ return m_faces[id - 1];
+ }
+ NFD_LOG_DEBUG("No face found returning NULL");
+ return shared_ptr<DummyFace>();
+ }
+
+ void
+ addFace(shared_ptr<Face> face)
+ {
+ m_faces.push_back(face);
+ }
+
+ void
+ validateControlResponseCommon(const Data& response,
+ const Name& expectedName,
+ uint32_t expectedCode,
+ const std::string& expectedText,
+ ControlResponse& control)
+ {
+ m_callbackFired = true;
+ Block controlRaw = response.getContent().blockFromValue();
+
+ control.wireDecode(controlRaw);
+
+ // NFD_LOG_DEBUG("received control response"
+ // << " Name: " << response.getName()
+ // << " code: " << control.getCode()
+ // << " text: " << control.getText());
+
+ BOOST_CHECK_EQUAL(response.getName(), expectedName);
+ BOOST_CHECK_EQUAL(control.getCode(), expectedCode);
+ BOOST_CHECK_EQUAL(control.getText(), expectedText);
+ }
+
+ void
+ validateControlResponse(const Data& response,
+ const Name& expectedName,
+ uint32_t expectedCode,
+ const std::string& expectedText)
+ {
+ ControlResponse control;
+ validateControlResponseCommon(response, expectedName,
+ expectedCode, expectedText, control);
+
+ if (!control.getBody().empty())
+ {
+ BOOST_FAIL("found unexpected control response body");
+ }
+ }
+
+ void
+ validateControlResponse(const Data& response,
+ const Name& expectedName,
+ uint32_t expectedCode,
+ const std::string& expectedText,
+ const Block& expectedBody)
+ {
+ ControlResponse control;
+ validateControlResponseCommon(response, expectedName,
+ expectedCode, expectedText, control);
+
+ BOOST_REQUIRE(!control.getBody().empty());
+ BOOST_REQUIRE_EQUAL(control.getBody().value_size(), expectedBody.value_size());
+
+ BOOST_CHECK(memcmp(control.getBody().value(), expectedBody.value(),
+ expectedBody.value_size()) == 0);
+
+ }
+
+ bool
+ didCallbackFire()
+ {
+ return m_callbackFired;
+ }
+
+ void
+ resetCallbackFired()
+ {
+ m_callbackFired = false;
+ }
+
+ shared_ptr<InternalFace>
+ getInternalFace()
+ {
+ return m_face;
+ }
+
+ FibManager&
+ getFibManager()
+ {
+ return m_manager;
+ }
+
+ Fib&
+ getFib()
+ {
+ return m_fib;
+ }
+
+ void
+ addInterestRule(const std::string& regex,
+ ndn::IdentityCertificate& certificate)
+ {
+ m_manager.addInterestRule(regex, certificate);
+ }
+
+protected:
+ FibManagerFixture()
+ : m_manager(boost::ref(m_fib),
+ bind(&FibManagerFixture::getFace, this, _1),
+ m_face)
+ , m_callbackFired(false)
+ {
+ }
+
+protected:
+ FibManager m_manager;
+
+ std::vector<shared_ptr<Face> > m_faces;
+ bool m_callbackFired;
+};
+
+template <typename T> class AuthorizedCommandFixture:
+ public CommandFixture<T>
+{
+public:
+ AuthorizedCommandFixture()
+ {
+ const std::string regex = "^<localhost><nfd><fib>";
+ T::addInterestRule(regex, *CommandFixture<T>::m_certificate);
+ }
+
+ virtual
+ ~AuthorizedCommandFixture()
+ {
+ }
+};
+
+BOOST_FIXTURE_TEST_SUITE(MgmtFibManager, AuthorizedCommandFixture<FibManagerFixture>)
+
+bool
+foundNextHop(FaceId id, uint32_t cost, const fib::NextHop& next)
+{
+ return id == next.getFace()->getId() && next.getCost() == cost;
+}
+
+bool
+addedNextHopWithCost(const Fib& fib, const Name& prefix, size_t oldSize, uint32_t cost)
+{
+ shared_ptr<fib::Entry> entry = fib.findExactMatch(prefix);
+
+ if (static_cast<bool>(entry))
+ {
+ const fib::NextHopList& hops = entry->getNextHops();
+ return hops.size() == oldSize + 1 &&
+ std::find_if(hops.begin(), hops.end(), bind(&foundNextHop, -1, cost, _1)) != hops.end();
+ }
+ return false;
+}
+
+bool
+foundNextHopWithFace(FaceId id, uint32_t cost,
+ shared_ptr<Face> face, const fib::NextHop& next)
+{
+ return id == next.getFace()->getId() && next.getCost() == cost && face == next.getFace();
+}
+
+bool
+addedNextHopWithFace(const Fib& fib, const Name& prefix, size_t oldSize,
+ uint32_t cost, shared_ptr<Face> face)
+{
+ shared_ptr<fib::Entry> entry = fib.findExactMatch(prefix);
+
+ if (static_cast<bool>(entry))
+ {
+ const fib::NextHopList& hops = entry->getNextHops();
+ return hops.size() == oldSize + 1 &&
+ std::find_if(hops.begin(), hops.end(), bind(&foundNextHop, -1, cost, _1)) != hops.end();
+ }
+ return false;
+}
+
+BOOST_AUTO_TEST_CASE(TestFireInterestFilter)
+{
+ shared_ptr<InternalFace> face = getInternalFace();
+
+ shared_ptr<Interest> command = makeInterest("/localhost/nfd/fib");
+
+ face->onReceiveData +=
+ bind(&FibManagerFixture::validateControlResponse, this, _1,
+ command->getName(), 400, "Malformed command");
+
+ face->sendInterest(*command);
+ g_io.run_one();
+
+ BOOST_REQUIRE(didCallbackFire());
+}
+
+BOOST_AUTO_TEST_CASE(MalformedCommmand)
+{
+ shared_ptr<InternalFace> face = getInternalFace();
+
+ BOOST_REQUIRE(didCallbackFire() == false);
+
+ Interest command("/localhost/nfd/fib");
+
+ face->onReceiveData +=
+ bind(&FibManagerFixture::validateControlResponse, this, _1,
+ command.getName(), 400, "Malformed command");
+
+ getFibManager().onFibRequest(command);
+
+ BOOST_REQUIRE(didCallbackFire());
+}
+
+BOOST_AUTO_TEST_CASE(UnsupportedVerb)
+{
+ shared_ptr<InternalFace> face = getInternalFace();
+
+ ControlParameters parameters;
+ parameters.setName("/hello");
+ parameters.setFaceId(1);
+ parameters.setCost(1);
+
+ Block encodedParameters(parameters.wireEncode());
+
+ Name commandName("/localhost/nfd/fib");
+ commandName.append("unsupported");
+ commandName.append(encodedParameters);
+
+ shared_ptr<Interest> command(make_shared<Interest>(commandName));
+ generateCommand(*command);
+
+ face->onReceiveData +=
+ bind(&FibManagerFixture::validateControlResponse, this, _1,
+ command->getName(), 501, "Unsupported command");
+
+ getFibManager().onFibRequest(*command);
+
+ BOOST_REQUIRE(didCallbackFire());
+}
+
+BOOST_AUTO_TEST_CASE(UnsignedCommand)
+{
+ addFace(make_shared<DummyFace>());
+
+ shared_ptr<InternalFace> face = getInternalFace();
+
+ ControlParameters parameters;
+ parameters.setName("/hello");
+ parameters.setFaceId(1);
+ parameters.setCost(101);
+
+ Block encodedParameters(parameters.wireEncode());
+
+ Name commandName("/localhost/nfd/fib");
+ commandName.append("add-nexthop");
+ commandName.append(encodedParameters);
+
+ Interest command(commandName);
+
+ face->onReceiveData +=
+ bind(&FibManagerFixture::validateControlResponse,
+ this, _1, command.getName(), 401, "Signature required");
+
+
+ getFibManager().onFibRequest(command);
+
+ BOOST_REQUIRE(didCallbackFire());
+ BOOST_REQUIRE(!addedNextHopWithCost(getFib(), "/hello", 0, 101));
+}
+
+BOOST_FIXTURE_TEST_CASE(UnauthorizedCommand, UnauthorizedCommandFixture<FibManagerFixture>)
+{
+ addFace(make_shared<DummyFace>());
+
+ shared_ptr<InternalFace> face = getInternalFace();
+
+ ControlParameters parameters;
+ parameters.setName("/hello");
+ parameters.setFaceId(1);
+ parameters.setCost(101);
+
+ Block encodedParameters(parameters.wireEncode());
+
+ Name commandName("/localhost/nfd/fib");
+ commandName.append("add-nexthop");
+ commandName.append(encodedParameters);
+
+ shared_ptr<Interest> command(make_shared<Interest>(commandName));
+ generateCommand(*command);
+
+ face->onReceiveData +=
+ bind(&FibManagerFixture::validateControlResponse,
+ this, _1, command->getName(), 403, "Unauthorized command");
+
+ getFibManager().onFibRequest(*command);
+
+ BOOST_REQUIRE(didCallbackFire());
+ BOOST_REQUIRE(!addedNextHopWithCost(getFib(), "/hello", 0, 101));
+}
+
+BOOST_AUTO_TEST_CASE(BadOptionParse)
+{
+ addFace(make_shared<DummyFace>());
+
+ shared_ptr<InternalFace> face = getInternalFace();
+
+ Name commandName("/localhost/nfd/fib");
+ commandName.append("add-nexthop");
+ commandName.append("NotReallyParameters");
+
+ shared_ptr<Interest> command(make_shared<Interest>(commandName));
+ generateCommand(*command);
+
+ face->onReceiveData +=
+ bind(&FibManagerFixture::validateControlResponse, this, _1,
+ command->getName(), 400, "Malformed command");
+
+ getFibManager().onFibRequest(*command);
+
+ BOOST_REQUIRE(didCallbackFire());
+}
+
+BOOST_AUTO_TEST_CASE(UnknownFaceId)
+{
+ addFace(make_shared<DummyFace>());
+
+ shared_ptr<InternalFace> face = getInternalFace();
+
+ ControlParameters parameters;
+ parameters.setName("/hello");
+ parameters.setFaceId(1000);
+ parameters.setCost(101);
+
+ Block encodedParameters(parameters.wireEncode());
+
+ Name commandName("/localhost/nfd/fib");
+ commandName.append("add-nexthop");
+ commandName.append(encodedParameters);
+
+ shared_ptr<Interest> command(make_shared<Interest>(commandName));
+ generateCommand(*command);
+
+ face->onReceiveData +=
+ bind(&FibManagerFixture::validateControlResponse, this, _1,
+ command->getName(), 410, "Face not found");
+
+ getFibManager().onFibRequest(*command);
+
+ BOOST_REQUIRE(didCallbackFire());
+ BOOST_REQUIRE(addedNextHopWithCost(getFib(), "/hello", 0, 101) == false);
+}
+
+BOOST_AUTO_TEST_CASE(TestImplicitFaceId)
+{
+ addFace(make_shared<DummyFace>());
+
+ shared_ptr<InternalFace> face = getInternalFace();
+
+ ControlParameters parameters;
+ parameters.setName("/hello");
+ parameters.setFaceId(0);
+ parameters.setCost(101);
+
+ Block encodedParameters(parameters.wireEncode());
+
+ Name commandName("/localhost/nfd/fib");
+ commandName.append("add-nexthop");
+ commandName.append(encodedParameters);
+
+ ControlParameters expectedParameters;
+ expectedParameters.setName("/hello");
+ expectedParameters.setFaceId(1);
+ expectedParameters.setCost(101);
+
+ Block encodedExpectedParameters(expectedParameters.wireEncode());
+
+ shared_ptr<Interest> command(make_shared<Interest>(commandName));
+ command->setIncomingFaceId(1);
+ generateCommand(*command);
+
+ face->onReceiveData +=
+ bind(&FibManagerFixture::validateControlResponse, this, _1,
+ command->getName(), 200, "Success", encodedExpectedParameters);
+
+ getFibManager().onFibRequest(*command);
+
+ BOOST_REQUIRE(didCallbackFire());
+ BOOST_REQUIRE(addedNextHopWithFace(getFib(), "/hello", 0, 101, getFace(1)));
+}
+
+BOOST_AUTO_TEST_CASE(AddNextHopVerbInitialAdd)
+{
+ addFace(make_shared<DummyFace>());
+
+ shared_ptr<InternalFace> face = getInternalFace();
+
+ ControlParameters parameters;
+ parameters.setName("/hello");
+ parameters.setFaceId(1);
+ parameters.setCost(101);
+
+ Block encodedParameters(parameters.wireEncode());
+
+ Name commandName("/localhost/nfd/fib");
+ commandName.append("add-nexthop");
+ commandName.append(encodedParameters);
+
+ shared_ptr<Interest> command(make_shared<Interest>(commandName));
+ generateCommand(*command);
+
+ face->onReceiveData +=
+ bind(&FibManagerFixture::validateControlResponse, this, _1,
+ command->getName(), 200, "Success", encodedParameters);
+
+ getFibManager().onFibRequest(*command);
+
+ BOOST_REQUIRE(didCallbackFire());
+ BOOST_REQUIRE(addedNextHopWithCost(getFib(), "/hello", 0, 101));
+}
+
+BOOST_AUTO_TEST_CASE(AddNextHopVerbImplicitCost)
+{
+ addFace(make_shared<DummyFace>());
+
+ shared_ptr<InternalFace> face = getInternalFace();
+
+ ControlParameters parameters;
+ parameters.setName("/hello");
+ parameters.setFaceId(1);
+
+ Block encodedParameters(parameters.wireEncode());
+
+ Name commandName("/localhost/nfd/fib");
+ commandName.append("add-nexthop");
+ commandName.append(encodedParameters);
+
+ shared_ptr<Interest> command(make_shared<Interest>(commandName));
+ generateCommand(*command);
+
+ ControlParameters resultParameters;
+ resultParameters.setName("/hello");
+ resultParameters.setFaceId(1);
+ resultParameters.setCost(0);
+
+ face->onReceiveData +=
+ bind(&FibManagerFixture::validateControlResponse, this, _1,
+ command->getName(), 200, "Success", resultParameters.wireEncode());
+
+ getFibManager().onFibRequest(*command);
+
+ BOOST_REQUIRE(didCallbackFire());
+ BOOST_REQUIRE(addedNextHopWithCost(getFib(), "/hello", 0, 0));
+}
+
+BOOST_AUTO_TEST_CASE(AddNextHopVerbAddToExisting)
+{
+ addFace(make_shared<DummyFace>());
+ shared_ptr<InternalFace> face = getInternalFace();
+
+ for (int i = 1; i <= 2; i++)
+ {
+
+ ControlParameters parameters;
+ parameters.setName("/hello");
+ parameters.setFaceId(1);
+ parameters.setCost(100 + i);
+
+ Block encodedParameters(parameters.wireEncode());
+
+ Name commandName("/localhost/nfd/fib");
+ commandName.append("add-nexthop");
+ commandName.append(encodedParameters);
+
+ shared_ptr<Interest> command(make_shared<Interest>(commandName));
+ generateCommand(*command);
+
+ face->onReceiveData +=
+ bind(&FibManagerFixture::validateControlResponse, this, _1,
+ command->getName(), 200, "Success", encodedParameters);
+
+ getFibManager().onFibRequest(*command);
+ BOOST_REQUIRE(didCallbackFire());
+ resetCallbackFired();
+
+ shared_ptr<fib::Entry> entry = getFib().findExactMatch("/hello");
+
+ if (static_cast<bool>(entry))
+ {
+ const fib::NextHopList& hops = entry->getNextHops();
+ BOOST_REQUIRE(hops.size() == 1);
+ BOOST_REQUIRE(std::find_if(hops.begin(), hops.end(),
+ bind(&foundNextHop, -1, 100 + i, _1)) != hops.end());
+
+ }
+ else
+ {
+ BOOST_FAIL("Failed to find expected fib entry");
+ }
+
+ face->onReceiveData.clear();
+ }
+}
+
+BOOST_AUTO_TEST_CASE(AddNextHopVerbUpdateFaceCost)
+{
+ addFace(make_shared<DummyFace>());
+ shared_ptr<InternalFace> face = getInternalFace();
+
+ ControlParameters parameters;
+ parameters.setName("/hello");
+ parameters.setFaceId(1);
+
+ {
+ parameters.setCost(1);
+
+ Block encodedParameters(parameters.wireEncode());
+
+ Name commandName("/localhost/nfd/fib");
+ commandName.append("add-nexthop");
+ commandName.append(encodedParameters);
+
+ shared_ptr<Interest> command(make_shared<Interest>(commandName));
+ generateCommand(*command);
+
+ face->onReceiveData +=
+ bind(&FibManagerFixture::validateControlResponse, this, _1,
+ command->getName(), 200, "Success", encodedParameters);
+
+ getFibManager().onFibRequest(*command);
+
+ BOOST_REQUIRE(didCallbackFire());
+ }
+
+ resetCallbackFired();
+ face->onReceiveData.clear();
+
+ {
+ parameters.setCost(102);
+
+ Block encodedParameters(parameters.wireEncode());
+
+ Name commandName("/localhost/nfd/fib");
+ commandName.append("add-nexthop");
+ commandName.append(encodedParameters);
+
+ shared_ptr<Interest> command(make_shared<Interest>(commandName));
+ generateCommand(*command);
+
+ face->onReceiveData +=
+ bind(&FibManagerFixture::validateControlResponse, this, _1,
+ command->getName(), 200, "Success", encodedParameters);
+
+ getFibManager().onFibRequest(*command);
+
+ BOOST_REQUIRE(didCallbackFire());
+ }
+
+ shared_ptr<fib::Entry> entry = getFib().findExactMatch("/hello");
+
+ // Add faces with cost == FaceID for the name /hello
+ // This test assumes:
+ // FaceIDs are -1 because we don't add them to a forwarder
+ if (static_cast<bool>(entry))
+ {
+ const fib::NextHopList& hops = entry->getNextHops();
+ BOOST_REQUIRE(hops.size() == 1);
+ BOOST_REQUIRE(std::find_if(hops.begin(),
+ hops.end(),
+ bind(&foundNextHop, -1, 102, _1)) != hops.end());
+ }
+ else
+ {
+ BOOST_FAIL("Failed to find expected fib entry");
+ }
+}
+
+BOOST_AUTO_TEST_CASE(AddNextHopVerbMissingPrefix)
+{
+ addFace(make_shared<DummyFace>());
+
+ shared_ptr<InternalFace> face = getInternalFace();
+
+ ControlParameters parameters;
+ parameters.setFaceId(1);
+
+ Block encodedParameters(parameters.wireEncode());
+
+ Name commandName("/localhost/nfd/fib");
+ commandName.append("add-nexthop");
+ commandName.append(encodedParameters);
+
+ shared_ptr<Interest> command(make_shared<Interest>(commandName));
+ generateCommand(*command);
+
+ face->onReceiveData +=
+ bind(&FibManagerFixture::validateControlResponse, this, _1,
+ command->getName(), 400, "Malformed command");
+
+ getFibManager().onFibRequest(*command);
+
+ BOOST_REQUIRE(didCallbackFire());
+}
+
+BOOST_AUTO_TEST_CASE(AddNextHopVerbMissingFaceId)
+{
+ addFace(make_shared<DummyFace>());
+
+ shared_ptr<InternalFace> face = getInternalFace();
+
+ ControlParameters parameters;
+ parameters.setName("/hello");
+
+ Block encodedParameters(parameters.wireEncode());
+
+ Name commandName("/localhost/nfd/fib");
+ commandName.append("add-nexthop");
+ commandName.append(encodedParameters);
+
+ shared_ptr<Interest> command(make_shared<Interest>(commandName));
+ generateCommand(*command);
+
+ face->onReceiveData +=
+ bind(&FibManagerFixture::validateControlResponse, this, _1,
+ command->getName(), 400, "Malformed command");
+
+ getFibManager().onFibRequest(*command);
+
+ BOOST_REQUIRE(didCallbackFire());
+}
+
+bool
+removedNextHopWithCost(const Fib& fib, const Name& prefix, size_t oldSize, uint32_t cost)
+{
+ shared_ptr<fib::Entry> entry = fib.findExactMatch(prefix);
+
+ if (static_cast<bool>(entry))
+ {
+ const fib::NextHopList& hops = entry->getNextHops();
+ return hops.size() == oldSize - 1 &&
+ std::find_if(hops.begin(), hops.end(), bind(&foundNextHop, -1, cost, _1)) == hops.end();
+ }
+ return false;
+}
+
+void
+testRemoveNextHop(CommandFixture<FibManagerFixture>* fixture,
+ FibManager& manager,
+ Fib& fib,
+ shared_ptr<Face> face,
+ const Name& targetName,
+ FaceId targetFace)
+{
+ ControlParameters parameters;
+ parameters.setName(targetName);
+ parameters.setFaceId(targetFace);
+
+ Block encodedParameters(parameters.wireEncode());
+
+ Name commandName("/localhost/nfd/fib");
+ commandName.append("remove-nexthop");
+ commandName.append(encodedParameters);
+
+ shared_ptr<Interest> command(make_shared<Interest>(commandName));
+ fixture->generateCommand(*command);
+
+ face->onReceiveData +=
+ bind(&FibManagerFixture::validateControlResponse, fixture, _1,
+ command->getName(), 200, "Success", encodedParameters);
+
+ manager.onFibRequest(*command);
+
+ BOOST_REQUIRE(fixture->didCallbackFire());
+
+ fixture->resetCallbackFired();
+ face->onReceiveData.clear();
+}
+
+BOOST_AUTO_TEST_CASE(RemoveNextHop)
+{
+ shared_ptr<Face> face1 = make_shared<DummyFace>();
+ shared_ptr<Face> face2 = make_shared<DummyFace>();
+ shared_ptr<Face> face3 = make_shared<DummyFace>();
+
+ addFace(face1);
+ addFace(face2);
+ addFace(face3);
+
+ shared_ptr<InternalFace> face = getInternalFace();
+ FibManager& manager = getFibManager();
+ Fib& fib = getFib();
+
+ shared_ptr<fib::Entry> entry = fib.insert("/hello").first;
+
+ entry->addNextHop(face1, 101);
+ entry->addNextHop(face2, 202);
+ entry->addNextHop(face3, 303);
+
+ testRemoveNextHop(this, manager, fib, face, "/hello", 2);
+ BOOST_REQUIRE(removedNextHopWithCost(fib, "/hello", 3, 202));
+
+ testRemoveNextHop(this, manager, fib, face, "/hello", 3);
+ BOOST_REQUIRE(removedNextHopWithCost(fib, "/hello", 2, 303));
+
+ testRemoveNextHop(this, manager, fib, face, "/hello", 1);
+ // BOOST_REQUIRE(removedNextHopWithCost(fib, "/hello", 1, 101));
+
+ BOOST_CHECK(!static_cast<bool>(getFib().findExactMatch("/hello")));
+}
+
+BOOST_AUTO_TEST_CASE(RemoveFaceNotFound)
+{
+ shared_ptr<InternalFace> face = getInternalFace();
+
+ ControlParameters parameters;
+ parameters.setName("/hello");
+ parameters.setFaceId(1);
+
+ Block encodedParameters(parameters.wireEncode());
+
+ Name commandName("/localhost/nfd/fib");
+ commandName.append("remove-nexthop");
+ commandName.append(encodedParameters);
+
+ shared_ptr<Interest> command(make_shared<Interest>(commandName));
+ generateCommand(*command);
+
+ face->onReceiveData +=
+ bind(&FibManagerFixture::validateControlResponse, this, _1,
+ command->getName(), 200, "Success", encodedParameters);
+
+ getFibManager().onFibRequest(*command);
+
+ BOOST_REQUIRE(didCallbackFire());
+}
+
+BOOST_AUTO_TEST_CASE(RemovePrefixNotFound)
+{
+ addFace(make_shared<DummyFace>());
+
+ shared_ptr<InternalFace> face = getInternalFace();
+
+ ControlParameters parameters;
+ parameters.setName("/hello");
+ parameters.setFaceId(1);
+
+ Block encodedParameters(parameters.wireEncode());
+
+ Name commandName("/localhost/nfd/fib");
+ commandName.append("remove-nexthop");
+ commandName.append(encodedParameters);
+
+ shared_ptr<Interest> command(make_shared<Interest>(commandName));
+ generateCommand(*command);
+
+ face->onReceiveData +=
+ bind(&FibManagerFixture::validateControlResponse, this, _1,
+ command->getName(), 200, "Success", encodedParameters);
+
+ getFibManager().onFibRequest(*command);
+
+ BOOST_REQUIRE(didCallbackFire());
+}
+
+BOOST_AUTO_TEST_CASE(RemoveMissingPrefix)
+{
+ addFace(make_shared<DummyFace>());
+
+ shared_ptr<InternalFace> face = getInternalFace();
+
+ ControlParameters parameters;
+ parameters.setFaceId(1);
+
+ Block encodedParameters(parameters.wireEncode());
+
+ Name commandName("/localhost/nfd/fib");
+ commandName.append("remove-nexthop");
+ commandName.append(encodedParameters);
+
+ shared_ptr<Interest> command(make_shared<Interest>(commandName));
+ generateCommand(*command);
+
+ face->onReceiveData +=
+ bind(&FibManagerFixture::validateControlResponse, this, _1,
+ command->getName(), 400, "Malformed command");
+
+ getFibManager().onFibRequest(*command);
+
+ BOOST_REQUIRE(didCallbackFire());
+}
+
+BOOST_AUTO_TEST_CASE(RemoveMissingFaceId)
+{
+ addFace(make_shared<DummyFace>());
+
+ shared_ptr<InternalFace> face = getInternalFace();
+
+ ControlParameters parameters;
+ parameters.setName("/hello");
+
+ Block encodedParameters(parameters.wireEncode());
+
+ Name commandName("/localhost/nfd/fib");
+ commandName.append("remove-nexthop");
+ commandName.append(encodedParameters);
+
+ shared_ptr<Interest> command(make_shared<Interest>(commandName));
+ generateCommand(*command);
+
+ face->onReceiveData +=
+ bind(&FibManagerFixture::validateControlResponse, this, _1,
+ command->getName(), 400, "Malformed command");
+
+ getFibManager().onFibRequest(*command);
+
+ BOOST_REQUIRE(didCallbackFire());
+}
+
+BOOST_FIXTURE_TEST_CASE(TestFibEnumerationRequest, FibManagerFixture)
+{
+ for (int i = 0; i < 87; i++)
+ {
+ Name prefix("/test");
+ prefix.appendSegment(i);
+
+ shared_ptr<DummyFace> dummy1(make_shared<DummyFace>());
+ shared_ptr<DummyFace> dummy2(make_shared<DummyFace>());
+
+ shared_ptr<fib::Entry> entry = m_fib.insert(prefix).first;
+ entry->addNextHop(dummy1, std::numeric_limits<uint64_t>::max() - 1);
+ entry->addNextHop(dummy2, std::numeric_limits<uint64_t>::max() - 2);
+
+ m_referenceEntries.insert(entry);
+ }
+ for (int i = 0; i < 2; i++)
+ {
+ Name prefix("/test2");
+ prefix.appendSegment(i);
+
+ shared_ptr<DummyFace> dummy1(make_shared<DummyFace>());
+ shared_ptr<DummyFace> dummy2(make_shared<DummyFace>());
+
+ shared_ptr<fib::Entry> entry = m_fib.insert(prefix).first;
+ entry->addNextHop(dummy1, std::numeric_limits<uint8_t>::max() - 1);
+ entry->addNextHop(dummy2, std::numeric_limits<uint8_t>::max() - 2);
+
+ m_referenceEntries.insert(entry);
+ }
+
+ ndn::EncodingBuffer buffer;
+
+ m_face->onReceiveData +=
+ bind(&FibEnumerationPublisherFixture::decodeFibEntryBlock, this, _1);
+
+ shared_ptr<Interest> command(make_shared<Interest>("/localhost/nfd/fib/list"));
+
+ m_manager.onFibRequest(*command);
+ BOOST_REQUIRE(m_finished);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+} // namespace nfd
diff --git a/tests/daemon/mgmt/internal-face.cpp b/tests/daemon/mgmt/internal-face.cpp
new file mode 100644
index 0000000..04ef4a2
--- /dev/null
+++ b/tests/daemon/mgmt/internal-face.cpp
@@ -0,0 +1,236 @@
+/* -*- 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 "mgmt/internal-face.hpp"
+#include "tests/daemon/face/dummy-face.hpp"
+
+#include "tests/test-common.hpp"
+
+namespace nfd {
+namespace tests {
+
+NFD_LOG_INIT("InternalFaceTest");
+
+class InternalFaceFixture : protected BaseFixture
+{
+public:
+
+ InternalFaceFixture()
+ : m_onInterestFired(false),
+ m_noOnInterestFired(false)
+ {
+
+ }
+
+ void
+ validateOnInterestCallback(const Name& name, const Interest& interest)
+ {
+ m_onInterestFired = true;
+ }
+
+ void
+ validateNoOnInterestCallback(const Name& name, const Interest& interest)
+ {
+ m_noOnInterestFired = true;
+ }
+
+ void
+ addFace(shared_ptr<Face> face)
+ {
+ m_faces.push_back(face);
+ }
+
+ bool
+ didOnInterestFire()
+ {
+ return m_onInterestFired;
+ }
+
+ bool
+ didNoOnInterestFire()
+ {
+ return m_noOnInterestFired;
+ }
+
+ void
+ resetOnInterestFired()
+ {
+ m_onInterestFired = false;
+ }
+
+ void
+ resetNoOnInterestFired()
+ {
+ m_noOnInterestFired = false;
+ }
+
+private:
+ std::vector<shared_ptr<Face> > m_faces;
+ bool m_onInterestFired;
+ bool m_noOnInterestFired;
+};
+
+BOOST_FIXTURE_TEST_SUITE(MgmtInternalFace, InternalFaceFixture)
+
+void
+validatePutData(bool& called, const Name& expectedName, const Data& data)
+{
+ called = true;
+ BOOST_CHECK_EQUAL(expectedName, data.getName());
+}
+
+BOOST_AUTO_TEST_CASE(PutData)
+{
+ addFace(make_shared<DummyFace>());
+
+ shared_ptr<InternalFace> face(new InternalFace);
+
+ bool didPutData = false;
+ Name dataName("/hello");
+ face->onReceiveData += bind(&validatePutData, boost::ref(didPutData), dataName, _1);
+
+ Data testData(dataName);
+ face->sign(testData);
+ face->put(testData);
+
+ BOOST_REQUIRE(didPutData);
+
+ BOOST_CHECK_THROW(face->close(), InternalFace::Error);
+}
+
+BOOST_AUTO_TEST_CASE(SendInterestHitEnd)
+{
+ addFace(make_shared<DummyFace>());
+
+ shared_ptr<InternalFace> face(new InternalFace);
+
+ face->setInterestFilter("/localhost/nfd/fib",
+ bind(&InternalFaceFixture::validateOnInterestCallback,
+ this, _1, _2));
+
+ // generate command whose name is canonically
+ // ordered after /localhost/nfd/fib so that
+ // we hit the end of the std::map
+
+ Name commandName("/localhost/nfd/fib/end");
+ shared_ptr<Interest> command = makeInterest(commandName);
+ face->sendInterest(*command);
+ g_io.run_one();
+
+ BOOST_REQUIRE(didOnInterestFire());
+ BOOST_REQUIRE(didNoOnInterestFire() == false);
+}
+
+BOOST_AUTO_TEST_CASE(SendInterestHitBegin)
+{
+ addFace(make_shared<DummyFace>());
+
+ shared_ptr<InternalFace> face(new InternalFace);
+
+ face->setInterestFilter("/localhost/nfd/fib",
+ bind(&InternalFaceFixture::validateNoOnInterestCallback,
+ this, _1, _2));
+
+ // generate command whose name is canonically
+ // ordered before /localhost/nfd/fib so that
+ // we hit the beginning of the std::map
+
+ Name commandName("/localhost/nfd");
+ shared_ptr<Interest> command = makeInterest(commandName);
+ face->sendInterest(*command);
+ g_io.run_one();
+
+ BOOST_REQUIRE(didNoOnInterestFire() == false);
+}
+
+BOOST_AUTO_TEST_CASE(SendInterestHitExact)
+{
+ addFace(make_shared<DummyFace>());
+
+ shared_ptr<InternalFace> face(new InternalFace);
+
+ face->setInterestFilter("/localhost/nfd/eib",
+ bind(&InternalFaceFixture::validateNoOnInterestCallback,
+ this, _1, _2));
+
+ face->setInterestFilter("/localhost/nfd/fib",
+ bind(&InternalFaceFixture::validateOnInterestCallback,
+ this, _1, _2));
+
+ face->setInterestFilter("/localhost/nfd/gib",
+ bind(&InternalFaceFixture::validateNoOnInterestCallback,
+ this, _1, _2));
+
+ // generate command whose name exactly matches
+ // /localhost/nfd/fib
+
+ Name commandName("/localhost/nfd/fib");
+ shared_ptr<Interest> command = makeInterest(commandName);
+ face->sendInterest(*command);
+ g_io.run_one();
+
+ BOOST_REQUIRE(didOnInterestFire());
+ BOOST_REQUIRE(didNoOnInterestFire() == false);
+}
+
+BOOST_AUTO_TEST_CASE(SendInterestHitPrevious)
+{
+ addFace(make_shared<DummyFace>());
+
+ shared_ptr<InternalFace> face(new InternalFace);
+
+ face->setInterestFilter("/localhost/nfd/fib",
+ bind(&InternalFaceFixture::validateOnInterestCallback,
+ this, _1, _2));
+
+ face->setInterestFilter("/localhost/nfd/fib/zzzzzzzzzzzzz/",
+ bind(&InternalFaceFixture::validateNoOnInterestCallback,
+ this, _1, _2));
+
+ // generate command whose name exactly matches
+ // an Interest filter
+
+ Name commandName("/localhost/nfd/fib/previous");
+ shared_ptr<Interest> command = makeInterest(commandName);
+ face->sendInterest(*command);
+ g_io.run_one();
+
+ BOOST_REQUIRE(didOnInterestFire());
+ BOOST_REQUIRE(didNoOnInterestFire() == false);
+}
+
+BOOST_AUTO_TEST_CASE(InterestGone)
+{
+ shared_ptr<InternalFace> face = make_shared<InternalFace>();
+ shared_ptr<Interest> interest = makeInterest("ndn:/localhost/nfd/gone");
+ face->sendInterest(*interest);
+
+ interest.reset();
+ BOOST_CHECK_NO_THROW(g_io.poll());
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+} // namespace nfd
diff --git a/tests/daemon/mgmt/malformed.ndncert b/tests/daemon/mgmt/malformed.ndncert
new file mode 100644
index 0000000..38b2fbb
--- /dev/null
+++ b/tests/daemon/mgmt/malformed.ndncert
@@ -0,0 +1 @@
+definitely not a key
\ No newline at end of file
diff --git a/tests/daemon/mgmt/manager-base.cpp b/tests/daemon/mgmt/manager-base.cpp
new file mode 100644
index 0000000..74fcb6e
--- /dev/null
+++ b/tests/daemon/mgmt/manager-base.cpp
@@ -0,0 +1,201 @@
+/* -*- 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 "mgmt/manager-base.hpp"
+#include "mgmt/internal-face.hpp"
+
+#include "tests/test-common.hpp"
+
+namespace nfd {
+namespace tests {
+
+NFD_LOG_INIT("ManagerBaseTest");
+
+class ManagerBaseTest : public ManagerBase, protected BaseFixture
+{
+
+public:
+
+ ManagerBaseTest()
+ : ManagerBase(make_shared<InternalFace>(), "TEST-PRIVILEGE"),
+ m_callbackFired(false)
+ {
+
+ }
+
+ void
+ testSetResponse(ControlResponse& response,
+ uint32_t code,
+ const std::string& text)
+ {
+ setResponse(response, code, text);
+ }
+
+ void
+ testSendResponse(const Name& name,
+ uint32_t code,
+ const std::string& text,
+ const Block& body)
+ {
+ sendResponse(name, code, text, body);
+ }
+
+ void
+ testSendResponse(const Name& name,
+ uint32_t code,
+ const std::string& text)
+ {
+ sendResponse(name, code, text);
+ }
+
+ void
+ testSendResponse(const Name& name,
+ const ControlResponse& response)
+ {
+ sendResponse(name, response);
+ }
+
+ shared_ptr<InternalFace>
+ getInternalFace()
+ {
+ return ndn::ptr_lib::static_pointer_cast<InternalFace>(m_face);
+ }
+
+ void
+ validateControlResponse(const Data& response,
+ const Name& expectedName,
+ uint32_t expectedCode,
+ const std::string& expectedText)
+ {
+ m_callbackFired = true;
+ Block controlRaw = response.getContent().blockFromValue();
+
+ ControlResponse control;
+ control.wireDecode(controlRaw);
+
+ NFD_LOG_DEBUG("received control response"
+ << " name: " << response.getName()
+ << " code: " << control.getCode()
+ << " text: " << control.getText());
+
+ BOOST_REQUIRE(response.getName() == expectedName);
+ BOOST_REQUIRE(control.getCode() == expectedCode);
+ BOOST_REQUIRE(control.getText() == expectedText);
+ }
+
+ void
+ validateControlResponse(const Data& response,
+ const Name& expectedName,
+ uint32_t expectedCode,
+ const std::string& expectedText,
+ const Block& expectedBody)
+ {
+ m_callbackFired = true;
+ Block controlRaw = response.getContent().blockFromValue();
+
+ ControlResponse control;
+ control.wireDecode(controlRaw);
+
+ NFD_LOG_DEBUG("received control response"
+ << " name: " << response.getName()
+ << " code: " << control.getCode()
+ << " text: " << control.getText());
+
+ BOOST_REQUIRE(response.getName() == expectedName);
+ BOOST_REQUIRE(control.getCode() == expectedCode);
+ BOOST_REQUIRE(control.getText() == expectedText);
+
+ BOOST_REQUIRE(control.getBody().value_size() == expectedBody.value_size());
+
+ BOOST_CHECK(memcmp(control.getBody().value(), expectedBody.value(),
+ expectedBody.value_size()) == 0);
+ }
+
+ bool
+ didCallbackFire()
+ {
+ return m_callbackFired;
+ }
+
+private:
+
+ bool m_callbackFired;
+
+};
+
+BOOST_FIXTURE_TEST_SUITE(MgmtManagerBase, ManagerBaseTest)
+
+BOOST_AUTO_TEST_CASE(SetResponse)
+{
+ ControlResponse response(200, "OK");
+
+ BOOST_CHECK_EQUAL(response.getCode(), 200);
+ BOOST_CHECK_EQUAL(response.getText(), "OK");
+
+ testSetResponse(response, 100, "test");
+
+ BOOST_CHECK_EQUAL(response.getCode(), 100);
+ BOOST_CHECK_EQUAL(response.getText(), "test");
+}
+
+BOOST_AUTO_TEST_CASE(SendResponse4Arg)
+{
+ ndn::nfd::ControlParameters parameters;
+ parameters.setName("/test/body");
+
+ getInternalFace()->onReceiveData +=
+ bind(&ManagerBaseTest::validateControlResponse, this, _1,
+ "/response", 100, "test", parameters.wireEncode());
+
+ testSendResponse("/response", 100, "test", parameters.wireEncode());
+ BOOST_REQUIRE(didCallbackFire());
+}
+
+
+BOOST_AUTO_TEST_CASE(SendResponse3Arg)
+{
+ getInternalFace()->onReceiveData +=
+ bind(&ManagerBaseTest::validateControlResponse, this, _1,
+ "/response", 100, "test");
+
+ testSendResponse("/response", 100, "test");
+ BOOST_REQUIRE(didCallbackFire());
+}
+
+BOOST_AUTO_TEST_CASE(SendResponse2Arg)
+{
+ getInternalFace()->onReceiveData +=
+ bind(&ManagerBaseTest::validateControlResponse, this, _1,
+ "/response", 100, "test");
+
+ ControlResponse response(100, "test");
+
+ testSendResponse("/response", 100, "test");
+ BOOST_REQUIRE(didCallbackFire());
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+} // namespace nfd
diff --git a/tests/daemon/mgmt/notification-stream.cpp b/tests/daemon/mgmt/notification-stream.cpp
new file mode 100644
index 0000000..32f5a20
--- /dev/null
+++ b/tests/daemon/mgmt/notification-stream.cpp
@@ -0,0 +1,140 @@
+/* -*- 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 "mgmt/notification-stream.hpp"
+#include "mgmt/internal-face.hpp"
+
+#include "tests/test-common.hpp"
+
+
+namespace nfd {
+namespace tests {
+
+NFD_LOG_INIT("NotificationStreamTest");
+
+
+
+class NotificationStreamFixture : public BaseFixture
+{
+public:
+ NotificationStreamFixture()
+ : m_callbackFired(false)
+ , m_prefix("/localhost/nfd/NotificationStreamTest")
+ , m_message("TestNotificationMessage")
+ , m_sequenceNo(0)
+ {
+ }
+
+ virtual
+ ~NotificationStreamFixture()
+ {
+ }
+
+ void
+ validateCallback(const Data& data)
+ {
+ Name expectedName(m_prefix);
+ expectedName.appendSegment(m_sequenceNo);
+ BOOST_REQUIRE_EQUAL(data.getName(), expectedName);
+
+ ndn::Block payload = data.getContent();
+ std::string message;
+
+ message.append(reinterpret_cast<const char*>(payload.value()), payload.value_size());
+
+ BOOST_REQUIRE_EQUAL(message, m_message);
+
+ m_callbackFired = true;
+ ++m_sequenceNo;
+ }
+
+ void
+ resetCallbackFired()
+ {
+ m_callbackFired = false;
+ }
+
+protected:
+ bool m_callbackFired;
+ const std::string m_prefix;
+ const std::string m_message;
+ uint64_t m_sequenceNo;
+};
+
+BOOST_FIXTURE_TEST_SUITE(MgmtNotificationStream, NotificationStreamFixture)
+
+class TestNotification
+{
+public:
+ TestNotification(const std::string& message)
+ : m_message(message)
+ {
+ }
+
+ ~TestNotification()
+ {
+ }
+
+ Block
+ wireEncode() const
+ {
+ ndn::EncodingBuffer buffer;
+
+ prependByteArrayBlock(buffer,
+ ndn::Tlv::Content,
+ reinterpret_cast<const uint8_t*>(m_message.c_str()),
+ m_message.size());
+ return buffer.block();
+ }
+
+private:
+ const std::string m_message;
+};
+
+BOOST_AUTO_TEST_CASE(TestPostEvent)
+{
+ shared_ptr<InternalFace> face(make_shared<InternalFace>());
+ NotificationStream notificationStream(face, "/localhost/nfd/NotificationStreamTest");
+
+ face->onReceiveData += bind(&NotificationStreamFixture::validateCallback, this, _1);
+
+ TestNotification event1(m_message);
+ notificationStream.postNotification(event1);
+
+ BOOST_REQUIRE(m_callbackFired);
+
+ resetCallbackFired();
+
+ TestNotification event2(m_message);
+ notificationStream.postNotification(event2);
+
+ BOOST_REQUIRE(m_callbackFired);
+}
+
+
+BOOST_AUTO_TEST_SUITE_END()
+
+
+} // namespace tests
+} // namespace nfd
diff --git a/tests/daemon/mgmt/segment-publisher.cpp b/tests/daemon/mgmt/segment-publisher.cpp
new file mode 100644
index 0000000..e40f372
--- /dev/null
+++ b/tests/daemon/mgmt/segment-publisher.cpp
@@ -0,0 +1,151 @@
+/* -*- 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 "mgmt/segment-publisher.hpp"
+#include "mgmt/internal-face.hpp"
+#include "mgmt/app-face.hpp"
+
+#include "tests/test-common.hpp"
+#include <ndn-cpp-dev/encoding/tlv.hpp>
+
+namespace nfd {
+namespace tests {
+
+NFD_LOG_INIT("SegmentPublisherTest");
+
+class TestSegmentPublisher : public SegmentPublisher
+{
+public:
+ TestSegmentPublisher(shared_ptr<AppFace> face,
+ const Name& prefix,
+ const uint64_t limit=10000)
+ : SegmentPublisher(face, prefix)
+ , m_limit((limit == 0)?(1):(limit))
+ {
+
+ }
+
+ virtual
+ ~TestSegmentPublisher()
+ {
+
+ }
+
+ uint16_t
+ getLimit() const
+ {
+ return m_limit;
+ }
+
+protected:
+
+ virtual size_t
+ generate(ndn::EncodingBuffer& outBuffer)
+ {
+ size_t totalLength = 0;
+ for (uint64_t i = 0; i < m_limit; i++)
+ {
+ totalLength += prependNonNegativeIntegerBlock(outBuffer, ndn::Tlv::Content, i);
+ }
+ return totalLength;
+ }
+
+protected:
+ const uint64_t m_limit;
+};
+
+class SegmentPublisherFixture : public BaseFixture
+{
+public:
+ SegmentPublisherFixture()
+ : m_face(make_shared<InternalFace>())
+ , m_publisher(m_face, "/localhost/nfd/SegmentPublisherFixture")
+ , m_finished(false)
+ {
+
+ }
+
+ void
+ validate(const Data& data)
+ {
+ Block payload = data.getContent();
+ NFD_LOG_DEBUG("payload size (w/o Content TLV): " << payload.value_size());
+
+ m_buffer.appendByteArray(payload.value(), payload.value_size());
+
+ uint64_t segmentNo = data.getName()[-1].toSegment();
+ if (data.getFinalBlockId() != data.getName()[-1])
+ {
+ return;
+ }
+
+ NFD_LOG_DEBUG("got final block: #" << segmentNo);
+
+ // wrap data in a single Content TLV for easy parsing
+ m_buffer.prependVarNumber(m_buffer.size());
+ m_buffer.prependVarNumber(ndn::Tlv::Content);
+
+ BOOST_TEST_CHECKPOINT("creating parser");
+ ndn::Block parser(m_buffer.buf(), m_buffer.size());
+ BOOST_TEST_CHECKPOINT("parsing aggregated response");
+ parser.parse();
+
+ BOOST_REQUIRE_EQUAL(parser.elements_size(), m_publisher.getLimit());
+
+ uint64_t expectedNo = m_publisher.getLimit() - 1;
+ for (Block::element_const_iterator i = parser.elements_begin();
+ i != parser.elements_end();
+ ++i)
+ {
+ uint64_t number = readNonNegativeInteger(*i);
+ BOOST_REQUIRE_EQUAL(number, expectedNo);
+ --expectedNo;
+ }
+ m_finished = true;
+ }
+
+protected:
+ shared_ptr<InternalFace> m_face;
+ TestSegmentPublisher m_publisher;
+ ndn::EncodingBuffer m_buffer;
+
+protected:
+ bool m_finished;
+};
+
+BOOST_FIXTURE_TEST_SUITE(MgmtSegmentPublisher, SegmentPublisherFixture)
+
+BOOST_AUTO_TEST_CASE(Generate)
+{
+ m_face->onReceiveData +=
+ bind(&SegmentPublisherFixture::validate, this, _1);
+
+ m_publisher.publish();
+ BOOST_REQUIRE(m_finished);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+} // namespace nfd
diff --git a/tests/daemon/mgmt/status-server.cpp b/tests/daemon/mgmt/status-server.cpp
new file mode 100644
index 0000000..75b4114
--- /dev/null
+++ b/tests/daemon/mgmt/status-server.cpp
@@ -0,0 +1,103 @@
+/* -*- 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 "mgmt/status-server.hpp"
+#include "fw/forwarder.hpp"
+#include "version.hpp"
+#include "mgmt/internal-face.hpp"
+
+#include "tests/test-common.hpp"
+#include "tests/daemon/face/dummy-face.hpp"
+
+namespace nfd {
+namespace tests {
+
+BOOST_FIXTURE_TEST_SUITE(MgmtStatusServer, BaseFixture)
+
+shared_ptr<const Data> g_response;
+
+void
+interceptResponse(const Data& data)
+{
+ g_response = data.shared_from_this();
+}
+
+BOOST_AUTO_TEST_CASE(Status)
+{
+ // initialize
+ time::system_clock::TimePoint t1 = time::system_clock::now();
+ Forwarder forwarder;
+ shared_ptr<InternalFace> internalFace = make_shared<InternalFace>();
+ internalFace->onReceiveData += &interceptResponse;
+ StatusServer statusServer(internalFace, boost::ref(forwarder));
+ time::system_clock::TimePoint t2 = time::system_clock::now();
+
+ // populate tables
+ forwarder.getFib().insert("ndn:/fib1");
+ forwarder.getPit().insert(*makeInterest("ndn:/pit1"));
+ forwarder.getPit().insert(*makeInterest("ndn:/pit2"));
+ forwarder.getPit().insert(*makeInterest("ndn:/pit3"));
+ forwarder.getPit().insert(*makeInterest("ndn:/pit4"));
+ forwarder.getMeasurements().get("ndn:/measurements1");
+ forwarder.getMeasurements().get("ndn:/measurements2");
+ forwarder.getMeasurements().get("ndn:/measurements3");
+ BOOST_CHECK_GE(forwarder.getFib().size(), 1);
+ BOOST_CHECK_GE(forwarder.getPit().size(), 4);
+ BOOST_CHECK_GE(forwarder.getMeasurements().size(), 3);
+
+ // request
+ shared_ptr<Interest> request = makeInterest("ndn:/localhost/nfd/status");
+ request->setMustBeFresh(true);
+ request->setChildSelector(1);
+
+ g_response.reset();
+ time::system_clock::TimePoint t3 = time::system_clock::now();
+ internalFace->sendInterest(*request);
+ g_io.run_one();
+ time::system_clock::TimePoint t4 = time::system_clock::now();
+ BOOST_REQUIRE(static_cast<bool>(g_response));
+
+ // verify
+ ndn::nfd::ForwarderStatus status;
+ BOOST_REQUIRE_NO_THROW(status.wireDecode(g_response->getContent()));
+
+ BOOST_CHECK_EQUAL(status.getNfdVersion(), NFD_VERSION);
+ BOOST_CHECK_GE(time::toUnixTimestamp(status.getStartTimestamp()), time::toUnixTimestamp(t1));
+ BOOST_CHECK_LE(time::toUnixTimestamp(status.getStartTimestamp()), time::toUnixTimestamp(t2));
+ BOOST_CHECK_GE(time::toUnixTimestamp(status.getCurrentTimestamp()), time::toUnixTimestamp(t3));
+ BOOST_CHECK_LE(time::toUnixTimestamp(status.getCurrentTimestamp()), time::toUnixTimestamp(t4));
+
+ // StatusServer under test isn't added to Forwarder,
+ // so request and response won't affect table size
+ BOOST_CHECK_EQUAL(status.getNNameTreeEntries(), forwarder.getNameTree().size());
+ BOOST_CHECK_EQUAL(status.getNFibEntries(), forwarder.getFib().size());
+ BOOST_CHECK_EQUAL(status.getNPitEntries(), forwarder.getPit().size());
+ BOOST_CHECK_EQUAL(status.getNMeasurementsEntries(), forwarder.getMeasurements().size());
+ BOOST_CHECK_EQUAL(status.getNCsEntries(), forwarder.getCs().size());
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+} // namespace nfd
diff --git a/tests/daemon/mgmt/strategy-choice-manager.cpp b/tests/daemon/mgmt/strategy-choice-manager.cpp
new file mode 100644
index 0000000..99d5636
--- /dev/null
+++ b/tests/daemon/mgmt/strategy-choice-manager.cpp
@@ -0,0 +1,548 @@
+/* -*- 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 "mgmt/strategy-choice-manager.hpp"
+#include "face/face.hpp"
+#include "mgmt/internal-face.hpp"
+#include "table/name-tree.hpp"
+#include "table/strategy-choice.hpp"
+#include "fw/forwarder.hpp"
+#include "fw/strategy.hpp"
+#include "tests/daemon/face/dummy-face.hpp"
+#include "tests/daemon/fw/dummy-strategy.hpp"
+
+#include "tests/test-common.hpp"
+#include "validation-common.hpp"
+
+namespace nfd {
+namespace tests {
+
+NFD_LOG_INIT("StrategyChoiceManagerTest");
+
+class StrategyChoiceManagerFixture : protected BaseFixture
+{
+public:
+
+ StrategyChoiceManagerFixture()
+ : m_strategyChoice(m_forwarder.getStrategyChoice())
+ , m_face(make_shared<InternalFace>())
+ , m_manager(m_strategyChoice, m_face)
+ , m_callbackFired(false)
+ {
+ m_strategyChoice.install(make_shared<DummyStrategy>(boost::ref(m_forwarder), "/localhost/nfd/strategy/test-strategy-a"));
+ m_strategyChoice.insert("ndn:/", "/localhost/nfd/strategy/test-strategy-a");
+ }
+
+ virtual
+ ~StrategyChoiceManagerFixture()
+ {
+
+ }
+
+ void
+ validateControlResponseCommon(const Data& response,
+ const Name& expectedName,
+ uint32_t expectedCode,
+ const std::string& expectedText,
+ ControlResponse& control)
+ {
+ m_callbackFired = true;
+ Block controlRaw = response.getContent().blockFromValue();
+
+ control.wireDecode(controlRaw);
+
+ NFD_LOG_DEBUG("received control response"
+ << " Name: " << response.getName()
+ << " code: " << control.getCode()
+ << " text: " << control.getText());
+
+ BOOST_CHECK_EQUAL(response.getName(), expectedName);
+ BOOST_CHECK_EQUAL(control.getCode(), expectedCode);
+ BOOST_CHECK_EQUAL(control.getText(), expectedText);
+ }
+
+ void
+ validateControlResponse(const Data& response,
+ const Name& expectedName,
+ uint32_t expectedCode,
+ const std::string& expectedText)
+ {
+ ControlResponse control;
+ validateControlResponseCommon(response, expectedName,
+ expectedCode, expectedText, control);
+
+ if (!control.getBody().empty())
+ {
+ BOOST_FAIL("found unexpected control response body");
+ }
+ }
+
+ void
+ validateControlResponse(const Data& response,
+ const Name& expectedName,
+ uint32_t expectedCode,
+ const std::string& expectedText,
+ const Block& expectedBody)
+ {
+ ControlResponse control;
+ validateControlResponseCommon(response, expectedName,
+ expectedCode, expectedText, control);
+
+ BOOST_REQUIRE(!control.getBody().empty());
+ BOOST_REQUIRE(control.getBody().value_size() == expectedBody.value_size());
+
+ BOOST_CHECK(memcmp(control.getBody().value(), expectedBody.value(),
+ expectedBody.value_size()) == 0);
+
+ }
+
+ bool
+ didCallbackFire()
+ {
+ return m_callbackFired;
+ }
+
+ void
+ resetCallbackFired()
+ {
+ m_callbackFired = false;
+ }
+
+ shared_ptr<InternalFace>&
+ getFace()
+ {
+ return m_face;
+ }
+
+ StrategyChoiceManager&
+ getManager()
+ {
+ return m_manager;
+ }
+
+ StrategyChoice&
+ getStrategyChoice()
+ {
+ return m_strategyChoice;
+ }
+
+ void
+ addInterestRule(const std::string& regex,
+ ndn::IdentityCertificate& certificate)
+ {
+ m_manager.addInterestRule(regex, certificate);
+ }
+
+protected:
+ Forwarder m_forwarder;
+ StrategyChoice& m_strategyChoice;
+ shared_ptr<InternalFace> m_face;
+ StrategyChoiceManager m_manager;
+
+private:
+ bool m_callbackFired;
+};
+
+class AllStrategiesFixture : public StrategyChoiceManagerFixture
+{
+public:
+ AllStrategiesFixture()
+ {
+ m_strategyChoice.install(make_shared<DummyStrategy>(boost::ref(m_forwarder), "/localhost/nfd/strategy/test-strategy-b"));
+ }
+
+ virtual
+ ~AllStrategiesFixture()
+ {
+
+ }
+};
+
+template <typename T> class AuthorizedCommandFixture : public CommandFixture<T>
+{
+public:
+ AuthorizedCommandFixture()
+ {
+ const std::string regex = "^<localhost><nfd><strategy-choice>";
+ T::addInterestRule(regex, *CommandFixture<T>::m_certificate);
+ }
+
+ virtual
+ ~AuthorizedCommandFixture()
+ {
+
+ }
+};
+
+BOOST_FIXTURE_TEST_SUITE(MgmtStrategyChoiceManager,
+ AuthorizedCommandFixture<AllStrategiesFixture>)
+
+BOOST_FIXTURE_TEST_CASE(TestFireInterestFilter, AllStrategiesFixture)
+{
+ shared_ptr<Interest> command(make_shared<Interest>("/localhost/nfd/strategy-choice"));
+
+ getFace()->onReceiveData +=
+ bind(&StrategyChoiceManagerFixture::validateControlResponse, this, _1,
+ command->getName(), 400, "Malformed command");
+
+ getFace()->sendInterest(*command);
+ g_io.run_one();
+
+ BOOST_REQUIRE(didCallbackFire());
+}
+
+BOOST_FIXTURE_TEST_CASE(MalformedCommmand, AllStrategiesFixture)
+{
+ shared_ptr<Interest> command(make_shared<Interest>("/localhost/nfd/strategy-choice"));
+
+ getFace()->onReceiveData +=
+ bind(&StrategyChoiceManagerFixture::validateControlResponse, this, _1,
+ command->getName(), 400, "Malformed command");
+
+ getManager().onStrategyChoiceRequest(*command);
+
+ BOOST_REQUIRE(didCallbackFire());
+}
+
+BOOST_FIXTURE_TEST_CASE(UnsignedCommand, AllStrategiesFixture)
+{
+ ControlParameters parameters;
+ parameters.setName("/test");
+ parameters.setStrategy("/localhost/nfd/strategy/best-route");
+
+ Block encodedParameters(parameters.wireEncode());
+
+ Name commandName("/localhost/nfd/strategy-choice");
+ commandName.append("set");
+ commandName.append(encodedParameters);
+
+ shared_ptr<Interest> command(make_shared<Interest>(commandName));
+
+ getFace()->onReceiveData +=
+ bind(&StrategyChoiceManagerFixture::validateControlResponse, this, _1,
+ command->getName(), 401, "Signature required");
+
+ getManager().onStrategyChoiceRequest(*command);
+
+ BOOST_REQUIRE(didCallbackFire());
+}
+
+BOOST_FIXTURE_TEST_CASE(UnauthorizedCommand,
+ UnauthorizedCommandFixture<StrategyChoiceManagerFixture>)
+{
+ ControlParameters parameters;
+ parameters.setName("/test");
+ parameters.setStrategy("/localhost/nfd/strategy/best-route");
+
+ Block encodedParameters(parameters.wireEncode());
+
+ Name commandName("/localhost/nfd/strategy-choice");
+ commandName.append("set");
+ commandName.append(encodedParameters);
+
+ shared_ptr<Interest> command(make_shared<Interest>(commandName));
+ generateCommand(*command);
+
+ getFace()->onReceiveData +=
+ bind(&StrategyChoiceManagerFixture::validateControlResponse, this, _1,
+ command->getName(), 403, "Unauthorized command");
+
+ getManager().onStrategyChoiceRequest(*command);
+
+ BOOST_REQUIRE(didCallbackFire());
+}
+
+BOOST_AUTO_TEST_CASE(UnsupportedVerb)
+{
+ ControlParameters parameters;
+ parameters.setName("/test");
+ parameters.setStrategy("/localhost/nfd/strategy/test-strategy-b");
+
+ Block encodedParameters(parameters.wireEncode());
+
+ Name commandName("/localhost/nfd/strategy-choice");
+ commandName.append("unsupported");
+ commandName.append(encodedParameters);
+
+ shared_ptr<Interest> command(make_shared<Interest>(commandName));
+ generateCommand(*command);
+
+ getFace()->onReceiveData +=
+ bind(&StrategyChoiceManagerFixture::validateControlResponse, this, _1,
+ command->getName(), 501, "Unsupported command");
+
+ getManager().onValidatedStrategyChoiceRequest(command);
+
+ BOOST_REQUIRE(didCallbackFire());
+}
+
+BOOST_AUTO_TEST_CASE(BadOptionParse)
+{
+ Name commandName("/localhost/nfd/strategy-choice");
+ commandName.append("set");
+ commandName.append("NotReallyParameters");
+
+ shared_ptr<Interest> command(make_shared<Interest>(commandName));
+ generateCommand(*command);
+
+ getFace()->onReceiveData +=
+ bind(&StrategyChoiceManagerFixture::validateControlResponse, this, _1,
+ command->getName(), 400, "Malformed command");
+
+ getManager().onValidatedStrategyChoiceRequest(command);
+
+ BOOST_REQUIRE(didCallbackFire());
+}
+
+BOOST_AUTO_TEST_CASE(SetStrategies)
+{
+ ControlParameters parameters;
+ parameters.setName("/test");
+ parameters.setStrategy("/localhost/nfd/strategy/test-strategy-b");
+
+ Block encodedParameters(parameters.wireEncode());
+
+ Name commandName("/localhost/nfd/strategy-choice");
+ commandName.append("set");
+ commandName.append(encodedParameters);
+
+ shared_ptr<Interest> command(make_shared<Interest>(commandName));
+
+ getFace()->onReceiveData +=
+ bind(&StrategyChoiceManagerFixture::validateControlResponse, this, _1,
+ command->getName(), 200, "Success", encodedParameters);
+
+ getManager().onValidatedStrategyChoiceRequest(command);
+
+ BOOST_REQUIRE(didCallbackFire());
+ fw::Strategy& strategy = getStrategyChoice().findEffectiveStrategy("/test");
+ BOOST_REQUIRE_EQUAL(strategy.getName(), "/localhost/nfd/strategy/test-strategy-b");
+}
+
+BOOST_AUTO_TEST_CASE(SetStrategiesMissingName)
+{
+ ControlParameters parameters;
+ parameters.setStrategy("/localhost/nfd/strategy/test-strategy-b");
+
+ Block encodedParameters(parameters.wireEncode());
+
+ Name commandName("/localhost/nfd/strategy-choice");
+ commandName.append("set");
+ commandName.append(encodedParameters);
+
+ shared_ptr<Interest> command(make_shared<Interest>(commandName));
+
+ getFace()->onReceiveData +=
+ bind(&StrategyChoiceManagerFixture::validateControlResponse, this, _1,
+ command->getName(), 400, "Malformed command");
+
+ getManager().onValidatedStrategyChoiceRequest(command);
+
+ BOOST_REQUIRE(didCallbackFire());
+}
+
+BOOST_AUTO_TEST_CASE(SetStrategiesMissingStrategy)
+{
+ ControlParameters parameters;
+ parameters.setName("/test");
+
+ Block encodedParameters(parameters.wireEncode());
+
+ Name commandName("/localhost/nfd/strategy-choice");
+ commandName.append("set");
+ commandName.append(encodedParameters);
+
+ shared_ptr<Interest> command(make_shared<Interest>(commandName));
+
+ getFace()->onReceiveData +=
+ bind(&StrategyChoiceManagerFixture::validateControlResponse, this, _1,
+ command->getName(), 400, "Malformed command");
+
+ getManager().onValidatedStrategyChoiceRequest(command);
+
+ BOOST_REQUIRE(didCallbackFire());
+ fw::Strategy& strategy = getStrategyChoice().findEffectiveStrategy("/test");
+ BOOST_REQUIRE_EQUAL(strategy.getName(), "/localhost/nfd/strategy/test-strategy-a");
+}
+
+BOOST_AUTO_TEST_CASE(SetUnsupportedStrategy)
+{
+ ControlParameters parameters;
+ parameters.setName("/test");
+ parameters.setStrategy("/localhost/nfd/strategy/unit-test-doesnotexist");
+
+ Block encodedParameters(parameters.wireEncode());
+
+ Name commandName("/localhost/nfd/strategy-choice");
+ commandName.append("set");
+ commandName.append(encodedParameters);
+
+ shared_ptr<Interest> command(make_shared<Interest>(commandName));
+ generateCommand(*command);
+
+ getFace()->onReceiveData +=
+ bind(&StrategyChoiceManagerFixture::validateControlResponse, this, _1,
+ command->getName(), 504, "Unsupported strategy");
+
+ getManager().onValidatedStrategyChoiceRequest(command);
+
+ BOOST_REQUIRE(didCallbackFire());
+ fw::Strategy& strategy = getStrategyChoice().findEffectiveStrategy("/test");
+ BOOST_CHECK_EQUAL(strategy.getName(), "/localhost/nfd/strategy/test-strategy-a");
+}
+
+class DefaultStrategyOnlyFixture : public StrategyChoiceManagerFixture
+{
+public:
+ DefaultStrategyOnlyFixture()
+ : StrategyChoiceManagerFixture()
+ {
+
+ }
+
+ virtual
+ ~DefaultStrategyOnlyFixture()
+ {
+
+ }
+};
+
+
+/// \todo I'm not sure this code branch (code 405) can happen. The manager tests for the strategy first and will return 504.
+// BOOST_FIXTURE_TEST_CASE(SetNotInstalled, DefaultStrategyOnlyFixture)
+// {
+// BOOST_REQUIRE(!getStrategyChoice().hasStrategy("/localhost/nfd/strategy/test-strategy-b"));
+// ControlParameters parameters;
+// parameters.setName("/test");
+// parameters.setStrategy("/localhost/nfd/strategy/test-strategy-b");
+
+// Block encodedParameters(parameters.wireEncode());
+
+// Name commandName("/localhost/nfd/strategy-choice");
+// commandName.append("set");
+// commandName.append(encodedParameters);
+
+// shared_ptr<Interest> command(make_shared<Interest>(commandName));
+// generateCommand(*command);
+
+// getFace()->onReceiveData +=
+// bind(&StrategyChoiceManagerFixture::validateControlResponse, this, _1,
+// command->getName(), 405, "Strategy not installed");
+
+// getManager().onValidatedStrategyChoiceRequest(command);
+
+// BOOST_REQUIRE(didCallbackFire());
+// fw::Strategy& strategy = getStrategyChoice().findEffectiveStrategy("/test");
+// BOOST_CHECK_EQUAL(strategy.getName(), "/localhost/nfd/strategy/test-strategy-a");
+// }
+
+BOOST_AUTO_TEST_CASE(Unset)
+{
+ ControlParameters parameters;
+ parameters.setName("/test");
+
+ BOOST_REQUIRE(m_strategyChoice.insert("/test", "/localhost/nfd/strategy/test-strategy-b"));
+ BOOST_REQUIRE_EQUAL(m_strategyChoice.findEffectiveStrategy("/test").getName(),
+ "/localhost/nfd/strategy/test-strategy-b");
+
+ Block encodedParameters(parameters.wireEncode());
+
+ Name commandName("/localhost/nfd/strategy-choice");
+ commandName.append("unset");
+ commandName.append(encodedParameters);
+
+ shared_ptr<Interest> command(make_shared<Interest>(commandName));
+ generateCommand(*command);
+
+ getFace()->onReceiveData +=
+ bind(&StrategyChoiceManagerFixture::validateControlResponse, this, _1,
+ command->getName(), 200, "Success", encodedParameters);
+
+ getManager().onValidatedStrategyChoiceRequest(command);
+
+ BOOST_REQUIRE(didCallbackFire());
+
+ BOOST_CHECK_EQUAL(m_strategyChoice.findEffectiveStrategy("/test").getName(),
+ "/localhost/nfd/strategy/test-strategy-a");
+}
+
+BOOST_AUTO_TEST_CASE(UnsetRoot)
+{
+ ControlParameters parameters;
+ parameters.setName("/");
+
+ Block encodedParameters(parameters.wireEncode());
+
+ Name commandName("/localhost/nfd/strategy-choice");
+ commandName.append("unset");
+ commandName.append(encodedParameters);
+
+ shared_ptr<Interest> command(make_shared<Interest>(commandName));
+ generateCommand(*command);
+
+ getFace()->onReceiveData +=
+ bind(&StrategyChoiceManagerFixture::validateControlResponse, this, _1,
+ command->getName(), 403, "Cannot unset root prefix strategy");
+
+ getManager().onValidatedStrategyChoiceRequest(command);
+
+ BOOST_REQUIRE(didCallbackFire());
+
+ BOOST_CHECK_EQUAL(m_strategyChoice.findEffectiveStrategy("/test").getName(),
+ "/localhost/nfd/strategy/test-strategy-a");
+}
+
+BOOST_AUTO_TEST_CASE(UnsetMissingName)
+{
+ ControlParameters parameters;
+
+ BOOST_REQUIRE(m_strategyChoice.insert("/test", "/localhost/nfd/strategy/test-strategy-b"));
+ BOOST_REQUIRE_EQUAL(m_strategyChoice.findEffectiveStrategy("/test").getName(),
+ "/localhost/nfd/strategy/test-strategy-b");
+
+ Block encodedParameters(parameters.wireEncode());
+
+ Name commandName("/localhost/nfd/strategy-choice");
+ commandName.append("unset");
+ commandName.append(encodedParameters);
+
+ shared_ptr<Interest> command(make_shared<Interest>(commandName));
+ generateCommand(*command);
+
+ getFace()->onReceiveData +=
+ bind(&StrategyChoiceManagerFixture::validateControlResponse, this, _1,
+ command->getName(), 400, "Malformed command");
+
+ getManager().onValidatedStrategyChoiceRequest(command);
+
+ BOOST_REQUIRE(didCallbackFire());
+
+ BOOST_CHECK_EQUAL(m_strategyChoice.findEffectiveStrategy("/test").getName(),
+ "/localhost/nfd/strategy/test-strategy-b");
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+} // namespace nfd
diff --git a/tests/daemon/mgmt/validation-common.cpp b/tests/daemon/mgmt/validation-common.cpp
new file mode 100644
index 0000000..6065c27
--- /dev/null
+++ b/tests/daemon/mgmt/validation-common.cpp
@@ -0,0 +1,50 @@
+/* -*- 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 "validation-common.hpp"
+
+#include <boost/test/unit_test.hpp>
+
+namespace nfd {
+namespace tests {
+
+const Name CommandIdentityGlobalFixture::s_identityName("/unit-test/CommandFixture/id");
+shared_ptr<ndn::IdentityCertificate> CommandIdentityGlobalFixture::s_certificate;
+
+CommandIdentityGlobalFixture::CommandIdentityGlobalFixture()
+{
+ BOOST_ASSERT(!static_cast<bool>(s_certificate));
+ s_certificate = m_keys.getCertificate(m_keys.createIdentity(s_identityName));
+}
+
+CommandIdentityGlobalFixture::~CommandIdentityGlobalFixture()
+{
+ s_certificate.reset();
+ m_keys.deleteIdentity(s_identityName);
+}
+
+BOOST_GLOBAL_FIXTURE(CommandIdentityGlobalFixture)
+
+} // namespace tests
+} // namespace nfd
diff --git a/tests/daemon/mgmt/validation-common.hpp b/tests/daemon/mgmt/validation-common.hpp
new file mode 100644
index 0000000..9aa2e28
--- /dev/null
+++ b/tests/daemon/mgmt/validation-common.hpp
@@ -0,0 +1,125 @@
+/* -*- 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_MGMT_VALIDATION_COMMON_HPP
+#define NFD_TESTS_NFD_MGMT_VALIDATION_COMMON_HPP
+
+#include "common.hpp"
+#include <ndn-cpp-dev/util/command-interest-generator.hpp>
+
+namespace nfd {
+namespace tests {
+
+// class ValidatedManagementFixture
+// {
+// public:
+// ValidatedManagementFixture()
+// : m_validator(make_shared<ndn::CommandInterestValidator>())
+// {
+// }
+
+// virtual
+// ~ValidatedManagementFixture()
+// {
+// }
+
+// protected:
+// shared_ptr<ndn::CommandInterestValidator> m_validator;
+// };
+
+/// a global fixture that holds the identity for CommandFixture
+class CommandIdentityGlobalFixture
+{
+public:
+ CommandIdentityGlobalFixture();
+
+ ~CommandIdentityGlobalFixture();
+
+ static const Name& getIdentityName()
+ {
+ return s_identityName;
+ }
+
+ static shared_ptr<ndn::IdentityCertificate> getCertificate()
+ {
+ BOOST_ASSERT(static_cast<bool>(s_certificate));
+ return s_certificate;
+ }
+
+private:
+ ndn::KeyChain m_keys;
+ static const Name s_identityName;
+ static shared_ptr<ndn::IdentityCertificate> s_certificate;
+};
+
+template<typename T>
+class CommandFixture : public T
+{
+public:
+ virtual
+ ~CommandFixture()
+ {
+ }
+
+ void
+ generateCommand(Interest& interest)
+ {
+ m_generator.generateWithIdentity(interest, getIdentityName());
+ }
+
+ const Name&
+ getIdentityName() const
+ {
+ return CommandIdentityGlobalFixture::getIdentityName();
+ }
+
+protected:
+ CommandFixture()
+ : m_certificate(CommandIdentityGlobalFixture::getCertificate())
+ {
+ }
+
+protected:
+ shared_ptr<ndn::IdentityCertificate> m_certificate;
+ ndn::CommandInterestGenerator m_generator;
+};
+
+template <typename T>
+class UnauthorizedCommandFixture : public CommandFixture<T>
+{
+public:
+ UnauthorizedCommandFixture()
+ {
+ }
+
+ virtual
+ ~UnauthorizedCommandFixture()
+ {
+ }
+};
+
+} // namespace tests
+} // namespace nfd
+
+#endif // NFD_TESTS_NFD_MGMT_VALIDATION_COMMON_HPP
diff --git a/tests/daemon/table/cs.cpp b/tests/daemon/table/cs.cpp
new file mode 100644
index 0000000..cdcc0db
--- /dev/null
+++ b/tests/daemon/table/cs.cpp
@@ -0,0 +1,919 @@
+/* -*- 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/>.
+ *
+ * \author Ilya Moiseenko <iliamo@ucla.edu>
+ **/
+
+#include "table/cs.hpp"
+#include "table/cs-entry.hpp"
+
+#include "tests/test-common.hpp"
+
+namespace nfd {
+namespace tests {
+
+class CsAccessor : public Cs
+{
+public:
+ bool
+ evictItem_accessor()
+ {
+ return evictItem();
+ }
+};
+
+BOOST_FIXTURE_TEST_SUITE(TableCs, BaseFixture)
+
+BOOST_AUTO_TEST_CASE(Insertion)
+{
+ Cs cs;
+
+ BOOST_CHECK_EQUAL(cs.insert(*makeData("/insertion")), true);
+}
+
+BOOST_AUTO_TEST_CASE(Insertion2)
+{
+ Cs cs;
+
+ cs.insert(*makeData("/a"));
+ cs.insert(*makeData("/b"));
+ cs.insert(*makeData("/c"));
+ cs.insert(*makeData("/d"));
+
+ BOOST_CHECK_EQUAL(cs.size(), 4);
+}
+
+
+BOOST_AUTO_TEST_CASE(DuplicateInsertion)
+{
+ Cs cs;
+
+ shared_ptr<Data> data0 = makeData("/insert/smth");
+ BOOST_CHECK_EQUAL(cs.insert(*data0), true);
+
+ shared_ptr<Data> data = makeData("/insert/duplicate");
+ BOOST_CHECK_EQUAL(cs.insert(*data), true);
+
+ cs.insert(*data);
+ BOOST_CHECK_EQUAL(cs.size(), 2);
+}
+
+
+BOOST_AUTO_TEST_CASE(DuplicateInsertion2)
+{
+ Cs cs;
+
+ shared_ptr<Data> data = makeData("/insert/duplicate");
+ BOOST_CHECK_EQUAL(cs.insert(*data), true);
+
+ cs.insert(*data);
+ BOOST_CHECK_EQUAL(cs.size(), 1);
+
+ shared_ptr<Data> data2 = makeData("/insert/original");
+ BOOST_CHECK_EQUAL(cs.insert(*data2), true);
+ BOOST_CHECK_EQUAL(cs.size(), 2);
+}
+
+BOOST_AUTO_TEST_CASE(InsertAndFind)
+{
+ Cs cs;
+
+ Name name("/insert/and/find");
+
+ shared_ptr<Data> data = makeData(name);
+ BOOST_CHECK_EQUAL(cs.insert(*data), true);
+
+ shared_ptr<Interest> interest = make_shared<Interest>(name);
+
+ const Data* found = cs.find(*interest);
+
+ BOOST_REQUIRE(found != 0);
+ BOOST_CHECK_EQUAL(data->getName(), found->getName());
+}
+
+BOOST_AUTO_TEST_CASE(InsertAndNotFind)
+{
+ Cs cs;
+
+ Name name("/insert/and/find");
+ shared_ptr<Data> data = makeData(name);
+ BOOST_CHECK_EQUAL(cs.insert(*data), true);
+
+ Name name2("/not/find");
+ shared_ptr<Interest> interest = make_shared<Interest>(name2);
+
+ const Data* found = cs.find(*interest);
+
+ BOOST_CHECK_EQUAL(found, static_cast<const Data*>(0));
+}
+
+
+BOOST_AUTO_TEST_CASE(InsertAndErase)
+{
+ CsAccessor cs;
+
+ shared_ptr<Data> data = makeData("/insertandelete");
+ cs.insert(*data);
+ BOOST_CHECK_EQUAL(cs.size(), 1);
+
+ cs.evictItem_accessor();
+ BOOST_CHECK_EQUAL(cs.size(), 0);
+}
+
+BOOST_AUTO_TEST_CASE(StalenessQueue)
+{
+ CsAccessor cs;
+
+ Name name2("/insert/fresh");
+ shared_ptr<Data> data2 = makeData(name2);
+ data2->setFreshnessPeriod(time::milliseconds(5000));
+ cs.insert(*data2);
+
+ Name name("/insert/expire");
+ shared_ptr<Data> data = makeData(name);
+ data->setFreshnessPeriod(time::milliseconds(500));
+ cs.insert(*data);
+
+ BOOST_CHECK_EQUAL(cs.size(), 2);
+
+ sleep(3);
+
+ cs.evictItem_accessor();
+ BOOST_CHECK_EQUAL(cs.size(), 1);
+
+ shared_ptr<Interest> interest = make_shared<Interest>(name2);
+ const Data* found = cs.find(*interest);
+ BOOST_REQUIRE(found != 0);
+ BOOST_CHECK_EQUAL(found->getName(), name2);
+}
+
+//even max size
+BOOST_AUTO_TEST_CASE(ReplacementEvenSize)
+{
+ Cs cs(4);
+
+ shared_ptr<Data> data = makeData("/a");
+ cs.insert(*data);
+
+ shared_ptr<Data> data2 = makeData("/b");
+ cs.insert(*data2);
+
+ shared_ptr<Data> data3 = makeData("/c");
+ cs.insert(*data3);
+
+ shared_ptr<Data> data4 = makeData("/d");
+ cs.insert(*data4);
+
+ shared_ptr<Data> data5 = makeData("/e");
+ cs.insert(*data5);
+
+ BOOST_CHECK_EQUAL(cs.size(), 4);
+}
+
+//odd max size
+BOOST_AUTO_TEST_CASE(Replacement2)
+{
+ Cs cs(3);
+
+ shared_ptr<Data> data = makeData("/a");
+ cs.insert(*data);
+
+ shared_ptr<Data> data2 = makeData("/b");
+ cs.insert(*data2);
+
+ shared_ptr<Data> data3 = makeData("/c");
+ cs.insert(*data3);
+
+ shared_ptr<Data> data4 = makeData("/d");
+ cs.insert(*data4);
+
+ BOOST_CHECK_EQUAL(cs.size(), 3);
+}
+
+BOOST_AUTO_TEST_CASE(InsertAndEraseByName)
+{
+ CsAccessor cs;
+
+ Name name("/insertandremovebyname");
+ shared_ptr<Data> data = makeData(name);
+ cs.insert(*data);
+
+ ndn::ConstBufferPtr digest1 = ndn::crypto::sha256(data->wireEncode().wire(),
+ data->wireEncode().size());
+
+ shared_ptr<Data> data2 = makeData("/a");
+ cs.insert(*data2);
+
+ shared_ptr<Data> data3 = makeData("/z");
+ cs.insert(*data3);
+
+ BOOST_CHECK_EQUAL(cs.size(), 3);
+
+ name.append(digest1->buf(), digest1->size());
+ cs.erase(name);
+ BOOST_CHECK_EQUAL(cs.size(), 2);
+}
+
+BOOST_AUTO_TEST_CASE(DigestCalculation)
+{
+ shared_ptr<Data> data = makeData("/digest/compute");
+
+ ndn::ConstBufferPtr digest1 = ndn::crypto::sha256(data->wireEncode().wire(),
+ data->wireEncode().size());
+ BOOST_CHECK_EQUAL(digest1->size(), 32);
+
+ cs::Entry* entry = new cs::Entry();
+ entry->setData(*data, false);
+
+ BOOST_CHECK_EQUAL_COLLECTIONS(digest1->begin(), digest1->end(),
+ entry->getDigest()->begin(), entry->getDigest()->end());
+}
+
+BOOST_AUTO_TEST_CASE(InsertCanonical)
+{
+ Cs cs;
+
+ shared_ptr<Data> data = makeData("/a");
+ cs.insert(*data);
+
+ shared_ptr<Data> data2 = makeData("/b");
+ cs.insert(*data2);
+
+ shared_ptr<Data> data3 = makeData("/c");
+ cs.insert(*data3);
+
+ shared_ptr<Data> data4 = makeData("/d");
+ cs.insert(*data4);
+
+ shared_ptr<Data> data5 = makeData("/c/c/1/2/3/4/5/6");
+ cs.insert(*data5);
+
+ shared_ptr<Data> data6 = makeData("/c/c/1/2/3");
+ cs.insert(*data6);
+
+ shared_ptr<Data> data7 = makeData("/c/c/1");
+ cs.insert(*data7);
+}
+
+BOOST_AUTO_TEST_CASE(EraseCanonical)
+{
+ Cs cs;
+
+ shared_ptr<Data> data = makeData("/a");
+ cs.insert(*data);
+
+ shared_ptr<Data> data2 = makeData("/b");
+ cs.insert(*data2);
+
+ shared_ptr<Data> data3 = makeData("/c");
+ cs.insert(*data3);
+
+ shared_ptr<Data> data4 = makeData("/d");
+ cs.insert(*data4);
+
+ shared_ptr<Data> data5 = makeData("/c/c/1/2/3/4/5/6");
+ cs.insert(*data5);
+
+ shared_ptr<Data> data6 = makeData("/c/c/1/2/3");
+ cs.insert(*data6);
+
+ shared_ptr<Data> data7 = makeData("/c/c/1");
+ cs.insert(*data7);
+
+ ndn::ConstBufferPtr digest1 = ndn::crypto::sha256(data->wireEncode().wire(),
+ data->wireEncode().size());
+
+ Name name("/a");
+ name.append(digest1->buf(), digest1->size());
+ cs.erase(name);
+ BOOST_CHECK_EQUAL(cs.size(), 6);
+}
+
+BOOST_AUTO_TEST_CASE(ImplicitDigestSelector)
+{
+ CsAccessor cs;
+
+ Name name("/digest/works");
+ shared_ptr<Data> data = makeData(name);
+ cs.insert(*data);
+
+ shared_ptr<Data> data2 = makeData("/a");
+ cs.insert(*data2);
+
+ shared_ptr<Data> data3 = makeData("/z/z/z");
+ cs.insert(*data3);
+
+ ndn::ConstBufferPtr digest1 = ndn::crypto::sha256(data->wireEncode().wire(),
+ data->wireEncode().size());
+ uint8_t digest2[32] = {0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1};
+
+ shared_ptr<Interest> interest = make_shared<Interest>();
+ interest->setName(Name(name).append(digest1->buf(), digest1->size()));
+ interest->setMinSuffixComponents(0);
+ interest->setMaxSuffixComponents(0);
+
+ const Data* found = cs.find(*interest);
+ BOOST_CHECK_NE(found, static_cast<const Data*>(0));
+ BOOST_CHECK_EQUAL(found->getName(), name);
+
+ shared_ptr<Interest> interest2 = make_shared<Interest>();
+ interest2->setName(Name(name).append(digest2, 32));
+ interest2->setMinSuffixComponents(0);
+ interest2->setMaxSuffixComponents(0);
+
+ const Data* notfound = cs.find(*interest2);
+ BOOST_CHECK_EQUAL(notfound, static_cast<const Data*>(0));
+}
+
+BOOST_AUTO_TEST_CASE(ChildSelector)
+{
+ Cs cs;
+
+ shared_ptr<Data> data = makeData("/a");
+ cs.insert(*data);
+
+ shared_ptr<Data> data2 = makeData("/b");
+ cs.insert(*data2);
+
+ shared_ptr<Data> data4 = makeData("/d");
+ cs.insert(*data4);
+
+ shared_ptr<Data> data5 = makeData("/c/c");
+ cs.insert(*data5);
+
+ shared_ptr<Data> data6 = makeData("/c/f");
+ cs.insert(*data6);
+
+ shared_ptr<Data> data7 = makeData("/c/n");
+ cs.insert(*data7);
+
+ shared_ptr<Interest> interest = make_shared<Interest>("/c");
+ interest->setChildSelector(1);
+
+ const Data* found = cs.find(*interest);
+ BOOST_CHECK_EQUAL(found->getName(), "/c/n");
+
+ shared_ptr<Interest> interest2 = make_shared<Interest>("/c");
+ interest2->setChildSelector(0);
+
+ const Data* found2 = cs.find(*interest2);
+ BOOST_CHECK_EQUAL(found2->getName(), "/c/c");
+}
+
+BOOST_AUTO_TEST_CASE(ChildSelector2)
+{
+ Cs cs;
+
+ shared_ptr<Data> data = makeData("/a/b/1");
+ cs.insert(*data);
+
+ shared_ptr<Data> data2 = makeData("/a/b/2");
+ cs.insert(*data2);
+
+ shared_ptr<Data> data3 = makeData("/a/z/1");
+ cs.insert(*data3);
+
+ shared_ptr<Data> data4 = makeData("/a/z/2");
+ cs.insert(*data4);
+
+ shared_ptr<Interest> interest = make_shared<Interest>("/a");
+ interest->setChildSelector(1);
+
+ const Data* found = cs.find(*interest);
+ BOOST_CHECK_EQUAL(found->getName(), "/a/z/1");
+}
+
+BOOST_AUTO_TEST_CASE(MustBeFreshSelector)
+{
+ Cs cs;
+
+ Name name("/insert/nonfresh");
+ shared_ptr<Data> data = makeData(name);
+ data->setFreshnessPeriod(time::milliseconds(500));
+ cs.insert(*data);
+
+ sleep(1);
+
+ shared_ptr<Interest> interest = make_shared<Interest>(name);
+ interest->setMustBeFresh(true);
+
+ const Data* found = cs.find(*interest);
+ BOOST_CHECK_EQUAL(found, static_cast<const Data*>(0));
+}
+
+BOOST_AUTO_TEST_CASE(PublisherKeySelector)
+{
+ Cs cs;
+
+ Name name("/insert/withkey");
+ shared_ptr<Data> data = makeData(name);
+ cs.insert(*data);
+
+ shared_ptr<Interest> interest = make_shared<Interest>(name);
+ Name keyName("/somewhere/key");
+
+ ndn::KeyLocator locator(keyName);
+ interest->setPublisherPublicKeyLocator(locator);
+
+ const Data* found = cs.find(*interest);
+ BOOST_CHECK_EQUAL(found, static_cast<const Data*>(0));
+}
+
+BOOST_AUTO_TEST_CASE(PublisherKeySelector2)
+{
+ Cs cs;
+ Name name("/insert/withkey");
+ shared_ptr<Data> data = makeData(name);
+ cs.insert(*data);
+
+ Name name2("/insert/withkey2");
+ shared_ptr<Data> data2 = make_shared<Data>(name2);
+
+ Name keyName("/somewhere/key");
+ const ndn::KeyLocator locator(keyName);
+
+ ndn::SignatureSha256WithRsa fakeSignature;
+ fakeSignature.setValue(ndn::dataBlock(tlv::SignatureValue,
+ reinterpret_cast<const uint8_t*>(0), 0));
+
+ fakeSignature.setKeyLocator(locator);
+ data2->setSignature(fakeSignature);
+
+ cs.insert(*data2);
+
+ shared_ptr<Interest> interest = make_shared<Interest>(name2);
+ interest->setPublisherPublicKeyLocator(locator);
+
+ const Data* found = cs.find(*interest);
+ BOOST_CHECK_NE(found, static_cast<const Data*>(0));
+ BOOST_CHECK_EQUAL(found->getName(), data2->getName());
+}
+
+
+BOOST_AUTO_TEST_CASE(MinMaxComponentsSelector)
+{
+ Cs cs;
+
+ shared_ptr<Data> data = makeData("/a");
+ cs.insert(*data);
+
+ shared_ptr<Data> data2 = makeData("/b");
+ cs.insert(*data2);
+
+ shared_ptr<Data> data4 = makeData("/d");
+ cs.insert(*data4);
+
+ shared_ptr<Data> data5 = makeData("/c/c/1/2/3/4/5/6");
+ cs.insert(*data5);
+
+ shared_ptr<Data> data6 = makeData("/c/c/6/7/8/9");
+ cs.insert(*data6);
+
+ shared_ptr<Data> data7 = makeData("/c/c/1/2/3");
+ cs.insert(*data7);
+
+ shared_ptr<Data> data8 = makeData("/c/c/1");
+ cs.insert(*data8);
+
+ shared_ptr<Interest> interest = make_shared<Interest>("/c/c");
+ interest->setMinSuffixComponents(3);
+ interest->setChildSelector(0);
+
+ const Data* found = cs.find(*interest);
+ BOOST_CHECK_EQUAL(found->getName(), "/c/c/1/2/3/4/5/6");
+
+ shared_ptr<Interest> interest2 = make_shared<Interest>("/c/c");
+ interest2->setMinSuffixComponents(4);
+ interest2->setChildSelector(1);
+
+ const Data* found2 = cs.find(*interest2);
+ BOOST_CHECK_EQUAL(found2->getName(), "/c/c/6/7/8/9");
+
+ shared_ptr<Interest> interest3 = make_shared<Interest>("/c/c");
+ interest3->setMaxSuffixComponents(2);
+ interest3->setChildSelector(1);
+
+ const Data* found3 = cs.find(*interest3);
+ BOOST_CHECK_EQUAL(found3->getName(), "/c/c/1");
+}
+
+BOOST_AUTO_TEST_CASE(ExcludeSelector)
+{
+ Cs cs;
+
+ shared_ptr<Data> data = makeData("/a");
+ cs.insert(*data);
+
+ shared_ptr<Data> data2 = makeData("/b");
+ cs.insert(*data2);
+
+ shared_ptr<Data> data3 = makeData("/c/a");
+ cs.insert(*data3);
+
+ shared_ptr<Data> data4 = makeData("/d");
+ cs.insert(*data4);
+
+ shared_ptr<Data> data5 = makeData("/c/c");
+ cs.insert(*data5);
+
+ shared_ptr<Data> data6 = makeData("/c/f");
+ cs.insert(*data6);
+
+ shared_ptr<Data> data7 = makeData("/c/n");
+ cs.insert(*data7);
+
+ shared_ptr<Interest> interest = make_shared<Interest>("/c");
+ interest->setChildSelector(1);
+ Exclude e;
+ e.excludeOne (Name::Component("n"));
+ interest->setExclude(e);
+
+ const Data* found = cs.find(*interest);
+ BOOST_CHECK_EQUAL(found->getName(), "/c/f");
+
+ shared_ptr<Interest> interest2 = make_shared<Interest>("/c");
+ interest2->setChildSelector(0);
+
+ Exclude e2;
+ e2.excludeOne (Name::Component("a"));
+ interest2->setExclude(e2);
+
+ const Data* found2 = cs.find(*interest2);
+ BOOST_CHECK_EQUAL(found2->getName(), "/c/c");
+
+ shared_ptr<Interest> interest3 = make_shared<Interest>("/c");
+ interest3->setChildSelector(0);
+
+ Exclude e3;
+ e3.excludeOne (Name::Component("c"));
+ interest3->setExclude(e3);
+
+ const Data* found3 = cs.find(*interest3);
+ BOOST_CHECK_EQUAL(found3->getName(), "/c/a");
+}
+
+//
+
+class FindFixture : protected BaseFixture
+{
+protected:
+ void
+ insert(uint32_t id, const Name& name)
+ {
+ shared_ptr<Data> data = makeData(name);
+ data->setFreshnessPeriod(time::milliseconds(99999));
+ data->setContent(reinterpret_cast<const uint8_t*>(&id), sizeof(id));
+
+ m_cs.insert(*data);
+ }
+
+ Interest&
+ startInterest(const Name& name)
+ {
+ m_interest = make_shared<Interest>(name);
+ return *m_interest;
+ }
+
+ uint32_t
+ find()
+ {
+ const Data* found = m_cs.find(*m_interest);
+ if (found == 0) {
+ return 0;
+ }
+ const Block& content = found->getContent();
+ if (content.value_size() != sizeof(uint32_t)) {
+ return 0;
+ }
+ return *reinterpret_cast<const uint32_t*>(content.value());
+ }
+
+protected:
+ Cs m_cs;
+ shared_ptr<Interest> m_interest;
+};
+
+BOOST_FIXTURE_TEST_SUITE(Find, FindFixture)
+
+BOOST_AUTO_TEST_CASE(EmptyDataName)
+{
+ insert(1, "ndn:/");
+
+ startInterest("ndn:/");
+ BOOST_CHECK_EQUAL(find(), 1);
+}
+
+BOOST_AUTO_TEST_CASE(EmptyInterestName)
+{
+ insert(1, "ndn:/A");
+
+ startInterest("ndn:/");
+ BOOST_CHECK_EQUAL(find(), 1);
+}
+
+BOOST_AUTO_TEST_CASE(Leftmost)
+{
+ insert(1, "ndn:/A");
+ insert(2, "ndn:/B/p/1");
+ insert(3, "ndn:/B/p/2");
+ insert(4, "ndn:/B/q/1");
+ insert(5, "ndn:/B/q/2");
+ insert(6, "ndn:/C");
+
+ startInterest("ndn:/B");
+ BOOST_CHECK_EQUAL(find(), 2);
+}
+
+BOOST_AUTO_TEST_CASE(Rightmost)
+{
+ insert(1, "ndn:/A");
+ insert(2, "ndn:/B/p/1");
+ insert(3, "ndn:/B/p/2");
+ insert(4, "ndn:/B/q/1");
+ insert(5, "ndn:/B/q/2");
+ insert(6, "ndn:/C");
+
+ startInterest("ndn:/B")
+ .setChildSelector(1);
+ BOOST_CHECK_EQUAL(find(), 4);
+}
+
+BOOST_AUTO_TEST_CASE(Leftmost_ExactName1)
+{
+ insert(1, "ndn:/");
+ insert(2, "ndn:/A/B");
+ insert(3, "ndn:/A/C");
+ insert(4, "ndn:/A");
+ insert(5, "ndn:/D");
+
+ // Intuitively you would think Data 4 should be between Data 1 and 2,
+ // but Data 4 has full Name ndn:/A/<32-octet hash>.
+ startInterest("ndn:/A");
+ BOOST_CHECK_EQUAL(find(), 2);
+}
+
+BOOST_AUTO_TEST_CASE(Leftmost_ExactName33)
+{
+ insert(1, "ndn:/");
+ insert(2, "ndn:/A");
+ insert(3, "ndn:/A/BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"); // 33 'B's
+ insert(4, "ndn:/A/CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC"); // 33 'C's
+ insert(5, "ndn:/D");
+
+ // Data 2 is returned, because <32-octet hash> is less than Data 3.
+ startInterest("ndn:/A");
+ BOOST_CHECK_EQUAL(find(), 2);
+}
+
+BOOST_AUTO_TEST_CASE(MinSuffixComponents)
+{
+ insert(1, "ndn:/A/1/2/3/4");
+ insert(2, "ndn:/B/1/2/3");
+ insert(3, "ndn:/C/1/2");
+ insert(4, "ndn:/D/1");
+ insert(5, "ndn:/E");
+ insert(6, "ndn:/");
+
+ startInterest("ndn:/")
+ .setChildSelector(1)
+ .setMinSuffixComponents(0);
+ BOOST_CHECK_EQUAL(find(), 6);
+
+ startInterest("ndn:/")
+ .setChildSelector(1)
+ .setMinSuffixComponents(1);
+ BOOST_CHECK_EQUAL(find(), 6);
+
+ startInterest("ndn:/")
+ .setChildSelector(1)
+ .setMinSuffixComponents(2);
+ BOOST_CHECK_EQUAL(find(), 5);
+
+ startInterest("ndn:/")
+ .setChildSelector(1)
+ .setMinSuffixComponents(3);
+ BOOST_CHECK_EQUAL(find(), 4);
+
+ startInterest("ndn:/")
+ .setChildSelector(1)
+ .setMinSuffixComponents(4);
+ BOOST_CHECK_EQUAL(find(), 3);
+
+ startInterest("ndn:/")
+ .setChildSelector(1)
+ .setMinSuffixComponents(5);
+ BOOST_CHECK_EQUAL(find(), 2);
+
+ startInterest("ndn:/")
+ .setChildSelector(1)
+ .setMinSuffixComponents(6);
+ BOOST_CHECK_EQUAL(find(), 1);
+
+ startInterest("ndn:/")
+ .setChildSelector(1)
+ .setMinSuffixComponents(7);
+ BOOST_CHECK_EQUAL(find(), 0);
+}
+
+BOOST_AUTO_TEST_CASE(MaxSuffixComponents)
+{
+ insert(1, "ndn:/");
+ insert(2, "ndn:/A");
+ insert(3, "ndn:/A/B");
+ insert(4, "ndn:/A/B/C");
+ insert(5, "ndn:/A/B/C/D");
+ insert(6, "ndn:/A/B/C/D/E");
+ // Order is 6,5,4,3,2,1, because <32-octet hash> is greater than a 1-octet component.
+
+ startInterest("ndn:/")
+ .setMaxSuffixComponents(0);
+ BOOST_CHECK_EQUAL(find(), 0);
+
+ startInterest("ndn:/")
+ .setMaxSuffixComponents(1);
+ BOOST_CHECK_EQUAL(find(), 1);
+
+ startInterest("ndn:/")
+ .setMaxSuffixComponents(2);
+ BOOST_CHECK_EQUAL(find(), 2);
+
+ startInterest("ndn:/")
+ .setMaxSuffixComponents(3);
+ BOOST_CHECK_EQUAL(find(), 3);
+
+ startInterest("ndn:/")
+ .setMaxSuffixComponents(4);
+ BOOST_CHECK_EQUAL(find(), 4);
+
+ startInterest("ndn:/")
+ .setMaxSuffixComponents(5);
+ BOOST_CHECK_EQUAL(find(), 5);
+
+ startInterest("ndn:/")
+ .setMaxSuffixComponents(6);
+ BOOST_CHECK_EQUAL(find(), 6);
+}
+
+BOOST_AUTO_TEST_CASE(DigestOrder)
+{
+ insert(1, "ndn:/A");
+ insert(2, "ndn:/A");
+ // We don't know which comes first, but there must be some order
+
+ startInterest("ndn:/A")
+ .setChildSelector(0);
+ uint32_t leftmost = find();
+
+ startInterest("ndn:/A")
+ .setChildSelector(1);
+ uint32_t rightmost = find();
+
+ BOOST_CHECK_NE(leftmost, rightmost);
+}
+
+BOOST_AUTO_TEST_CASE(DigestExclude)
+{
+ insert(1, "ndn:/A/B");
+ insert(2, "ndn:/A");
+ insert(3, "ndn:/A/CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC"); // 33 'C's
+
+ startInterest("ndn:/A")
+ .setExclude(Exclude().excludeBefore(name::Component(reinterpret_cast<const uint8_t*>(
+ "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"
+ "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"), 31))); // 31 0xFF's
+ BOOST_CHECK_EQUAL(find(), 2);
+
+ startInterest("ndn:/A")
+ .setChildSelector(1)
+ .setExclude(Exclude().excludeAfter(name::Component(reinterpret_cast<const uint8_t*>(
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00"), 33))); // 33 0x00's
+ BOOST_CHECK_EQUAL(find(), 2);
+}
+
+BOOST_AUTO_TEST_CASE(ExactName32)
+{
+ insert(1, "ndn:/A/BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"); // 32 'B's
+ insert(2, "ndn:/A/CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC"); // 32 'C's
+
+ startInterest("ndn:/A/BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB");
+ BOOST_CHECK_EQUAL(find(), 1);
+}
+
+BOOST_AUTO_TEST_CASE(MinSuffixComponents32)
+{
+ insert(1, "ndn:/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/A/1/2/3/4"); // 32 'x's
+ insert(2, "ndn:/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/B/1/2/3");
+ insert(3, "ndn:/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/C/1/2");
+ insert(4, "ndn:/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/D/1");
+ insert(5, "ndn:/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/E");
+ insert(6, "ndn:/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
+
+ startInterest("ndn:/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx")
+ .setChildSelector(1)
+ .setMinSuffixComponents(0);
+ BOOST_CHECK_EQUAL(find(), 6);
+
+ startInterest("ndn:/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx")
+ .setChildSelector(1)
+ .setMinSuffixComponents(1);
+ BOOST_CHECK_EQUAL(find(), 6);
+
+ startInterest("ndn:/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx")
+ .setChildSelector(1)
+ .setMinSuffixComponents(2);
+ BOOST_CHECK_EQUAL(find(), 5);
+
+ startInterest("ndn:/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx")
+ .setChildSelector(1)
+ .setMinSuffixComponents(3);
+ BOOST_CHECK_EQUAL(find(), 4);
+
+ startInterest("ndn:/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx")
+ .setChildSelector(1)
+ .setMinSuffixComponents(4);
+ BOOST_CHECK_EQUAL(find(), 3);
+
+ startInterest("ndn:/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx")
+ .setChildSelector(1)
+ .setMinSuffixComponents(5);
+ BOOST_CHECK_EQUAL(find(), 2);
+
+ startInterest("ndn:/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx")
+ .setChildSelector(1)
+ .setMinSuffixComponents(6);
+ BOOST_CHECK_EQUAL(find(), 1);
+
+ startInterest("ndn:/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx")
+ .setChildSelector(1)
+ .setMinSuffixComponents(7);
+ BOOST_CHECK_EQUAL(find(), 0);
+}
+
+BOOST_AUTO_TEST_CASE(MaxSuffixComponents32)
+{
+ insert(1, "ndn:/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/"); // 32 'x's
+ insert(2, "ndn:/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/A");
+ insert(3, "ndn:/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/A/B");
+ insert(4, "ndn:/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/A/B/C");
+ insert(5, "ndn:/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/A/B/C/D");
+ insert(6, "ndn:/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/A/B/C/D/E");
+ // Order is 6,5,4,3,2,1, because <32-octet hash> is greater than a 1-octet component.
+
+ startInterest("ndn:/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx")
+ .setMaxSuffixComponents(0);
+ BOOST_CHECK_EQUAL(find(), 0);
+
+ startInterest("ndn:/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx")
+ .setMaxSuffixComponents(1);
+ BOOST_CHECK_EQUAL(find(), 1);
+
+ startInterest("ndn:/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx")
+ .setMaxSuffixComponents(2);
+ BOOST_CHECK_EQUAL(find(), 2);
+
+ startInterest("ndn:/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx")
+ .setMaxSuffixComponents(3);
+ BOOST_CHECK_EQUAL(find(), 3);
+
+ startInterest("ndn:/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx")
+ .setMaxSuffixComponents(4);
+ BOOST_CHECK_EQUAL(find(), 4);
+
+ startInterest("ndn:/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx")
+ .setMaxSuffixComponents(5);
+ BOOST_CHECK_EQUAL(find(), 5);
+
+ startInterest("ndn:/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx")
+ .setMaxSuffixComponents(6);
+ BOOST_CHECK_EQUAL(find(), 6);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // Find
+
+BOOST_AUTO_TEST_SUITE_END() // TableCs
+
+} // namespace tests
+} // namespace nfd
diff --git a/tests/daemon/table/fib.cpp b/tests/daemon/table/fib.cpp
new file mode 100644
index 0000000..bdf2fca
--- /dev/null
+++ b/tests/daemon/table/fib.cpp
@@ -0,0 +1,381 @@
+/* -*- 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 "table/fib.hpp"
+#include "tests/daemon/face/dummy-face.hpp"
+
+#include "tests/test-common.hpp"
+
+namespace nfd {
+namespace tests {
+
+BOOST_FIXTURE_TEST_SUITE(TableFib, BaseFixture)
+
+BOOST_AUTO_TEST_CASE(Entry)
+{
+ Name prefix("ndn:/pxWhfFza");
+ shared_ptr<Face> face1 = make_shared<DummyFace>();
+ shared_ptr<Face> face2 = make_shared<DummyFace>();
+
+ fib::Entry entry(prefix);
+ BOOST_CHECK_EQUAL(entry.getPrefix(), prefix);
+
+ const fib::NextHopList& nexthops1 = entry.getNextHops();
+ // []
+ BOOST_CHECK_EQUAL(nexthops1.size(), 0);
+
+ entry.addNextHop(face1, 20);
+ const fib::NextHopList& nexthops2 = entry.getNextHops();
+ // [(face1,20)]
+ BOOST_CHECK_EQUAL(nexthops2.size(), 1);
+ BOOST_CHECK_EQUAL(nexthops2.begin()->getFace(), face1);
+ BOOST_CHECK_EQUAL(nexthops2.begin()->getCost(), 20);
+
+ entry.addNextHop(face1, 30);
+ const fib::NextHopList& nexthops3 = entry.getNextHops();
+ // [(face1,30)]
+ BOOST_CHECK_EQUAL(nexthops3.size(), 1);
+ BOOST_CHECK_EQUAL(nexthops3.begin()->getFace(), face1);
+ BOOST_CHECK_EQUAL(nexthops3.begin()->getCost(), 30);
+
+ entry.addNextHop(face2, 40);
+ const fib::NextHopList& nexthops4 = entry.getNextHops();
+ // [(face1,30), (face2,40)]
+ BOOST_CHECK_EQUAL(nexthops4.size(), 2);
+ int i = -1;
+ for (fib::NextHopList::const_iterator it = nexthops4.begin();
+ it != nexthops4.end(); ++it) {
+ ++i;
+ switch (i) {
+ case 0 :
+ BOOST_CHECK_EQUAL(it->getFace(), face1);
+ BOOST_CHECK_EQUAL(it->getCost(), 30);
+ break;
+ case 1 :
+ BOOST_CHECK_EQUAL(it->getFace(), face2);
+ BOOST_CHECK_EQUAL(it->getCost(), 40);
+ break;
+ }
+ }
+
+ entry.addNextHop(face2, 10);
+ const fib::NextHopList& nexthops5 = entry.getNextHops();
+ // [(face2,10), (face1,30)]
+ BOOST_CHECK_EQUAL(nexthops5.size(), 2);
+ i = -1;
+ for (fib::NextHopList::const_iterator it = nexthops5.begin();
+ it != nexthops5.end(); ++it) {
+ ++i;
+ switch (i) {
+ case 0 :
+ BOOST_CHECK_EQUAL(it->getFace(), face2);
+ BOOST_CHECK_EQUAL(it->getCost(), 10);
+ break;
+ case 1 :
+ BOOST_CHECK_EQUAL(it->getFace(), face1);
+ BOOST_CHECK_EQUAL(it->getCost(), 30);
+ break;
+ }
+ }
+
+ entry.removeNextHop(face1);
+ const fib::NextHopList& nexthops6 = entry.getNextHops();
+ // [(face2,10)]
+ BOOST_CHECK_EQUAL(nexthops6.size(), 1);
+ BOOST_CHECK_EQUAL(nexthops6.begin()->getFace(), face2);
+ BOOST_CHECK_EQUAL(nexthops6.begin()->getCost(), 10);
+
+ entry.removeNextHop(face1);
+ const fib::NextHopList& nexthops7 = entry.getNextHops();
+ // [(face2,10)]
+ BOOST_CHECK_EQUAL(nexthops7.size(), 1);
+ BOOST_CHECK_EQUAL(nexthops7.begin()->getFace(), face2);
+ BOOST_CHECK_EQUAL(nexthops7.begin()->getCost(), 10);
+
+ entry.removeNextHop(face2);
+ const fib::NextHopList& nexthops8 = entry.getNextHops();
+ // []
+ BOOST_CHECK_EQUAL(nexthops8.size(), 0);
+
+ entry.removeNextHop(face2);
+ const fib::NextHopList& nexthops9 = entry.getNextHops();
+ // []
+ BOOST_CHECK_EQUAL(nexthops9.size(), 0);
+}
+
+BOOST_AUTO_TEST_CASE(Insert_LongestPrefixMatch)
+{
+ Name nameEmpty;
+ Name nameA ("ndn:/A");
+ Name nameAB ("ndn:/A/B");
+ Name nameABC ("ndn:/A/B/C");
+ Name nameABCD("ndn:/A/B/C/D");
+ Name nameE ("ndn:/E");
+
+ std::pair<shared_ptr<fib::Entry>, bool> insertRes;
+ shared_ptr<fib::Entry> entry;
+
+ NameTree nameTree;
+ Fib fib(nameTree);
+ // []
+ BOOST_CHECK_EQUAL(fib.size(), 0);
+
+ entry = fib.findLongestPrefixMatch(nameA);
+ BOOST_REQUIRE(static_cast<bool>(entry)); // the empty entry
+
+ insertRes = fib.insert(nameEmpty);
+ BOOST_CHECK_EQUAL(insertRes.second, true);
+ BOOST_CHECK_EQUAL(insertRes.first->getPrefix(), nameEmpty);
+ // ['/']
+ BOOST_CHECK_EQUAL(fib.size(), 1);
+
+ entry = fib.findLongestPrefixMatch(nameA);
+ BOOST_REQUIRE(static_cast<bool>(entry));
+ BOOST_CHECK_EQUAL(entry->getPrefix(), nameEmpty);
+
+ insertRes = fib.insert(nameA);
+ BOOST_CHECK_EQUAL(insertRes.second, true);
+ BOOST_CHECK_EQUAL(insertRes.first->getPrefix(), nameA);
+ // ['/', '/A']
+ BOOST_CHECK_EQUAL(fib.size(), 2);
+
+ insertRes = fib.insert(nameA);
+ BOOST_CHECK_EQUAL(insertRes.second, false);
+ BOOST_CHECK_EQUAL(insertRes.first->getPrefix(), nameA);
+ // ['/', '/A']
+ BOOST_CHECK_EQUAL(fib.size(), 2);
+
+ entry = fib.findLongestPrefixMatch(nameA);
+ BOOST_REQUIRE(static_cast<bool>(entry));
+ BOOST_CHECK_EQUAL(entry->getPrefix(), nameA);
+
+ entry = fib.findLongestPrefixMatch(nameABCD);
+ BOOST_REQUIRE(static_cast<bool>(entry));
+ BOOST_CHECK_EQUAL(entry->getPrefix(), nameA);
+
+ insertRes = fib.insert(nameABC);
+ BOOST_CHECK_EQUAL(insertRes.second, true);
+ BOOST_CHECK_EQUAL(insertRes.first->getPrefix(), nameABC);
+ // ['/', '/A', '/A/B/C']
+ BOOST_CHECK_EQUAL(fib.size(), 3);
+
+ entry = fib.findLongestPrefixMatch(nameA);
+ BOOST_REQUIRE(static_cast<bool>(entry));
+ BOOST_CHECK_EQUAL(entry->getPrefix(), nameA);
+
+ entry = fib.findLongestPrefixMatch(nameAB);
+ BOOST_REQUIRE(static_cast<bool>(entry));
+ BOOST_CHECK_EQUAL(entry->getPrefix(), nameA);
+
+ entry = fib.findLongestPrefixMatch(nameABCD);
+ BOOST_REQUIRE(static_cast<bool>(entry));
+ BOOST_CHECK_EQUAL(entry->getPrefix(), nameABC);
+
+ entry = fib.findLongestPrefixMatch(nameE);
+ BOOST_REQUIRE(static_cast<bool>(entry));
+ BOOST_CHECK_EQUAL(entry->getPrefix(), nameEmpty);
+}
+
+BOOST_AUTO_TEST_CASE(RemoveNextHopFromAllEntries)
+{
+ shared_ptr<Face> face1 = make_shared<DummyFace>();
+ shared_ptr<Face> face2 = make_shared<DummyFace>();
+ Name nameEmpty("ndn:/");
+ Name nameA("ndn:/A");
+ Name nameB("ndn:/B");
+
+ std::pair<shared_ptr<fib::Entry>, bool> insertRes;
+ shared_ptr<fib::Entry> entry;
+
+ NameTree nameTree;
+ Fib fib(nameTree);
+ // {}
+
+ insertRes = fib.insert(nameA);
+ insertRes.first->addNextHop(face1, 0);
+ insertRes.first->addNextHop(face2, 0);
+ // {'/A':[1,2]}
+
+ insertRes = fib.insert(nameB);
+ insertRes.first->addNextHop(face1, 0);
+ // {'/A':[1,2], '/B':[1]}
+ BOOST_CHECK_EQUAL(fib.size(), 2);
+
+ fib.removeNextHopFromAllEntries(face1);
+ // {'/A':[2]}
+ BOOST_CHECK_EQUAL(fib.size(), 1);
+
+ entry = fib.findLongestPrefixMatch(nameA);
+ BOOST_CHECK_EQUAL(entry->getPrefix(), nameA);
+ const fib::NextHopList& nexthopsA = entry->getNextHops();
+ BOOST_CHECK_EQUAL(nexthopsA.size(), 1);
+ BOOST_CHECK_EQUAL(nexthopsA.begin()->getFace(), face2);
+
+ entry = fib.findLongestPrefixMatch(nameB);
+ BOOST_CHECK_EQUAL(entry->getPrefix(), nameEmpty);
+}
+
+void
+validateFindExactMatch(const Fib& fib, const Name& target)
+{
+ shared_ptr<fib::Entry> entry = fib.findExactMatch(target);
+ if (static_cast<bool>(entry))
+ {
+ BOOST_CHECK_EQUAL(entry->getPrefix(), target);
+ }
+ else
+ {
+ BOOST_FAIL("No entry found for " << target);
+ }
+}
+
+void
+validateNoExactMatch(const Fib& fib, const Name& target)
+{
+ shared_ptr<fib::Entry> entry = fib.findExactMatch(target);
+ if (static_cast<bool>(entry))
+ {
+ BOOST_FAIL("Found unexpected entry for " << target);
+ }
+}
+
+BOOST_AUTO_TEST_CASE(FindExactMatch)
+{
+ NameTree nameTree;
+ Fib fib(nameTree);
+ fib.insert("/A");
+ fib.insert("/A/B");
+ fib.insert("/A/B/C");
+
+ validateFindExactMatch(fib, "/A");
+ validateFindExactMatch(fib, "/A/B");
+ validateFindExactMatch(fib, "/A/B/C");
+ validateNoExactMatch(fib, "/");
+
+ validateNoExactMatch(fib, "/does/not/exist");
+
+ NameTree gapNameTree;
+ Fib gapFib(nameTree);
+ fib.insert("/X");
+ fib.insert("/X/Y/Z");
+
+ validateNoExactMatch(gapFib, "/X/Y");
+
+ NameTree emptyNameTree;
+ Fib emptyFib(emptyNameTree);
+ validateNoExactMatch(emptyFib, "/nothing/here");
+}
+
+void
+validateRemove(Fib& fib, const Name& target)
+{
+ fib.erase(target);
+
+ shared_ptr<fib::Entry> entry = fib.findExactMatch(target);
+ if (static_cast<bool>(entry))
+ {
+ BOOST_FAIL("Found \"removed\" entry for " << target);
+ }
+}
+
+BOOST_AUTO_TEST_CASE(Remove)
+{
+ NameTree emptyNameTree;
+ Fib emptyFib(emptyNameTree);
+
+ emptyFib.erase("/does/not/exist"); // crash test
+
+ validateRemove(emptyFib, "/");
+
+ emptyFib.erase("/still/does/not/exist"); // crash test
+
+ NameTree nameTree;
+ Fib fib(nameTree);
+ fib.insert("/");
+ fib.insert("/A");
+ fib.insert("/A/B");
+ fib.insert("/A/B/C");
+
+ // check if we remove the right thing and leave
+ // everything else alone
+
+ validateRemove(fib, "/A/B");
+ validateFindExactMatch(fib, "/A");
+ validateFindExactMatch(fib, "/A/B/C");
+ validateFindExactMatch(fib, "/");
+
+ validateRemove(fib, "/A/B/C");
+ validateFindExactMatch(fib, "/A");
+ validateFindExactMatch(fib, "/");
+
+ validateRemove(fib, "/A");
+ validateFindExactMatch(fib, "/");
+
+ validateRemove(fib, "/");
+ validateNoExactMatch(fib, "/");
+
+ NameTree gapNameTree;
+ Fib gapFib(gapNameTree);
+ gapFib.insert("/X");
+ gapFib.insert("/X/Y/Z");
+
+ gapFib.erase("/X/Y"); //should do nothing
+ validateFindExactMatch(gapFib, "/X");
+ validateFindExactMatch(gapFib, "/X/Y/Z");
+}
+
+BOOST_AUTO_TEST_CASE(Iterator)
+{
+ NameTree nameTree;
+ Fib fib(nameTree);
+ Name nameA("/A");
+ Name nameAB("/A/B");
+ Name nameABC("/A/B/C");
+ Name nameRoot("/");
+
+ fib.insert(nameA);
+ fib.insert(nameAB);
+ fib.insert(nameABC);
+ fib.insert(nameRoot);
+
+ std::set<Name> expected;
+ expected.insert(nameA);
+ expected.insert(nameAB);
+ expected.insert(nameABC);
+ expected.insert(nameRoot);
+
+ for (Fib::const_iterator it = fib.begin(); it != fib.end(); it++)
+ {
+ bool isInSet = expected.find(it->getPrefix()) != expected.end();
+ BOOST_CHECK(isInSet);
+ expected.erase(it->getPrefix());
+ }
+
+ BOOST_CHECK_EQUAL(expected.size(), 0);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+} // namespace nfd
diff --git a/tests/daemon/table/measurements-accessor.cpp b/tests/daemon/table/measurements-accessor.cpp
new file mode 100644
index 0000000..6dc8ff3
--- /dev/null
+++ b/tests/daemon/table/measurements-accessor.cpp
@@ -0,0 +1,110 @@
+/* -*- 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 "table/measurements-accessor.hpp"
+#include "fw/strategy.hpp"
+
+#include "tests/test-common.hpp"
+
+namespace nfd {
+namespace tests {
+
+BOOST_FIXTURE_TEST_SUITE(TableMeasurementsAccessor, BaseFixture)
+
+class MeasurementsAccessorTestStrategy : public fw::Strategy
+{
+public:
+ MeasurementsAccessorTestStrategy(Forwarder& forwarder, const Name& name)
+ : Strategy(forwarder, name)
+ {
+ }
+
+ virtual
+ ~MeasurementsAccessorTestStrategy()
+
+ {
+ }
+
+ virtual void
+ afterReceiveInterest(const Face& inFace,
+ const Interest& interest,
+ shared_ptr<fib::Entry> fibEntry,
+ shared_ptr<pit::Entry> pitEntry)
+ {
+ BOOST_ASSERT(false);
+ }
+
+public: // accessors
+ MeasurementsAccessor&
+ getMeasurements_accessor()
+ {
+ return this->getMeasurements();
+ }
+};
+
+BOOST_AUTO_TEST_CASE(Access)
+{
+ Forwarder forwarder;
+
+ shared_ptr<MeasurementsAccessorTestStrategy> strategy1 =
+ make_shared<MeasurementsAccessorTestStrategy>(boost::ref(forwarder), "ndn:/strategy1");
+ shared_ptr<MeasurementsAccessorTestStrategy> strategy2 =
+ make_shared<MeasurementsAccessorTestStrategy>(boost::ref(forwarder), "ndn:/strategy2");
+
+ Name nameRoot("ndn:/");
+ Name nameA ("ndn:/A");
+ Name nameAB ("ndn:/A/B");
+ Name nameABC ("ndn:/A/B/C");
+ Name nameAD ("ndn:/A/D");
+
+ StrategyChoice& strategyChoice = forwarder.getStrategyChoice();
+ strategyChoice.install(strategy1);
+ strategyChoice.install(strategy2);
+ strategyChoice.insert(nameRoot, strategy1->getName());
+ strategyChoice.insert(nameA , strategy2->getName());
+ strategyChoice.insert(nameAB , strategy1->getName());
+
+ MeasurementsAccessor& accessor1 = strategy1->getMeasurements_accessor();
+ MeasurementsAccessor& accessor2 = strategy2->getMeasurements_accessor();
+
+ BOOST_CHECK_EQUAL(static_cast<bool>(accessor1.get(nameRoot)), true);
+ BOOST_CHECK_EQUAL(static_cast<bool>(accessor1.get(nameA )), false);
+ BOOST_CHECK_EQUAL(static_cast<bool>(accessor1.get(nameAB )), true);
+ BOOST_CHECK_EQUAL(static_cast<bool>(accessor1.get(nameABC )), true);
+ BOOST_CHECK_EQUAL(static_cast<bool>(accessor1.get(nameAD )), false);
+
+ shared_ptr<measurements::Entry> entryRoot = forwarder.getMeasurements().get(nameRoot);
+ BOOST_CHECK_NO_THROW(accessor1.getParent(entryRoot));
+
+ BOOST_CHECK_EQUAL(static_cast<bool>(accessor2.get(nameRoot)), false);
+ BOOST_CHECK_EQUAL(static_cast<bool>(accessor2.get(nameA )), true);
+ BOOST_CHECK_EQUAL(static_cast<bool>(accessor2.get(nameAB )), false);
+ BOOST_CHECK_EQUAL(static_cast<bool>(accessor2.get(nameABC )), false);
+ BOOST_CHECK_EQUAL(static_cast<bool>(accessor2.get(nameAD )), true);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+} // namespace nfd
diff --git a/tests/daemon/table/measurements.cpp b/tests/daemon/table/measurements.cpp
new file mode 100644
index 0000000..98676b5
--- /dev/null
+++ b/tests/daemon/table/measurements.cpp
@@ -0,0 +1,61 @@
+/* -*- 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 "table/measurements.hpp"
+
+#include "tests/test-common.hpp"
+
+namespace nfd {
+namespace tests {
+
+BOOST_FIXTURE_TEST_SUITE(TableMeasurements, BaseFixture)
+
+BOOST_AUTO_TEST_CASE(Get_Parent)
+{
+ NameTree nameTree;
+ Measurements measurements(nameTree);
+
+ Name name0;
+ Name nameA ("ndn:/A");
+ Name nameAB("ndn:/A/B");
+
+ shared_ptr<measurements::Entry> entryAB = measurements.get(nameAB);
+ BOOST_REQUIRE(static_cast<bool>(entryAB));
+ BOOST_CHECK_EQUAL(entryAB->getName(), nameAB);
+
+ shared_ptr<measurements::Entry> entry0 = measurements.get(name0);
+ BOOST_REQUIRE(static_cast<bool>(entry0));
+
+ shared_ptr<measurements::Entry> entryA = measurements.getParent(entryAB);
+ BOOST_REQUIRE(static_cast<bool>(entryA));
+ BOOST_CHECK_EQUAL(entryA->getName(), nameA);
+
+ shared_ptr<measurements::Entry> entry0c = measurements.getParent(entryA);
+ BOOST_CHECK_EQUAL(entry0, entry0c);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+} // namespace nfd
diff --git a/tests/daemon/table/name-tree.cpp b/tests/daemon/table/name-tree.cpp
new file mode 100644
index 0000000..e5e9089
--- /dev/null
+++ b/tests/daemon/table/name-tree.cpp
@@ -0,0 +1,801 @@
+/* -*- 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 "table/name-tree.hpp"
+#include "tests/test-common.hpp"
+
+namespace nfd {
+namespace tests {
+
+using name_tree::Entry;
+
+BOOST_FIXTURE_TEST_SUITE(TableNameTree, BaseFixture)
+
+BOOST_AUTO_TEST_CASE(Hash)
+{
+ Name root("/");
+ root.wireEncode();
+ size_t hashValue = name_tree::computeHash(root);
+ BOOST_CHECK_EQUAL(hashValue, static_cast<size_t>(0));
+
+ Name prefix("/nohello/world/ndn/research");
+ prefix.wireEncode();
+ std::vector<size_t> hashSet = name_tree::computeHashSet(prefix);
+ BOOST_CHECK_EQUAL(hashSet.size(), prefix.size() + 1);
+}
+
+BOOST_AUTO_TEST_CASE(Entry)
+{
+ Name prefix("ndn:/named-data/research/abc/def/ghi");
+
+ shared_ptr<name_tree::Entry> npe = make_shared<name_tree::Entry>(prefix);
+ BOOST_CHECK_EQUAL(npe->getPrefix(), prefix);
+
+ // examine all the get methods
+
+ size_t hash = npe->getHash();
+ BOOST_CHECK_EQUAL(hash, static_cast<size_t>(0));
+
+ shared_ptr<name_tree::Entry> parent = npe->getParent();
+ BOOST_CHECK(!static_cast<bool>(parent));
+
+ std::vector<shared_ptr<name_tree::Entry> >& childList = npe->getChildren();
+ BOOST_CHECK_EQUAL(childList.size(), static_cast<size_t>(0));
+
+ shared_ptr<fib::Entry> fib = npe->getFibEntry();
+ BOOST_CHECK(!static_cast<bool>(fib));
+
+ const std::vector< shared_ptr<pit::Entry> >& pitList = npe->getPitEntries();
+ BOOST_CHECK_EQUAL(pitList.size(), static_cast<size_t>(0));
+
+ // examine all the set method
+
+ npe->setHash(static_cast<size_t>(12345));
+ BOOST_CHECK_EQUAL(npe->getHash(), static_cast<size_t>(12345));
+
+ Name parentName("ndn:/named-data/research/abc/def");
+ parent = make_shared<name_tree::Entry>(parentName);
+ npe->setParent(parent);
+ BOOST_CHECK_EQUAL(npe->getParent(), parent);
+
+ // Insert FIB
+
+ shared_ptr<fib::Entry> fibEntry(new fib::Entry(prefix));
+ shared_ptr<fib::Entry> fibEntryParent(new fib::Entry(parentName));
+
+ npe->setFibEntry(fibEntry);
+ BOOST_CHECK_EQUAL(npe->getFibEntry(), fibEntry);
+
+ npe->setFibEntry(shared_ptr<fib::Entry>());
+ BOOST_CHECK(!static_cast<bool>(npe->getFibEntry()));
+
+ // Insert a PIT
+
+ shared_ptr<pit::Entry> PitEntry(make_shared<pit::Entry>(prefix));
+ shared_ptr<pit::Entry> PitEntry2(make_shared<pit::Entry>(parentName));
+
+ npe->insertPitEntry(PitEntry);
+ BOOST_CHECK_EQUAL(npe->getPitEntries().size(), 1);
+
+ npe->insertPitEntry(PitEntry2);
+ BOOST_CHECK_EQUAL(npe->getPitEntries().size(), 2);
+
+ npe->erasePitEntry(PitEntry);
+ BOOST_CHECK_EQUAL(npe->getPitEntries().size(), 1);
+
+ npe->erasePitEntry(PitEntry2);
+ BOOST_CHECK_EQUAL(npe->getPitEntries().size(), 0);
+}
+
+BOOST_AUTO_TEST_CASE(NameTreeBasic)
+{
+ size_t nBuckets = 16;
+ NameTree nt(nBuckets);
+
+ BOOST_CHECK_EQUAL(nt.size(), 0);
+ BOOST_CHECK_EQUAL(nt.getNBuckets(), nBuckets);
+
+ Name nameABC("ndn:/a/b/c");
+ shared_ptr<name_tree::Entry> npeABC = nt.lookup(nameABC);
+ BOOST_CHECK_EQUAL(nt.size(), 4);
+
+ Name nameABD("/a/b/d");
+ shared_ptr<name_tree::Entry> npeABD = nt.lookup(nameABD);
+ BOOST_CHECK_EQUAL(nt.size(), 5);
+
+ Name nameAE("/a/e/");
+ shared_ptr<name_tree::Entry> npeAE = nt.lookup(nameAE);
+ BOOST_CHECK_EQUAL(nt.size(), 6);
+
+ Name nameF("/f");
+ shared_ptr<name_tree::Entry> npeF = nt.lookup(nameF);
+ BOOST_CHECK_EQUAL(nt.size(), 7);
+
+ // validate lookup() and findExactMatch()
+
+ Name nameAB ("/a/b");
+ BOOST_CHECK_EQUAL(npeABC->getParent(), nt.findExactMatch(nameAB));
+ BOOST_CHECK_EQUAL(npeABD->getParent(), nt.findExactMatch(nameAB));
+
+ Name nameA ("/a");
+ BOOST_CHECK_EQUAL(npeAE->getParent(), nt.findExactMatch(nameA));
+
+ Name nameRoot ("/");
+ BOOST_CHECK_EQUAL(npeF->getParent(), nt.findExactMatch(nameRoot));
+ BOOST_CHECK_EQUAL(nt.size(), 7);
+
+ Name name0("/does/not/exist");
+ shared_ptr<name_tree::Entry> npe0 = nt.findExactMatch(name0);
+ BOOST_CHECK(!static_cast<bool>(npe0));
+
+
+ // Longest Prefix Matching
+ shared_ptr<name_tree::Entry> temp;
+ Name nameABCLPM("/a/b/c/def/asdf/nlf");
+ temp = nt.findLongestPrefixMatch(nameABCLPM);
+ BOOST_CHECK_EQUAL(temp, nt.findExactMatch(nameABC));
+
+ Name nameABDLPM("/a/b/d/def/asdf/nlf");
+ temp = nt.findLongestPrefixMatch(nameABDLPM);
+ BOOST_CHECK_EQUAL(temp, nt.findExactMatch(nameABD));
+
+ Name nameABLPM("/a/b/hello/world");
+ temp = nt.findLongestPrefixMatch(nameABLPM);
+ BOOST_CHECK_EQUAL(temp, nt.findExactMatch(nameAB));
+
+ Name nameAELPM("/a/e/hello/world");
+ temp = nt.findLongestPrefixMatch(nameAELPM);
+ BOOST_CHECK_EQUAL(temp, nt.findExactMatch(nameAE));
+
+ Name nameALPM("/a/hello/world");
+ temp = nt.findLongestPrefixMatch(nameALPM);
+ BOOST_CHECK_EQUAL(temp, nt.findExactMatch(nameA));
+
+ Name nameFLPM("/f/hello/world");
+ temp = nt.findLongestPrefixMatch(nameFLPM);
+ BOOST_CHECK_EQUAL(temp, nt.findExactMatch(nameF));
+
+ Name nameRootLPM("/does_not_exist");
+ temp = nt.findLongestPrefixMatch(nameRootLPM);
+ BOOST_CHECK_EQUAL(temp, nt.findExactMatch(nameRoot));
+
+ bool eraseResult = false;
+ temp = nt.findExactMatch(nameABC);
+ if (static_cast<bool>(temp))
+ eraseResult = nt.
+ eraseEntryIfEmpty(temp);
+ BOOST_CHECK_EQUAL(nt.size(), 6);
+ BOOST_CHECK(!static_cast<bool>(nt.findExactMatch(nameABC)));
+ BOOST_CHECK_EQUAL(eraseResult, true);
+
+ eraseResult = false;
+ temp = nt.findExactMatch(nameABCLPM);
+ if (static_cast<bool>(temp))
+ eraseResult = nt.
+ eraseEntryIfEmpty(temp);
+ BOOST_CHECK(!static_cast<bool>(temp));
+ BOOST_CHECK_EQUAL(nt.size(), 6);
+ BOOST_CHECK_EQUAL(eraseResult, false);
+
+ // nt.dump(std::cout);
+
+ nt.lookup(nameABC);
+ BOOST_CHECK_EQUAL(nt.size(), 7);
+
+ eraseResult = false;
+ temp = nt.findExactMatch(nameABC);
+ if (static_cast<bool>(temp))
+ eraseResult = nt.
+ eraseEntryIfEmpty(temp);
+ BOOST_CHECK_EQUAL(nt.size(), 6);
+ BOOST_CHECK_EQUAL(eraseResult, true);
+ BOOST_CHECK(!static_cast<bool>(nt.findExactMatch(nameABC)));
+
+ BOOST_CHECK_EQUAL(nt.getNBuckets(), 16);
+
+ // should resize now
+ Name nameABCD("a/b/c/d");
+ nt.lookup(nameABCD);
+ Name nameABCDE("a/b/c/d/e");
+ nt.lookup(nameABCDE);
+ BOOST_CHECK_EQUAL(nt.size(), 9);
+ BOOST_CHECK_EQUAL(nt.getNBuckets(), 32);
+
+ // try to erase /a/b/c, should return false
+ temp = nt.findExactMatch(nameABC);
+ BOOST_CHECK_EQUAL(temp->getPrefix(), nameABC);
+ eraseResult = nt.
+ eraseEntryIfEmpty(temp);
+ BOOST_CHECK_EQUAL(eraseResult, false);
+ temp = nt.findExactMatch(nameABC);
+ BOOST_CHECK_EQUAL(temp->getPrefix(), nameABC);
+
+ temp = nt.findExactMatch(nameABD);
+ if (static_cast<bool>(temp))
+ nt.
+ eraseEntryIfEmpty(temp);
+ BOOST_CHECK_EQUAL(nt.size(), 8);
+}
+
+BOOST_AUTO_TEST_CASE(NameTreeIteratorFullEnumerate)
+{
+ size_t nBuckets = 1024;
+ NameTree nt(nBuckets);
+
+ BOOST_CHECK_EQUAL(nt.size(), 0);
+ BOOST_CHECK_EQUAL(nt.getNBuckets(), nBuckets);
+
+ Name nameABC("ndn:/a/b/c");
+ shared_ptr<name_tree::Entry> npeABC = nt.lookup(nameABC);
+ BOOST_CHECK_EQUAL(nt.size(), 4);
+
+ Name nameABD("/a/b/d");
+ shared_ptr<name_tree::Entry> npeABD = nt.lookup(nameABD);
+ BOOST_CHECK_EQUAL(nt.size(), 5);
+
+ Name nameAE("/a/e/");
+ shared_ptr<name_tree::Entry> npeAE = nt.lookup(nameAE);
+ BOOST_CHECK_EQUAL(nt.size(), 6);
+
+ Name nameF("/f");
+ shared_ptr<name_tree::Entry> npeF = nt.lookup(nameF);
+ BOOST_CHECK_EQUAL(nt.size(), 7);
+
+ Name nameRoot("/");
+ shared_ptr<name_tree::Entry> npeRoot = nt.lookup(nameRoot);
+ BOOST_CHECK_EQUAL(nt.size(), 7);
+
+ Name nameA("/a");
+ Name nameAB("/a/b");
+
+ bool hasRoot = false;
+ bool hasA = false;
+ bool hasAB = false;
+ bool hasABC = false;
+ bool hasABD = false;
+ bool hasAE = false;
+ bool hasF = false;
+
+ int counter = 0;
+ NameTree::const_iterator it = nt.fullEnumerate();
+
+ for(; it != nt.end(); it++)
+ {
+ counter++;
+
+ if (it->getPrefix() == nameRoot)
+ hasRoot = true;
+ if (it->getPrefix() == nameA)
+ hasA = true;
+ if (it->getPrefix() == nameAB)
+ hasAB = true;
+ if (it->getPrefix() == nameABC)
+ hasABC = true;
+ if (it->getPrefix() == nameABD)
+ hasABD = true;
+ if (it->getPrefix() == nameAE)
+ hasAE = true;
+ if (it->getPrefix() == nameF)
+ hasF = true;
+ }
+
+ BOOST_CHECK_EQUAL(hasRoot , true);
+ BOOST_CHECK_EQUAL(hasA , true);
+ BOOST_CHECK_EQUAL(hasAB , true);
+ BOOST_CHECK_EQUAL(hasABC , true);
+ BOOST_CHECK_EQUAL(hasABD , true);
+ BOOST_CHECK_EQUAL(hasAE , true);
+ BOOST_CHECK_EQUAL(hasF , true);
+
+ BOOST_CHECK_EQUAL(counter , 7);
+}
+
+// Predicates for testing the partial enumerate function
+
+static inline std::pair<bool, bool>
+predicate_NameTreeEntry_NameA_Only(const name_tree::Entry& entry)
+{
+ Name nameA("/a");
+ bool first = nameA.equals(entry.getPrefix());
+ return std::make_pair(first, true);
+}
+
+static inline std::pair<bool, bool>
+predicate_NameTreeEntry_Except_NameA(const name_tree::Entry& entry)
+{
+ Name nameA("/a");
+ bool first = !(nameA.equals(entry.getPrefix()));
+ return std::make_pair(first, true);
+}
+
+static inline std::pair<bool, bool>
+predicate_NameTreeEntry_NoSubNameA(const name_tree::Entry& entry)
+{
+ Name nameA("/a");
+ bool second = !(nameA.equals(entry.getPrefix()));
+ return std::make_pair(true, second);
+}
+
+static inline std::pair<bool, bool>
+predicate_NameTreeEntry_NoNameA_NoSubNameAB(const name_tree::Entry& entry)
+{
+ Name nameA("/a");
+ Name nameAB("/a/b");
+ bool first = !(nameA.equals(entry.getPrefix()));
+ bool second = !(nameAB.equals(entry.getPrefix()));
+ return std::make_pair(first, second);
+}
+
+static inline std::pair<bool, bool>
+predicate_NameTreeEntry_NoNameA_NoSubNameAC(const name_tree::Entry& entry)
+{
+ Name nameA("/a");
+ Name nameAC("/a/c");
+ bool first = !(nameA.equals(entry.getPrefix()));
+ bool second = !(nameAC.equals(entry.getPrefix()));
+ return std::make_pair(first, second);
+}
+
+static inline std::pair<bool, bool>
+predicate_NameTreeEntry_Example(const name_tree::Entry& entry)
+{
+ Name nameRoot("/");
+ Name nameA("/a");
+ Name nameAB("/a/b");
+ Name nameABC("/a/b/c");
+ Name nameAD("/a/d");
+ Name nameE("/e");
+ Name nameF("/f");
+
+ bool first = false;
+ bool second = false;
+
+ Name name = entry.getPrefix();
+
+ if (name == nameRoot || name == nameAB || name == nameABC || name == nameAD)
+ {
+ first = true;
+ }
+
+ if(name == nameRoot || name == nameA || name == nameF)
+ {
+ second = true;
+ }
+
+ return std::make_pair(first, second);
+}
+
+BOOST_AUTO_TEST_CASE(NameTreeIteratorPartialEnumerate)
+{
+ typedef NameTree::const_iterator const_iterator;
+
+ size_t nBuckets = 16;
+ NameTree nameTree(nBuckets);
+ int counter = 0;
+
+ // empty nameTree, should return end();
+ Name nameA("/a");
+ bool hasA = false;
+ const_iterator itA = nameTree.partialEnumerate(nameA);
+ BOOST_CHECK(itA == nameTree.end());
+
+ // We have "/", "/a", "/a/b", "a/c" now.
+ Name nameAB("/a/b");
+ bool hasAB = false;
+ nameTree.lookup(nameAB);
+
+ Name nameAC("/a/c");
+ bool hasAC = false;
+ nameTree.lookup(nameAC);
+ BOOST_CHECK_EQUAL(nameTree.size(), 4);
+
+ // Enumerate on some name that is not in nameTree
+ Name name0="/0";
+ const_iterator it0 = nameTree.partialEnumerate(name0);
+ BOOST_CHECK(it0 == nameTree.end());
+
+ // Accept "root" nameA only
+ const_iterator itAOnly = nameTree.partialEnumerate(nameA, &predicate_NameTreeEntry_NameA_Only);
+ BOOST_CHECK_EQUAL(itAOnly->getPrefix(), nameA);
+ BOOST_CHECK(++itAOnly == nameTree.end());
+
+ // Accept anything except "root" nameA
+ const_iterator itExceptA = nameTree.partialEnumerate(nameA, &predicate_NameTreeEntry_Except_NameA);
+ hasA = false;
+ hasAB = false;
+ hasAC = false;
+
+ counter = 0;
+ for (; itExceptA != nameTree.end(); itExceptA++)
+ {
+ counter++;
+
+ if (itExceptA->getPrefix() == nameA)
+ hasA = true;
+
+ if (itExceptA->getPrefix() == nameAB)
+ hasAB = true;
+
+ if (itExceptA->getPrefix() == nameAC)
+ hasAC = true;
+ }
+ BOOST_CHECK_EQUAL(hasA, false);
+ BOOST_CHECK_EQUAL(hasAB, true);
+ BOOST_CHECK_EQUAL(hasAC, true);
+
+ BOOST_CHECK_EQUAL(counter, 2);
+
+ Name nameAB1("a/b/1");
+ bool hasAB1 = false;
+ nameTree.lookup(nameAB1);
+
+ Name nameAB2("a/b/2");
+ bool hasAB2 = false;
+ nameTree.lookup(nameAB2);
+
+ Name nameAC1("a/c/1");
+ bool hasAC1 = false;
+ nameTree.lookup(nameAC1);
+
+ Name nameAC2("a/c/2");
+ bool hasAC2 = false;
+ nameTree.lookup(nameAC2);
+
+ BOOST_CHECK_EQUAL(nameTree.size(), 8);
+
+ // No NameA
+ // No SubTree from NameAB
+ const_iterator itNoNameANoSubNameAB = nameTree.partialEnumerate(nameA, &predicate_NameTreeEntry_NoNameA_NoSubNameAB);
+ hasA = false;
+ hasAB = false;
+ hasAB1 = false;
+ hasAB2 = false;
+ hasAC = false;
+ hasAC1 = false;
+ hasAC2 = false;
+
+ counter = 0;
+
+ for (; itNoNameANoSubNameAB != nameTree.end(); itNoNameANoSubNameAB++)
+ {
+ counter++;
+
+ if (itNoNameANoSubNameAB->getPrefix() == nameA)
+ hasA = true;
+
+ if (itNoNameANoSubNameAB->getPrefix() == nameAB)
+ hasAB = true;
+
+ if (itNoNameANoSubNameAB->getPrefix() == nameAB1)
+ hasAB1 = true;
+
+ if (itNoNameANoSubNameAB->getPrefix() == nameAB2)
+ hasAB2 = true;
+
+ if (itNoNameANoSubNameAB->getPrefix() == nameAC)
+ hasAC = true;
+
+ if (itNoNameANoSubNameAB->getPrefix() == nameAC1)
+ hasAC1 = true;
+
+ if (itNoNameANoSubNameAB->getPrefix() == nameAC2)
+ hasAC2 = true;
+ }
+
+ BOOST_CHECK_EQUAL(hasA, false);
+ BOOST_CHECK_EQUAL(hasAB, true);
+ BOOST_CHECK_EQUAL(hasAB1, false);
+ BOOST_CHECK_EQUAL(hasAB2, false);
+ BOOST_CHECK_EQUAL(hasAC, true);
+ BOOST_CHECK_EQUAL(hasAC1, true);
+ BOOST_CHECK_EQUAL(hasAC2, true);
+
+ BOOST_CHECK_EQUAL(counter, 4);
+
+ // No NameA
+ // No SubTree from NameAC
+ const_iterator itNoNameANoSubNameAC = nameTree.partialEnumerate(nameA, &predicate_NameTreeEntry_NoNameA_NoSubNameAC);
+ hasA = false;
+ hasAB = false;
+ hasAB1 = false;
+ hasAB2 = false;
+ hasAC = false;
+ hasAC1 = false;
+ hasAC2 = false;
+
+ counter = 0;
+
+ for (; itNoNameANoSubNameAC != nameTree.end(); itNoNameANoSubNameAC++)
+ {
+ counter++;
+
+ if (itNoNameANoSubNameAC->getPrefix() == nameA)
+ hasA = true;
+
+ if (itNoNameANoSubNameAC->getPrefix() == nameAB)
+ hasAB = true;
+
+ if (itNoNameANoSubNameAC->getPrefix() == nameAB1)
+ hasAB1 = true;
+
+ if (itNoNameANoSubNameAC->getPrefix() == nameAB2)
+ hasAB2 = true;
+
+ if (itNoNameANoSubNameAC->getPrefix() == nameAC)
+ hasAC = true;
+
+ if (itNoNameANoSubNameAC->getPrefix() == nameAC1)
+ hasAC1 = true;
+
+ if (itNoNameANoSubNameAC->getPrefix() == nameAC2)
+ hasAC2 = true;
+ }
+
+ BOOST_CHECK_EQUAL(hasA, false);
+ BOOST_CHECK_EQUAL(hasAB, true);
+ BOOST_CHECK_EQUAL(hasAB1, true);
+ BOOST_CHECK_EQUAL(hasAB2, true);
+ BOOST_CHECK_EQUAL(hasAC, true);
+ BOOST_CHECK_EQUAL(hasAC1, false);
+ BOOST_CHECK_EQUAL(hasAC2, false);
+
+ BOOST_CHECK_EQUAL(counter, 4);
+
+ // No Subtree from NameA
+ const_iterator itNoANoSubNameA = nameTree.partialEnumerate(nameA, &predicate_NameTreeEntry_NoSubNameA);
+
+ hasA = false;
+ hasAB = false;
+ hasAB1 = false;
+ hasAB2 = false;
+ hasAC = false;
+ hasAC1 = false;
+ hasAC2 = false;
+
+ counter = 0;
+
+ for (; itNoANoSubNameA != nameTree.end(); itNoANoSubNameA++)
+ {
+ counter++;
+
+ if (itNoANoSubNameA->getPrefix() == nameA)
+ hasA = true;
+
+ if (itNoANoSubNameA->getPrefix() == nameAB)
+ hasAB = true;
+
+ if (itNoANoSubNameA->getPrefix() == nameAB1)
+ hasAB1 = true;
+
+ if (itNoANoSubNameA->getPrefix() == nameAB2)
+ hasAB2 = true;
+
+ if (itNoANoSubNameA->getPrefix() == nameAC)
+ hasAC = true;
+
+ if (itNoANoSubNameA->getPrefix() == nameAC1)
+ hasAC1 = true;
+
+ if (itNoANoSubNameA->getPrefix() == nameAC2)
+ hasAC2 = true;
+ }
+
+ BOOST_CHECK_EQUAL(hasA, true);
+ BOOST_CHECK_EQUAL(hasAB, false);
+ BOOST_CHECK_EQUAL(hasAB1, false);
+ BOOST_CHECK_EQUAL(hasAB2, false);
+ BOOST_CHECK_EQUAL(hasAC, false);
+ BOOST_CHECK_EQUAL(hasAC1, false);
+ BOOST_CHECK_EQUAL(hasAC2, false);
+
+ BOOST_CHECK_EQUAL(counter, 1);
+
+ // Example
+ // /
+ // /A
+ // /A/B x
+ // /A/B/C
+ // /A/D x
+ // /E
+ // /F
+
+ NameTree nameTreeExample(nBuckets);
+
+ Name nameRoot("/");
+ bool hasRoot = false;
+
+ nameTreeExample.lookup(nameA);
+ hasA = false;
+ nameTreeExample.lookup(nameAB);
+ hasAB = false;
+
+ Name nameABC("a/b/c");
+ bool hasABC = false;
+ nameTreeExample.lookup(nameABC);
+
+ Name nameAD("/a/d");
+ nameTreeExample.lookup(nameAD);
+ bool hasAD = false;
+
+ Name nameE("/e");
+ nameTreeExample.lookup(nameE);
+ bool hasE = false;
+
+ Name nameF("/f");
+ nameTreeExample.lookup(nameF);
+ bool hasF = false;
+
+ counter = 0;
+ const_iterator itExample = nameTreeExample.partialEnumerate(nameA, &predicate_NameTreeEntry_Example);
+
+ for (; itExample != nameTreeExample.end(); itExample++)
+ {
+ counter++;
+
+ if (itExample->getPrefix() == nameRoot)
+ hasRoot = true;
+
+ if (itExample->getPrefix() == nameA)
+ hasA = true;
+
+ if (itExample->getPrefix() == nameAB)
+ hasAB = true;
+
+ if (itExample->getPrefix() == nameABC)
+ hasABC = true;
+
+ if (itExample->getPrefix() == nameAD)
+ hasAD = true;
+
+ if (itExample->getPrefix() == nameE)
+ hasE = true;
+
+ if (itExample->getPrefix() == nameF)
+ hasF = true;
+ }
+
+ BOOST_CHECK_EQUAL(hasRoot, false);
+ BOOST_CHECK_EQUAL(hasA, false);
+ BOOST_CHECK_EQUAL(hasAB, true);
+ BOOST_CHECK_EQUAL(hasABC, false);
+ BOOST_CHECK_EQUAL(hasAD, true);
+ BOOST_CHECK_EQUAL(hasE, false);
+ BOOST_CHECK_EQUAL(hasF, false);
+
+ BOOST_CHECK_EQUAL(counter, 2);
+}
+
+
+BOOST_AUTO_TEST_CASE(NameTreeIteratorFindAllMatches)
+{
+ size_t nBuckets = 16;
+ NameTree nt(nBuckets);
+ int counter = 0;
+
+ Name nameABCDEF("a/b/c/d/e/f");
+ shared_ptr<name_tree::Entry> npeABCDEF = nt.lookup(nameABCDEF);
+
+ Name nameAAC("a/a/c");
+ shared_ptr<name_tree::Entry> npeAAC = nt.lookup(nameAAC);
+
+ Name nameAAD1("a/a/d/1");
+ shared_ptr<name_tree::Entry> npeAAD1 = nt.lookup(nameAAD1);
+
+ Name nameAAD2("a/a/d/2");
+ shared_ptr<name_tree::Entry> npeAAD2 = nt.lookup(nameAAD2);
+
+
+ BOOST_CHECK_EQUAL(nt.size(), 12);
+
+ Name nameRoot ("/");
+ Name nameA ("/a");
+ Name nameAB ("/a/b");
+ Name nameABC ("/a/b/c");
+ Name nameABCD ("/a/b/c/d");
+ Name nameABCDE("/a/b/c/d/e");
+ Name nameAA ("/a/a");
+ Name nameAAD ("/a/a/d");
+
+ bool hasRoot = false;
+ bool hasA = false;
+ bool hasAB = false;
+ bool hasABC = false;
+ bool hasABCD = false;
+ bool hasABCDE = false;
+ bool hasABCDEF = false;
+ bool hasAA = false;
+ bool hasAAC = false;
+ bool hasAAD = false;
+ bool hasAAD1 = false;
+ bool hasAAD2 = false;
+
+ counter = 0;
+
+ for (NameTree::const_iterator it = nt.findAllMatches(nameABCDEF);
+ it != nt.end(); it++)
+ {
+ counter++;
+
+ if (it->getPrefix() == nameRoot)
+ hasRoot = true;
+ if (it->getPrefix() == nameA)
+ hasA = true;
+ if (it->getPrefix() == nameAB)
+ hasAB = true;
+ if (it->getPrefix() == nameABC)
+ hasABC = true;
+ if (it->getPrefix() == nameABCD)
+ hasABCD = true;
+ if (it->getPrefix() == nameABCDE)
+ hasABCDE = true;
+ if (it->getPrefix() == nameABCDEF)
+ hasABCDEF = true;
+ if (it->getPrefix() == nameAA)
+ hasAA = true;
+ if (it->getPrefix() == nameAAC)
+ hasAAC = true;
+ if (it->getPrefix() == nameAAD)
+ hasAAD = true;
+ if (it->getPrefix() == nameAAD1)
+ hasAAD1 = true;
+ if (it->getPrefix() == nameAAD2)
+ hasAAD2 = true;
+ }
+
+ BOOST_CHECK_EQUAL(hasRoot , true);
+ BOOST_CHECK_EQUAL(hasA , true);
+ BOOST_CHECK_EQUAL(hasAB , true);
+ BOOST_CHECK_EQUAL(hasABC , true);
+ BOOST_CHECK_EQUAL(hasABCD , true);
+ BOOST_CHECK_EQUAL(hasABCDE , true);
+ BOOST_CHECK_EQUAL(hasABCDEF , true);
+ BOOST_CHECK_EQUAL(hasAA , false);
+ BOOST_CHECK_EQUAL(hasAAC , false);
+ BOOST_CHECK_EQUAL(hasAAD , false);
+ BOOST_CHECK_EQUAL(hasAAD1 , false);
+ BOOST_CHECK_EQUAL(hasAAD2 , false);
+
+ BOOST_CHECK_EQUAL(counter, 7);
+}
+
+BOOST_AUTO_TEST_CASE(NameTreeHashTableResizeShrink)
+{
+ size_t nBuckets = 16;
+ NameTree nameTree(nBuckets);
+
+ Name prefix("/a/b/c/d/e/f/g/h"); // requires 9 buckets
+
+ shared_ptr<name_tree::Entry> entry = nameTree.lookup(prefix);
+ BOOST_CHECK_EQUAL(nameTree.size(), 9);
+ BOOST_CHECK_EQUAL(nameTree.getNBuckets(), 32);
+
+ nameTree.eraseEntryIfEmpty(entry);
+ BOOST_CHECK_EQUAL(nameTree.size(), 0);
+ BOOST_CHECK_EQUAL(nameTree.getNBuckets(), 16);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+} // namespace nfd
diff --git a/tests/daemon/table/pit.cpp b/tests/daemon/table/pit.cpp
new file mode 100644
index 0000000..c77c909
--- /dev/null
+++ b/tests/daemon/table/pit.cpp
@@ -0,0 +1,383 @@
+/* -*- 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 "table/pit.hpp"
+#include "tests/daemon/face/dummy-face.hpp"
+
+#include "tests/test-common.hpp"
+
+namespace nfd {
+namespace tests {
+
+BOOST_FIXTURE_TEST_SUITE(TablePit, BaseFixture)
+
+BOOST_AUTO_TEST_CASE(EntryInOutRecords)
+{
+ shared_ptr<Face> face1 = make_shared<DummyFace>();
+ shared_ptr<Face> face2 = make_shared<DummyFace>();
+ Name name("ndn:/KuYfjtRq");
+ shared_ptr<Interest> interest = makeInterest(name);
+ shared_ptr<Interest> interest1 = makeInterest(name);
+ interest1->setInterestLifetime(time::milliseconds(2528));
+ interest1->setNonce(25559);
+ shared_ptr<Interest> interest2 = makeInterest(name);
+ interest2->setInterestLifetime(time::milliseconds(6464));
+ interest2->setNonce(19004);
+ shared_ptr<Interest> interest3 = makeInterest(name);
+ interest3->setInterestLifetime(time::milliseconds(3585));
+ interest3->setNonce(24216);
+ shared_ptr<Interest> interest4 = makeInterest(name);
+ interest4->setInterestLifetime(time::milliseconds(8795));
+ interest4->setNonce(17365);
+
+ pit::Entry entry(*interest);
+
+ BOOST_CHECK_EQUAL(entry.getInterest().getName(), name);
+ BOOST_CHECK_EQUAL(entry.getName(), name);
+
+ const pit::InRecordCollection& inRecords1 = entry.getInRecords();
+ BOOST_CHECK_EQUAL(inRecords1.size(), 0);
+ const pit::OutRecordCollection& outRecords1 = entry.getOutRecords();
+ BOOST_CHECK_EQUAL(outRecords1.size(), 0);
+
+ // insert InRecord
+ time::steady_clock::TimePoint before1 = time::steady_clock::now();
+ pit::InRecordCollection::iterator in1 =
+ entry.insertOrUpdateInRecord(face1, *interest1);
+ time::steady_clock::TimePoint after1 = time::steady_clock::now();
+ const pit::InRecordCollection& inRecords2 = entry.getInRecords();
+ BOOST_CHECK_EQUAL(inRecords2.size(), 1);
+ BOOST_CHECK(in1 == inRecords2.begin());
+ BOOST_CHECK_EQUAL(in1->getFace(), face1);
+ BOOST_CHECK_EQUAL(in1->getLastNonce(), interest1->getNonce());
+ BOOST_CHECK_GE(in1->getLastRenewed(), before1);
+ BOOST_CHECK_LE(in1->getLastRenewed(), after1);
+ BOOST_CHECK_LE(in1->getExpiry() - in1->getLastRenewed()
+ - interest1->getInterestLifetime(),
+ (after1 - before1));
+
+ // insert OutRecord
+ time::steady_clock::TimePoint before2 = time::steady_clock::now();
+ pit::OutRecordCollection::iterator out1 =
+ entry.insertOrUpdateOutRecord(face1, *interest1);
+ time::steady_clock::TimePoint after2 = time::steady_clock::now();
+ const pit::OutRecordCollection& outRecords2 = entry.getOutRecords();
+ BOOST_CHECK_EQUAL(outRecords2.size(), 1);
+ BOOST_CHECK(out1 == outRecords2.begin());
+ BOOST_CHECK_EQUAL(out1->getFace(), face1);
+ BOOST_CHECK_EQUAL(out1->getLastNonce(), interest1->getNonce());
+ BOOST_CHECK_GE(out1->getLastRenewed(), before2);
+ BOOST_CHECK_LE(out1->getLastRenewed(), after2);
+ BOOST_CHECK_LE(out1->getExpiry() - out1->getLastRenewed()
+ - interest1->getInterestLifetime(),
+ (after2 - before2));
+
+ // update InRecord
+ time::steady_clock::TimePoint before3 = time::steady_clock::now();
+ pit::InRecordCollection::iterator in2 =
+ entry.insertOrUpdateInRecord(face1, *interest2);
+ time::steady_clock::TimePoint after3 = time::steady_clock::now();
+ const pit::InRecordCollection& inRecords3 = entry.getInRecords();
+ BOOST_CHECK_EQUAL(inRecords3.size(), 1);
+ BOOST_CHECK(in2 == inRecords3.begin());
+ BOOST_CHECK_EQUAL(in2->getFace(), face1);
+ BOOST_CHECK_EQUAL(in2->getLastNonce(), interest2->getNonce());
+ BOOST_CHECK_LE(in2->getExpiry() - in2->getLastRenewed()
+ - interest2->getInterestLifetime(),
+ (after3 - before3));
+
+ // insert another InRecord
+ pit::InRecordCollection::iterator in3 =
+ entry.insertOrUpdateInRecord(face2, *interest3);
+ const pit::InRecordCollection& inRecords4 = entry.getInRecords();
+ BOOST_CHECK_EQUAL(inRecords4.size(), 2);
+ BOOST_CHECK_EQUAL(in3->getFace(), face2);
+
+ // delete all InRecords
+ entry.deleteInRecords();
+ const pit::InRecordCollection& inRecords5 = entry.getInRecords();
+ BOOST_CHECK_EQUAL(inRecords5.size(), 0);
+
+ // insert another OutRecord
+ pit::OutRecordCollection::iterator out2 =
+ entry.insertOrUpdateOutRecord(face2, *interest4);
+ const pit::OutRecordCollection& outRecords3 = entry.getOutRecords();
+ BOOST_CHECK_EQUAL(outRecords3.size(), 2);
+ BOOST_CHECK_EQUAL(out2->getFace(), face2);
+
+ // delete OutRecord
+ entry.deleteOutRecord(face2);
+ const pit::OutRecordCollection& outRecords4 = entry.getOutRecords();
+ BOOST_REQUIRE_EQUAL(outRecords4.size(), 1);
+ BOOST_CHECK_EQUAL(outRecords4.begin()->getFace(), face1);
+}
+
+BOOST_AUTO_TEST_CASE(EntryNonce)
+{
+ shared_ptr<Interest> interest = makeInterest("ndn:/qtCQ7I1c");
+
+ pit::Entry entry(*interest);
+
+ BOOST_CHECK_EQUAL(entry.addNonce(25559), true);
+ BOOST_CHECK_EQUAL(entry.addNonce(25559), false);
+ BOOST_CHECK_EQUAL(entry.addNonce(19004), true);
+ BOOST_CHECK_EQUAL(entry.addNonce(19004), false);
+}
+
+BOOST_AUTO_TEST_CASE(EntryLifetime)
+{
+ shared_ptr<Interest> interest = makeInterest("ndn:/7oIEurbgy6");
+ BOOST_ASSERT(interest->getInterestLifetime() < time::milliseconds::zero()); // library uses -1 to indicate unset lifetime
+
+ shared_ptr<Face> face = make_shared<DummyFace>();
+ pit::Entry entry(*interest);
+
+ pit::InRecordCollection::iterator inIt = entry.insertOrUpdateInRecord(face, *interest);
+ BOOST_CHECK_GT(inIt->getExpiry(), time::steady_clock::now());
+
+ pit::OutRecordCollection::iterator outIt = entry.insertOrUpdateOutRecord(face, *interest);
+ BOOST_CHECK_GT(outIt->getExpiry(), time::steady_clock::now());
+}
+
+BOOST_AUTO_TEST_CASE(EntryCanForwardTo)
+{
+ shared_ptr<Interest> interest = makeInterest("ndn:/WDsuBLIMG");
+ pit::Entry entry(*interest);
+
+ shared_ptr<Face> face1 = make_shared<DummyFace>();
+ shared_ptr<Face> face2 = make_shared<DummyFace>();
+
+ entry.insertOrUpdateInRecord(face1, *interest);
+ BOOST_CHECK_EQUAL(entry.canForwardTo(*face1), false);
+ BOOST_CHECK_EQUAL(entry.canForwardTo(*face2), true);
+
+ entry.insertOrUpdateInRecord(face2, *interest);
+ BOOST_CHECK_EQUAL(entry.canForwardTo(*face1), true);
+ BOOST_CHECK_EQUAL(entry.canForwardTo(*face2), true);
+
+ entry.insertOrUpdateOutRecord(face1, *interest);
+ BOOST_CHECK_EQUAL(entry.canForwardTo(*face1), false);
+ BOOST_CHECK_EQUAL(entry.canForwardTo(*face2), true);
+}
+
+BOOST_AUTO_TEST_CASE(Insert)
+{
+ Name name1("ndn:/5vzBNnMst");
+ Name name2("ndn:/igSGfEIM62");
+ Exclude exclude1;
+ exclude1.excludeOne(Name::Component("u26p47oep"));
+ Exclude exclude2;
+ exclude2.excludeBefore(Name::Component("u26p47oep"));
+ ndn::KeyLocator keyLocator1("ndn:/sGAE3peMHA");
+ ndn::KeyLocator keyLocator2("ndn:/nIJH6pr4");
+
+ NameTree nameTree(16);
+ Pit pit(nameTree);
+ BOOST_CHECK_EQUAL(pit.size(), 0);
+ std::pair<shared_ptr<pit::Entry>, bool> insertResult;
+
+ // base
+ shared_ptr<Interest> interestA = make_shared<Interest>(name1);
+ insertResult = pit.insert(*interestA);
+ BOOST_CHECK_EQUAL(insertResult.second, true);
+ BOOST_CHECK_EQUAL(pit.size(), 1);
+
+ // A+MinSuffixComponents
+ shared_ptr<Interest> interestB = make_shared<Interest>(*interestA);
+ interestB->setMinSuffixComponents(2);
+ insertResult = pit.insert(*interestB);
+ BOOST_CHECK_EQUAL(insertResult.second, true);
+ BOOST_CHECK_EQUAL(pit.size(), 2);
+
+ // A+MaxSuffixComponents
+ shared_ptr<Interest> interestC = make_shared<Interest>(*interestA);
+ interestC->setMaxSuffixComponents(4);
+ insertResult = pit.insert(*interestC);
+ BOOST_CHECK_EQUAL(insertResult.second, true);
+ BOOST_CHECK_EQUAL(pit.size(), 3);
+
+ // A+KeyLocator1
+ shared_ptr<Interest> interestD = make_shared<Interest>(*interestA);
+ interestD->setPublisherPublicKeyLocator(keyLocator1);
+ insertResult = pit.insert(*interestD);
+ BOOST_CHECK_EQUAL(insertResult.second, true);
+ BOOST_CHECK_EQUAL(pit.size(), 4);
+
+ // A+KeyLocator2
+ shared_ptr<Interest> interestE = make_shared<Interest>(*interestA);
+ interestE->setPublisherPublicKeyLocator(keyLocator2);
+ insertResult = pit.insert(*interestE);
+ BOOST_CHECK_EQUAL(insertResult.second, true);
+ BOOST_CHECK_EQUAL(pit.size(), 5);
+
+ // A+Exclude1
+ shared_ptr<Interest> interestF = make_shared<Interest>(*interestA);
+ interestF->setExclude(exclude1);
+ insertResult = pit.insert(*interestF);
+ BOOST_CHECK_EQUAL(insertResult.second, true);
+ BOOST_CHECK_EQUAL(pit.size(), 6);
+
+ // A+Exclude2
+ shared_ptr<Interest> interestG = make_shared<Interest>(*interestA);
+ interestG->setExclude(exclude2);
+ insertResult = pit.insert(*interestG);
+ BOOST_CHECK_EQUAL(insertResult.second, true);
+ BOOST_CHECK_EQUAL(pit.size(), 7);
+
+ // A+ChildSelector0
+ shared_ptr<Interest> interestH = make_shared<Interest>(*interestA);
+ interestH->setChildSelector(0);
+ insertResult = pit.insert(*interestH);
+ BOOST_CHECK_EQUAL(insertResult.second, true);
+ BOOST_CHECK_EQUAL(pit.size(), 8);
+
+ // A+ChildSelector1
+ shared_ptr<Interest> interestI = make_shared<Interest>(*interestA);
+ interestI->setChildSelector(1);
+ insertResult = pit.insert(*interestI);
+ BOOST_CHECK_EQUAL(insertResult.second, true);
+ BOOST_CHECK_EQUAL(pit.size(), 9);
+
+ // A+MustBeFresh
+ shared_ptr<Interest> interestJ = make_shared<Interest>(*interestA);
+ interestJ->setMustBeFresh(true);
+ insertResult = pit.insert(*interestJ);
+ BOOST_CHECK_EQUAL(insertResult.second, true);
+ BOOST_CHECK_EQUAL(pit.size(), 10);
+
+ // A+InterestLifetime
+ shared_ptr<Interest> interestK = make_shared<Interest>(*interestA);
+ interestK->setInterestLifetime(time::milliseconds(1000));
+ insertResult = pit.insert(*interestK);
+ BOOST_CHECK_EQUAL(insertResult.second, false);// only guiders differ
+ BOOST_CHECK_EQUAL(pit.size(), 10);
+
+ // A+Nonce
+ shared_ptr<Interest> interestL = make_shared<Interest>(*interestA);
+ interestL->setNonce(2192);
+ insertResult = pit.insert(*interestL);
+ BOOST_CHECK_EQUAL(insertResult.second, false);// only guiders differ
+ BOOST_CHECK_EQUAL(pit.size(), 10);
+
+ // different Name+Exclude1
+ shared_ptr<Interest> interestM = make_shared<Interest>(name2);
+ interestM->setExclude(exclude1);
+ insertResult = pit.insert(*interestM);
+ BOOST_CHECK_EQUAL(insertResult.second, true);
+ BOOST_CHECK_EQUAL(pit.size(), 11);
+}
+
+BOOST_AUTO_TEST_CASE(Erase)
+{
+ Interest interest(Name("ndn:/z88Admz6A2"));
+
+ NameTree nameTree(16);
+ Pit pit(nameTree);
+
+ std::pair<shared_ptr<pit::Entry>, bool> insertResult;
+
+ BOOST_CHECK_EQUAL(pit.size(), 0);
+
+ insertResult = pit.insert(interest);
+ BOOST_CHECK_EQUAL(insertResult.second, true);
+ BOOST_CHECK_EQUAL(pit.size(), 1);
+
+ insertResult = pit.insert(interest);
+ BOOST_CHECK_EQUAL(insertResult.second, false);
+ BOOST_CHECK_EQUAL(pit.size(), 1);
+
+ pit.erase(insertResult.first);
+ BOOST_CHECK_EQUAL(pit.size(), 0);
+
+ insertResult = pit.insert(interest);
+ BOOST_CHECK_EQUAL(insertResult.second, true);
+ BOOST_CHECK_EQUAL(pit.size(), 1);
+
+}
+
+BOOST_AUTO_TEST_CASE(FindAllDataMatches)
+{
+ Name nameA ("ndn:/A");
+ Name nameAB ("ndn:/A/B");
+ Name nameABC ("ndn:/A/B/C");
+ Name nameABCD("ndn:/A/B/C/D");
+ Name nameD ("ndn:/D");
+
+ Interest interestA (nameA );
+ Interest interestABC(nameABC);
+ Interest interestD (nameD );
+
+ NameTree nameTree(16);
+ Pit pit(nameTree);
+ int count = 0;
+
+ BOOST_CHECK_EQUAL(pit.size(), 0);
+
+ pit.insert(interestA );
+ pit.insert(interestABC);
+ pit.insert(interestD );
+
+ nameTree.lookup(nameABCD); // make sure /A/B/C/D is in nameTree
+
+ BOOST_CHECK_EQUAL(pit.size(), 3);
+
+ Data data(nameABCD);
+
+ shared_ptr<pit::DataMatchResult> matches = pit.findAllDataMatches(data);
+
+ bool hasA = false;
+ bool hasAB = false;
+ bool hasABC = false;
+ bool hasD = false;
+
+ for (pit::DataMatchResult::iterator it = matches->begin();
+ it != matches->end(); ++it) {
+ ++count;
+ shared_ptr<pit::Entry> entry = *it;
+
+ if (entry->getName().equals(nameA ))
+ hasA = true;
+
+ if (entry->getName().equals(nameAB))
+ hasAB = true;
+
+ if (entry->getName().equals(nameABC))
+ hasABC = true;
+
+ if (entry->getName().equals(nameD))
+ hasD = true;
+ }
+ BOOST_CHECK_EQUAL(hasA , true);
+ BOOST_CHECK_EQUAL(hasAB , false);
+ BOOST_CHECK_EQUAL(hasABC, true);
+ BOOST_CHECK_EQUAL(hasD , false);
+
+ BOOST_CHECK_EQUAL(count, 2);
+
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+} // namespace nfd
diff --git a/tests/daemon/table/strategy-choice.cpp b/tests/daemon/table/strategy-choice.cpp
new file mode 100644
index 0000000..6927375
--- /dev/null
+++ b/tests/daemon/table/strategy-choice.cpp
@@ -0,0 +1,155 @@
+/* -*- 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 "table/strategy-choice.hpp"
+#include "tests/daemon/fw/dummy-strategy.hpp"
+
+#include "tests/test-common.hpp"
+
+namespace nfd {
+namespace tests {
+
+BOOST_FIXTURE_TEST_SUITE(TableStrategyChoice, BaseFixture)
+
+using fw::Strategy;
+
+BOOST_AUTO_TEST_CASE(Effective)
+{
+ Forwarder forwarder;
+ Name nameP("ndn:/strategy/P");
+ Name nameQ("ndn:/strategy/Q");
+ Name nameZ("ndn:/strategy/Z");
+ shared_ptr<Strategy> strategyP = make_shared<DummyStrategy>(boost::ref(forwarder), nameP);
+ shared_ptr<Strategy> strategyQ = make_shared<DummyStrategy>(boost::ref(forwarder), nameQ);
+
+ StrategyChoice& table = forwarder.getStrategyChoice();
+
+ // install
+ BOOST_CHECK_EQUAL(table.install(strategyP), true);
+ BOOST_CHECK_EQUAL(table.install(strategyQ), true);
+ BOOST_CHECK_EQUAL(table.install(strategyQ), false);
+
+ BOOST_CHECK(table.insert("ndn:/", nameP));
+ // { '/'=>P }
+
+ BOOST_CHECK_EQUAL(table.findEffectiveStrategy("ndn:/") .getName(), nameP);
+ BOOST_CHECK_EQUAL(table.findEffectiveStrategy("ndn:/A") .getName(), nameP);
+ BOOST_CHECK_EQUAL(table.findEffectiveStrategy("ndn:/A/B").getName(), nameP);
+
+ BOOST_CHECK(table.insert("ndn:/A/B", nameP));
+ // { '/'=>P, '/A/B'=>P }
+
+ BOOST_CHECK_EQUAL(table.findEffectiveStrategy("ndn:/") .getName(), nameP);
+ BOOST_CHECK_EQUAL(table.findEffectiveStrategy("ndn:/A") .getName(), nameP);
+ BOOST_CHECK_EQUAL(table.findEffectiveStrategy("ndn:/A/B").getName(), nameP);
+ // same instance
+ BOOST_CHECK_EQUAL(&table.findEffectiveStrategy("ndn:/"), strategyP.get());
+ BOOST_CHECK_EQUAL(&table.findEffectiveStrategy("ndn:/A"), strategyP.get());
+ BOOST_CHECK_EQUAL(&table.findEffectiveStrategy("ndn:/A/B"), strategyP.get());
+
+ table.erase("ndn:/A"); // no effect
+ // { '/'=>P, '/A/B'=>P }
+
+ BOOST_CHECK_EQUAL(table.findEffectiveStrategy("ndn:/") .getName(), nameP);
+ BOOST_CHECK_EQUAL(table.findEffectiveStrategy("ndn:/A") .getName(), nameP);
+ BOOST_CHECK_EQUAL(table.findEffectiveStrategy("ndn:/A/B").getName(), nameP);
+
+ BOOST_CHECK(table.insert("ndn:/A", nameQ));
+ // { '/'=>P, '/A/B'=>P, '/A'=>Q }
+
+ BOOST_CHECK_EQUAL(table.findEffectiveStrategy("ndn:/") .getName(), nameP);
+ BOOST_CHECK_EQUAL(table.findEffectiveStrategy("ndn:/A") .getName(), nameQ);
+ BOOST_CHECK_EQUAL(table.findEffectiveStrategy("ndn:/A/B").getName(), nameP);
+
+ table.erase("ndn:/A/B");
+ // { '/'=>P, '/A'=>Q }
+
+ BOOST_CHECK_EQUAL(table.findEffectiveStrategy("ndn:/") .getName(), nameP);
+ BOOST_CHECK_EQUAL(table.findEffectiveStrategy("ndn:/A") .getName(), nameQ);
+ BOOST_CHECK_EQUAL(table.findEffectiveStrategy("ndn:/A/B").getName(), nameQ);
+
+ BOOST_CHECK(!table.insert("ndn:/", nameZ)); // non existent strategy
+
+ BOOST_CHECK(table.insert("ndn:/", nameQ));
+ BOOST_CHECK(table.insert("ndn:/A", nameP));
+ // { '/'=>Q, '/A'=>P }
+
+ BOOST_CHECK_EQUAL(table.findEffectiveStrategy("ndn:/") .getName(), nameQ);
+ BOOST_CHECK_EQUAL(table.findEffectiveStrategy("ndn:/A") .getName(), nameP);
+ BOOST_CHECK_EQUAL(table.findEffectiveStrategy("ndn:/A/B").getName(), nameP);
+ BOOST_CHECK_EQUAL(table.findEffectiveStrategy("ndn:/D") .getName(), nameQ);
+}
+
+class PStrategyInfo : public fw::StrategyInfo
+{
+};
+
+BOOST_AUTO_TEST_CASE(ClearStrategyInfo)
+{
+ Forwarder forwarder;
+ Name nameP("ndn:/strategy/P");
+ Name nameQ("ndn:/strategy/Q");
+ shared_ptr<Strategy> strategyP = make_shared<DummyStrategy>(boost::ref(forwarder), nameP);
+ shared_ptr<Strategy> strategyQ = make_shared<DummyStrategy>(boost::ref(forwarder), nameQ);
+
+ StrategyChoice& table = forwarder.getStrategyChoice();
+ Measurements& measurements = forwarder.getMeasurements();
+
+ // install
+ table.install(strategyP);
+ table.install(strategyQ);
+
+ BOOST_CHECK(table.insert("ndn:/", nameP));
+ // { '/'=>P }
+ measurements.get("ndn:/") ->getOrCreateStrategyInfo<PStrategyInfo>();
+ measurements.get("ndn:/A") ->getOrCreateStrategyInfo<PStrategyInfo>();
+ measurements.get("ndn:/A/B") ->getOrCreateStrategyInfo<PStrategyInfo>();
+ measurements.get("ndn:/A/C") ->getOrCreateStrategyInfo<PStrategyInfo>();
+
+ BOOST_CHECK(table.insert("ndn:/A/B", nameP));
+ // { '/'=>P, '/A/B'=>P }
+ BOOST_CHECK( static_cast<bool>(measurements.get("ndn:/") ->getStrategyInfo<PStrategyInfo>()));
+ BOOST_CHECK( static_cast<bool>(measurements.get("ndn:/A") ->getStrategyInfo<PStrategyInfo>()));
+ BOOST_CHECK( static_cast<bool>(measurements.get("ndn:/A/B")->getStrategyInfo<PStrategyInfo>()));
+ BOOST_CHECK( static_cast<bool>(measurements.get("ndn:/A/C")->getStrategyInfo<PStrategyInfo>()));
+
+ BOOST_CHECK(table.insert("ndn:/A", nameQ));
+ // { '/'=>P, '/A/B'=>P, '/A'=>Q }
+ BOOST_CHECK( static_cast<bool>(measurements.get("ndn:/") ->getStrategyInfo<PStrategyInfo>()));
+ BOOST_CHECK(!static_cast<bool>(measurements.get("ndn:/A") ->getStrategyInfo<PStrategyInfo>()));
+ BOOST_CHECK( static_cast<bool>(measurements.get("ndn:/A/B")->getStrategyInfo<PStrategyInfo>()));
+ BOOST_CHECK(!static_cast<bool>(measurements.get("ndn:/A/C")->getStrategyInfo<PStrategyInfo>()));
+
+ table.erase("ndn:/A/B");
+ // { '/'=>P, '/A'=>Q }
+ BOOST_CHECK( static_cast<bool>(measurements.get("ndn:/") ->getStrategyInfo<PStrategyInfo>()));
+ BOOST_CHECK(!static_cast<bool>(measurements.get("ndn:/A") ->getStrategyInfo<PStrategyInfo>()));
+ BOOST_CHECK(!static_cast<bool>(measurements.get("ndn:/A/B")->getStrategyInfo<PStrategyInfo>()));
+ BOOST_CHECK(!static_cast<bool>(measurements.get("ndn:/A/C")->getStrategyInfo<PStrategyInfo>()));
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+} // namespace nfd
diff --git a/tests/daemon/table/strategy-info-host.cpp b/tests/daemon/table/strategy-info-host.cpp
new file mode 100644
index 0000000..82d4fcc
--- /dev/null
+++ b/tests/daemon/table/strategy-info-host.cpp
@@ -0,0 +1,79 @@
+/* -*- 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 "table/strategy-info-host.hpp"
+
+#include "tests/test-common.hpp"
+
+namespace nfd {
+namespace tests {
+
+static int g_DummyStrategyInfo_count = 0;
+
+class DummyStrategyInfo : public fw::StrategyInfo
+{
+public:
+ DummyStrategyInfo(int id)
+ : m_id(id)
+ {
+ ++g_DummyStrategyInfo_count;
+ }
+
+ virtual ~DummyStrategyInfo()
+ {
+ --g_DummyStrategyInfo_count;
+ }
+
+ int m_id;
+};
+
+BOOST_FIXTURE_TEST_SUITE(TableStrategyInfoHost, BaseFixture)
+
+BOOST_AUTO_TEST_CASE(SetGetClear)
+{
+ StrategyInfoHost host;
+
+ BOOST_CHECK(!static_cast<bool>(host.getStrategyInfo<DummyStrategyInfo>()));
+
+ g_DummyStrategyInfo_count = 0;
+
+ shared_ptr<DummyStrategyInfo> info = make_shared<DummyStrategyInfo>(7591);
+ host.setStrategyInfo(info);
+ BOOST_REQUIRE(static_cast<bool>(host.getStrategyInfo<DummyStrategyInfo>()));
+ BOOST_CHECK_EQUAL(host.getStrategyInfo<DummyStrategyInfo>()->m_id, 7591);
+
+ info.reset(); // unlink local reference
+ // host should still have a reference to info
+ BOOST_REQUIRE(static_cast<bool>(host.getStrategyInfo<DummyStrategyInfo>()));
+ BOOST_CHECK_EQUAL(host.getStrategyInfo<DummyStrategyInfo>()->m_id, 7591);
+
+ host.clearStrategyInfo();
+ BOOST_CHECK(!static_cast<bool>(host.getStrategyInfo<DummyStrategyInfo>()));
+ BOOST_CHECK_EQUAL(g_DummyStrategyInfo_count, 0);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+} // namespace nfd