mgmt refactoring: FaceManager
Change-Id: I292a3e3d702746d6e946a2844e6f88f64cd711a4
Refs: #2107
diff --git a/tests/daemon/mgmt/face-manager-create-face.t.cpp b/tests/daemon/mgmt/face-manager-create-face.t.cpp
new file mode 100644
index 0000000..ffee5aa
--- /dev/null
+++ b/tests/daemon/mgmt/face-manager-create-face.t.cpp
@@ -0,0 +1,461 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014-2015, 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,
+ * The University of Memphis.
+ *
+ * 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 "fw/forwarder.hpp"
+
+#include "tests/test-common.hpp"
+
+#include <ndn-cxx/mgmt/dispatcher.hpp>
+#include <ndn-cxx/util/dummy-client-face.hpp>
+
+#include <boost/property_tree/info_parser.hpp>
+
+namespace nfd {
+namespace tests {
+
+
+BOOST_AUTO_TEST_SUITE(Mgmt)
+BOOST_AUTO_TEST_SUITE(TestFaceManager)
+
+BOOST_FIXTURE_TEST_SUITE(CreateFace, BaseFixture)
+
+class FaceManagerNode
+{
+public:
+ FaceManagerNode(ndn::KeyChain& keyChain, const std::string& port = "6363")
+ : face(ndn::util::makeDummyClientFace(getGlobalIoService(), {true, true}))
+ , dispatcher(*face, keyChain, ndn::security::SigningInfo())
+ , manager(forwarder.getFaceTable(), dispatcher, validator)
+ {
+ dispatcher.addTopPrefix("/localhost/nfd");
+
+ std::string basicConfig =
+ "face_system\n"
+ "{\n"
+ " tcp\n"
+ " {\n"
+ " port " + port + "\n"
+ " }\n"
+ " udp\n"
+ " {\n"
+ " port " + port + "\n"
+ " }\n"
+ "}\n"
+ "authorizations\n"
+ "{\n"
+ " authorize\n"
+ " {\n"
+ " certfile any\n"
+ " privileges\n"
+ " {\n"
+ " faces\n"
+ " }\n"
+ " }\n"
+ "}\n"
+ "\n";
+ std::istringstream input(basicConfig);
+ nfd::ConfigSection configSection;
+ boost::property_tree::read_info(input, configSection);
+
+ ConfigFile config;
+ manager.setConfigFile(config);
+ validator.setConfigFile(config);
+ config.parse(configSection, false, "dummy-config");
+ }
+
+ void
+ closeFaces()
+ {
+ std::vector<shared_ptr<Face>> facesToClose;
+ std::copy(forwarder.getFaceTable().begin(), forwarder.getFaceTable().end(),
+ std::back_inserter(facesToClose));
+ for (auto face : facesToClose) {
+ face->close();
+ }
+ }
+
+public:
+ Forwarder forwarder;
+ shared_ptr<ndn::util::DummyClientFace> face;
+ ndn::mgmt::Dispatcher dispatcher;
+ CommandValidator validator;
+ FaceManager manager;
+};
+
+class FaceManagerFixture : public UnitTestTimeFixture
+{
+public:
+ FaceManagerFixture()
+ : node1(keyChain, "16363")
+ , node2(keyChain, "26363")
+ {
+ advanceClocks(time::milliseconds(1), 100);
+ }
+
+ ~FaceManagerFixture()
+ {
+ node1.closeFaces();
+ node2.closeFaces();
+ advanceClocks(time::milliseconds(1), 100);
+ }
+
+public:
+ ndn::KeyChain keyChain;
+ FaceManagerNode node1; // used to test FaceManager
+ FaceManagerNode node2; // acts as a remote endpoint
+};
+
+class TcpFaceOnDemand
+{
+public:
+ ControlParameters
+ getParameters()
+ {
+ return ControlParameters()
+ .setUri("tcp4://127.0.0.1:26363")
+ .setFacePersistency(ndn::nfd::FACE_PERSISTENCY_ON_DEMAND);
+ }
+};
+
+class TcpFacePersistent
+{
+public:
+ ControlParameters
+ getParameters()
+ {
+ return ControlParameters()
+ .setUri("tcp4://127.0.0.1:26363")
+ .setFacePersistency(ndn::nfd::FACE_PERSISTENCY_PERSISTENT);
+ }
+};
+
+class TcpFacePermanent
+{
+public:
+ ControlParameters
+ getParameters()
+ {
+ return ControlParameters()
+ .setUri("tcp4://127.0.0.1:26363")
+ .setFacePersistency(ndn::nfd::FACE_PERSISTENCY_PERMANENT);
+ }
+};
+
+class UdpFaceOnDemand
+{
+public:
+ ControlParameters
+ getParameters()
+ {
+ return ControlParameters()
+ .setUri("udp4://127.0.0.1:26363")
+ .setFacePersistency(ndn::nfd::FACE_PERSISTENCY_ON_DEMAND);
+ }
+};
+
+class UdpFaceCannotConnect // face that will cause afterCreateFaceFailure to be invoked
+{
+public:
+ ControlParameters
+ getParameters()
+ {
+ return ControlParameters()
+ .setUri("udp4://0.0.0.0:16363"); // cannot connect to self
+ }
+};
+
+
+class UdpFacePersistent
+{
+public:
+ ControlParameters
+ getParameters()
+ {
+ return ControlParameters()
+ .setUri("udp4://127.0.0.1:26363")
+ .setFacePersistency(ndn::nfd::FACE_PERSISTENCY_PERSISTENT);
+ }
+};
+
+class UdpFacePermanent
+{
+public:
+ ControlParameters
+ getParameters()
+ {
+ return ControlParameters()
+ .setUri("udp4://127.0.0.1:26363")
+ .setFacePersistency(ndn::nfd::FACE_PERSISTENCY_PERMANENT);
+ }
+};
+
+class Success
+{
+public:
+ ControlResponse
+ getExpected()
+ {
+ return ControlResponse()
+ .setCode(200)
+ .setText("OK");
+ }
+};
+
+template<int CODE>
+class Failure
+{
+public:
+ ControlResponse
+ getExpected()
+ {
+ return ControlResponse()
+ .setCode(CODE)
+ .setText("Error"); // error description should not be checked
+ }
+};
+
+namespace mpl = boost::mpl;
+
+// pairs of CreateCommand and Success status
+typedef mpl::vector<mpl::pair<TcpFaceOnDemand, Failure<500>>,
+ mpl::pair<TcpFacePersistent, Success>,
+ mpl::pair<TcpFacePermanent, Failure<500>>,
+ mpl::pair<UdpFaceOnDemand, Failure<500>>,
+ mpl::pair<UdpFacePersistent, Success>,
+ mpl::pair<UdpFacePermanent, Success>,
+ mpl::pair<UdpFaceCannotConnect, Failure<408>>> Faces;
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE(NewFace, T, Faces, FaceManagerFixture)
+{
+ typedef typename T::first FaceType;
+ typedef typename T::second CreateResult;
+
+ Name commandName("/localhost/nfd/faces");
+ commandName.append("create");
+ commandName.append(FaceType().getParameters().wireEncode());
+
+ shared_ptr<Interest> command(make_shared<Interest>(commandName));
+ this->keyChain.sign(*command);
+
+ bool hasCallbackFired = false;
+ this->node1.face->onSendData.connect([this, command, &hasCallbackFired] (const Data& response) {
+ // std::cout << response << std::endl;
+ if (!command->getName().isPrefixOf(response.getName())) {
+ return;
+ }
+
+ ControlResponse actual(response.getContent().blockFromValue());
+ ControlResponse expected(CreateResult().getExpected());
+ BOOST_CHECK_EQUAL(expected.getCode(), actual.getCode());
+ BOOST_MESSAGE(actual.getText());
+
+ if (actual.getBody().hasWire()) {
+ ControlParameters expectedParams(FaceType().getParameters());
+ ControlParameters actualParams(actual.getBody());
+
+ BOOST_CHECK_EQUAL(expectedParams.getUri(), actualParams.getUri());
+ BOOST_CHECK_EQUAL(expectedParams.getFacePersistency(), actualParams.getFacePersistency());
+ }
+ hasCallbackFired = true;
+ });
+
+ this->node1.face->receive(*command);
+ this->advanceClocks(time::milliseconds(1), 100);
+
+ BOOST_CHECK(hasCallbackFired);
+}
+
+
+typedef mpl::vector<// mpl::pair<mpl::pair<TcpFacePersistent, TcpFacePermanent>, TcpFacePermanent>, // no need to check now
+ // mpl::pair<mpl::pair<TcpFacePermanent, TcpFacePersistent>, TcpFacePermanent>, // no need to check now
+ mpl::pair<mpl::pair<UdpFacePersistent, UdpFacePermanent>, UdpFacePermanent>,
+ mpl::pair<mpl::pair<UdpFacePermanent, UdpFacePersistent>, UdpFacePermanent>> FaceTransitions;
+
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE(ExistingFace, T, FaceTransitions, FaceManagerFixture)
+{
+ typedef typename T::first::first FaceType1;
+ typedef typename T::first::second FaceType2;
+ typedef typename T::second FinalFaceType;
+
+ {
+ // create face
+
+ Name commandName("/localhost/nfd/faces");
+ commandName.append("create");
+ commandName.append(FaceType1().getParameters().wireEncode());
+
+ shared_ptr<Interest> command(make_shared<Interest>(commandName));
+ this->keyChain.sign(*command);
+
+ this->node1.face->receive(*command);
+ this->advanceClocks(time::milliseconds(1), 10);
+ }
+
+ //
+ {
+ // re-create face (= change face persistency)
+
+ Name commandName("/localhost/nfd/faces");
+ commandName.append("create");
+ commandName.append(FaceType2().getParameters().wireEncode());
+
+ shared_ptr<Interest> command(make_shared<Interest>(commandName));
+ this->keyChain.sign(*command);
+
+ bool hasCallbackFired = false;
+ this->node1.face->onSendData.connect([this, command, &hasCallbackFired] (const Data& response) {
+ if (!command->getName().isPrefixOf(response.getName())) {
+ return;
+ }
+
+ ControlResponse actual(response.getContent().blockFromValue());
+ BOOST_REQUIRE_EQUAL(actual.getCode(), 200);
+
+ ControlParameters expectedParams(FinalFaceType().getParameters());
+ ControlParameters actualParams(actual.getBody());
+ BOOST_CHECK_EQUAL(expectedParams.getFacePersistency(), actualParams.getFacePersistency());
+
+ hasCallbackFired = true;
+ });
+
+ this->node1.face->receive(*command);
+ this->advanceClocks(time::milliseconds(1), 10);
+
+ BOOST_CHECK(hasCallbackFired);
+ }
+}
+
+
+class UdpFace
+{
+public:
+ ControlParameters
+ getParameters()
+ {
+ return ControlParameters()
+ .setUri("udp4://127.0.0.1:16363")
+ .setFacePersistency(ndn::nfd::FACE_PERSISTENCY_PERSISTENT);
+ }
+};
+
+
+// Note that the transitions from on-demand TcpFace are intentionally not tested.
+// On-demand TcpFace has a remote endpoint with a randomized port number. Normal face
+// creation operations will not need to create a face toward a remote port not listened by
+// a channel.
+
+typedef mpl::vector<mpl::pair<UdpFace, UdpFacePersistent>,
+ mpl::pair<UdpFace, UdpFacePermanent>> OnDemandFaceTransitions;
+
+// need a slightly different logic to test transitions from OnDemand state
+BOOST_FIXTURE_TEST_CASE_TEMPLATE(ExistingFaceOnDemand, T, OnDemandFaceTransitions, FaceManagerFixture)
+{
+ typedef typename T::first OtherNodeFace;
+ typedef typename T::second FaceType;
+
+ {
+ // create on-demand face
+
+ Name commandName("/localhost/nfd/faces");
+ commandName.append("create");
+ commandName.append(OtherNodeFace().getParameters().wireEncode());
+
+ shared_ptr<Interest> command(make_shared<Interest>(commandName));
+ this->keyChain.sign(*command);
+
+ ndn::util::signal::ScopedConnection connection =
+ this->node2.face->onSendData.connect([this, command] (const Data& response) {
+ if (!command->getName().isPrefixOf(response.getName())) {
+ return;
+ }
+
+ ControlResponse controlResponse(response.getContent().blockFromValue());
+ BOOST_REQUIRE_EQUAL(controlResponse.getText(), "OK");
+ BOOST_REQUIRE_EQUAL(controlResponse.getCode(), 200);
+ uint64_t faceId = ControlParameters(controlResponse.getBody()).getFaceId();
+ auto face = this->node2.forwarder.getFace(static_cast<FaceId>(faceId));
+
+ // to force creation of on-demand face
+ auto dummyInterest = make_shared<Interest>("/hello/world");
+ face->sendInterest(*dummyInterest);
+ });
+
+ this->node2.face->receive(*command);
+ this->advanceClocks(time::milliseconds(1), 10);
+ }
+
+ // make sure there is on-demand face
+ bool onDemandFaceFound = false;
+ FaceUri onDemandFaceUri(FaceType().getParameters().getUri());
+ for (auto face : this->node1.forwarder.getFaceTable()) {
+ if (face->getRemoteUri() == onDemandFaceUri) {
+ onDemandFaceFound = true;
+ break;
+ }
+ }
+ BOOST_REQUIRE(onDemandFaceFound);
+
+ //
+ {
+ // re-create face (= change face persistency)
+
+ Name commandName("/localhost/nfd/faces");
+ commandName.append("create");
+ commandName.append(FaceType().getParameters().wireEncode());
+
+ shared_ptr<Interest> command(make_shared<Interest>(commandName));
+ this->keyChain.sign(*command);
+
+ bool hasCallbackFired = false;
+ this->node1.face->onSendData.connect([this, command, &hasCallbackFired] (const Data& response) {
+ if (!command->getName().isPrefixOf(response.getName())) {
+ return;
+ }
+
+ ControlResponse actual(response.getContent().blockFromValue());
+ BOOST_REQUIRE_EQUAL(actual.getCode(), 200);
+
+ ControlParameters expectedParams(FaceType().getParameters());
+ ControlParameters actualParams(actual.getBody());
+ BOOST_CHECK_EQUAL(expectedParams.getFacePersistency(), actualParams.getFacePersistency());
+
+ hasCallbackFired = true;
+ });
+
+ this->node1.face->receive(*command);
+ this->advanceClocks(time::milliseconds(1), 10);
+
+ BOOST_CHECK(hasCallbackFired);
+ }
+}
+
+BOOST_AUTO_TEST_SUITE_END() // CreateFace
+BOOST_AUTO_TEST_SUITE_END() // TestFaceManager
+BOOST_AUTO_TEST_SUITE_END() // Mgmt
+
+} // tests
+} // nfd
diff --git a/tests/daemon/mgmt/face-manager-process-config.t.cpp b/tests/daemon/mgmt/face-manager-process-config.t.cpp
new file mode 100644
index 0000000..9463ac4
--- /dev/null
+++ b/tests/daemon/mgmt/face-manager-process-config.t.cpp
@@ -0,0 +1,438 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014-2015, 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,
+ * The University of Memphis.
+ *
+ * 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 "face/udp-factory.hpp"
+
+#ifdef HAVE_LIBPCAP
+#include "face/ethernet-factory.hpp"
+#endif // HAVE_LIBPCAP
+
+#include "manager-common-fixture.hpp"
+
+namespace nfd {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Mgmt)
+BOOST_AUTO_TEST_SUITE(TestFaceManager)
+
+class FaceManagerProcessConfigFixture : public ManagerCommonFixture
+{
+public:
+ FaceManagerProcessConfigFixture()
+ : m_manager(m_forwarder.getFaceTable(), m_dispatcher, m_validator)
+ {
+ m_manager.setConfigFile(m_config);
+ }
+
+public:
+ void
+ parseConfig(const std::string& type, bool isDryRun)
+ {
+ m_config.parse(type, isDryRun, "test-config");
+ }
+
+protected:
+ FaceManager m_manager;
+ ConfigFile m_config;
+};
+
+
+BOOST_FIXTURE_TEST_SUITE(ProcessConfig, FaceManagerProcessConfigFixture)
+
+#ifdef HAVE_UNIX_SOCKETS
+
+BOOST_AUTO_TEST_CASE(ProcessSectionUnix)
+{
+ const std::string CONFIG =
+ "face_system\n"
+ "{\n"
+ " unix\n"
+ " {\n"
+ " path /tmp/nfd.sock\n"
+ " }\n"
+ "}\n";
+ BOOST_CHECK_NO_THROW(parseConfig(CONFIG, true));
+ BOOST_CHECK_NO_THROW(parseConfig(CONFIG, false));
+}
+
+BOOST_AUTO_TEST_CASE(ProcessSectionUnixUnknownOption)
+{
+ const std::string CONFIG =
+ "face_system\n"
+ "{\n"
+ " unix\n"
+ " {\n"
+ " hello\n"
+ " }\n"
+ "}\n";
+ BOOST_CHECK_THROW(parseConfig(CONFIG, true), ConfigFile::Error);
+ BOOST_CHECK_THROW(parseConfig(CONFIG, false), ConfigFile::Error);
+}
+
+#endif // HAVE_UNIX_SOCKETS
+
+BOOST_AUTO_TEST_CASE(ProcessSectionTcp)
+{
+ const std::string CONFIG =
+ "face_system\n"
+ "{\n"
+ " tcp\n"
+ " {\n"
+ " listen yes\n"
+ " port 16363\n"
+ " enable_v4 yes\n"
+ " enable_v6 yes\n"
+ " }\n"
+ "}\n";
+ BOOST_CHECK_NO_THROW(parseConfig(CONFIG, true));
+ BOOST_CHECK_NO_THROW(parseConfig(CONFIG, false));
+}
+
+BOOST_AUTO_TEST_CASE(ProcessSectionTcpBadListen)
+{
+ const std::string CONFIG =
+ "face_system\n"
+ "{\n"
+ " tcp\n"
+ " {\n"
+ " listen hello\n"
+ " }\n"
+ "}\n";
+
+ BOOST_CHECK_THROW(parseConfig(CONFIG, true), ConfigFile::Error);
+ BOOST_CHECK_THROW(parseConfig(CONFIG, false), ConfigFile::Error);
+}
+
+BOOST_AUTO_TEST_CASE(ProcessSectionTcpChannelsDisabled)
+{
+ 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_THROW(parseConfig(CONFIG, true), ConfigFile::Error);
+ BOOST_CHECK_THROW(parseConfig(CONFIG, false), ConfigFile::Error);
+}
+
+BOOST_AUTO_TEST_CASE(ProcessSectionTcpUnknownOption)
+{
+ const std::string CONFIG =
+ "face_system\n"
+ "{\n"
+ " tcp\n"
+ " {\n"
+ " hello\n"
+ " }\n"
+ "}\n";
+ BOOST_CHECK_THROW(parseConfig(CONFIG, true), ConfigFile::Error);
+ BOOST_CHECK_THROW(parseConfig(CONFIG, false), ConfigFile::Error);
+}
+
+BOOST_AUTO_TEST_CASE(ProcessSectionUdp)
+{
+ 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, true));
+ BOOST_CHECK_NO_THROW(parseConfig(CONFIG, false));
+}
+
+BOOST_AUTO_TEST_CASE(ProcessSectionUdpBadIdleTimeout)
+{
+ const std::string CONFIG =
+ "face_system\n"
+ "{\n"
+ " udp\n"
+ " {\n"
+ " idle_timeout hello\n"
+ " }\n"
+ "}\n";
+ BOOST_CHECK_THROW(parseConfig(CONFIG, true), ConfigFile::Error);
+ BOOST_CHECK_THROW(parseConfig(CONFIG, false), ConfigFile::Error);
+}
+
+BOOST_AUTO_TEST_CASE(ProcessSectionUdpBadMcast)
+{
+ const std::string CONFIG =
+ "face_system\n"
+ "{\n"
+ " udp\n"
+ " {\n"
+ " mcast hello\n"
+ " }\n"
+ "}\n";
+ BOOST_CHECK_THROW(parseConfig(CONFIG, true), ConfigFile::Error);
+ BOOST_CHECK_THROW(parseConfig(CONFIG, false), ConfigFile::Error);
+}
+
+BOOST_AUTO_TEST_CASE(ProcessSectionUdpBadMcastGroup)
+{
+ 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_THROW(parseConfig(CONFIG, true), ConfigFile::Error);
+ BOOST_CHECK_THROW(parseConfig(CONFIG, false), ConfigFile::Error);
+}
+
+BOOST_AUTO_TEST_CASE(ProcessSectionUdpBadMcastGroupV6)
+{
+ 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_THROW(parseConfig(CONFIG, true), ConfigFile::Error);
+ BOOST_CHECK_THROW(parseConfig(CONFIG, false), ConfigFile::Error);
+}
+
+BOOST_AUTO_TEST_CASE(ProcessSectionUdpChannelsDisabled)
+{
+ 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_THROW(parseConfig(CONFIG, true), ConfigFile::Error);
+ BOOST_CHECK_THROW(parseConfig(CONFIG, false), ConfigFile::Error);
+}
+
+BOOST_AUTO_TEST_CASE(ProcessSectionUdpConflictingMcast)
+{
+ 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_THROW(parseConfig(CONFIG, true), ConfigFile::Error);
+ BOOST_CHECK_THROW(parseConfig(CONFIG, false), ConfigFile::Error);
+}
+
+
+
+BOOST_AUTO_TEST_CASE(ProcessSectionUdpUnknownOption)
+{
+ const std::string CONFIG =
+ "face_system\n"
+ "{\n"
+ " udp\n"
+ " {\n"
+ " hello\n"
+ " }\n"
+ "}\n";
+ BOOST_CHECK_THROW(parseConfig(CONFIG, true), ConfigFile::Error);
+ BOOST_CHECK_THROW(parseConfig(CONFIG, false), ConfigFile::Error);
+}
+
+
+BOOST_AUTO_TEST_CASE(ProcessSectionUdpMulticastReinit)
+{
+ const std::string CONFIG_WITH_MCAST =
+ "face_system\n"
+ "{\n"
+ " udp\n"
+ " {\n"
+ " mcast yes\n"
+ " }\n"
+ "}\n";
+ BOOST_CHECK_NO_THROW(parseConfig(CONFIG_WITH_MCAST, false));
+
+ BOOST_REQUIRE(m_manager.m_factories.find("udp") != m_manager.m_factories.end());
+ auto factory = dynamic_pointer_cast<UdpFactory>(m_manager.m_factories.find("udp")->second);
+ BOOST_REQUIRE(factory != nullptr);
+
+ if (factory->getMulticastFaces().size() == 0) {
+ BOOST_TEST_MESSAGE("Destroying multicast faces is not tested because "
+ "no UDP multicast faces are available");
+ return;
+ }
+ BOOST_CHECK_GT(factory->getMulticastFaces().size(), 0);
+
+ const std::string CONFIG_WITHOUT_MCAST =
+ "face_system\n"
+ "{\n"
+ " udp\n"
+ " {\n"
+ " mcast no\n"
+ " }\n"
+ "}\n";
+ BOOST_CHECK_NO_THROW(parseConfig(CONFIG_WITHOUT_MCAST, false));
+ BOOST_CHECK_EQUAL(factory->getMulticastFaces().size(), 0);
+}
+
+#ifdef HAVE_LIBPCAP
+
+BOOST_AUTO_TEST_CASE(ProcessSectionEther)
+{
+
+ 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_CHECK_NO_THROW(parseConfig(CONFIG, false));
+}
+
+BOOST_AUTO_TEST_CASE(ProcessSectionEtherBadMcast)
+{
+ const std::string CONFIG =
+ "face_system\n"
+ "{\n"
+ " ether\n"
+ " {\n"
+ " mcast hello\n"
+ " }\n"
+ "}\n";
+ BOOST_CHECK_THROW(parseConfig(CONFIG, true), ConfigFile::Error);
+ BOOST_CHECK_THROW(parseConfig(CONFIG, false), ConfigFile::Error);
+}
+
+BOOST_AUTO_TEST_CASE(ProcessSectionEtherBadMcastGroup)
+{
+ const std::string CONFIG =
+ "face_system\n"
+ "{\n"
+ " ether\n"
+ " {\n"
+ " mcast yes\n"
+ " mcast_group\n"
+ " }\n"
+ "}\n";
+ BOOST_CHECK_THROW(parseConfig(CONFIG, true), ConfigFile::Error);
+ BOOST_CHECK_THROW(parseConfig(CONFIG, false), ConfigFile::Error);
+}
+
+BOOST_AUTO_TEST_CASE(ProcessSectionEtherUnknownOption)
+{
+ const std::string CONFIG =
+ "face_system\n"
+ "{\n"
+ " ether\n"
+ " {\n"
+ " hello\n"
+ " }\n"
+ "}\n";
+ BOOST_CHECK_THROW(parseConfig(CONFIG, true), ConfigFile::Error);
+ BOOST_CHECK_THROW(parseConfig(CONFIG, false), ConfigFile::Error);
+}
+
+BOOST_AUTO_TEST_CASE(ProcessSectionEtherMulticastReinit)
+{
+ const std::string CONFIG_WITH_MCAST =
+ "face_system\n"
+ "{\n"
+ " ether\n"
+ " {\n"
+ " mcast yes\n"
+ " }\n"
+ "}\n";
+ BOOST_CHECK_NO_THROW(parseConfig(CONFIG_WITH_MCAST, false));
+
+ BOOST_REQUIRE(m_manager.m_factories.find("ether") != m_manager.m_factories.end());
+ auto factory = dynamic_pointer_cast<EthernetFactory>(m_manager.m_factories.find("ether")->second);
+ BOOST_REQUIRE(factory != nullptr);
+
+ if (factory->getMulticastFaces().size() == 0) {
+ BOOST_TEST_MESSAGE("Destroying multicast faces is not tested because "
+ "no Ethernet multicast faces are available");
+ return;
+ }
+ BOOST_CHECK_GT(factory->getMulticastFaces().size(), 0);
+
+ const std::string CONFIG_WITHOUT_MCAST =
+ "face_system\n"
+ "{\n"
+ " ether\n"
+ " {\n"
+ " mcast no\n"
+ " }\n"
+ "}\n";
+ BOOST_CHECK_NO_THROW(parseConfig(CONFIG_WITHOUT_MCAST, false));
+ BOOST_CHECK_EQUAL(factory->getMulticastFaces().size(), 0);
+}
+
+#endif // HAVE_LIBPCAP
+
+BOOST_AUTO_TEST_SUITE_END() // ProcessConfig
+BOOST_AUTO_TEST_SUITE_END() // TestFaceManager
+BOOST_AUTO_TEST_SUITE_END() // Mgmt
+
+} // namespace tests
+} // namespace nfd
diff --git a/tests/daemon/mgmt/face-manager.t.cpp b/tests/daemon/mgmt/face-manager.t.cpp
new file mode 100644
index 0000000..db24029
--- /dev/null
+++ b/tests/daemon/mgmt/face-manager.t.cpp
@@ -0,0 +1,449 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014-2015, 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,
+ * The University of Memphis.
+ *
+ * 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 "manager-common-fixture.hpp"
+#include "../face/dummy-face.hpp"
+#include "face/tcp-factory.hpp"
+#include "face/udp-factory.hpp"
+
+#include <ndn-cxx/util/random.hpp>
+#include <ndn-cxx/encoding/tlv.hpp>
+#include <ndn-cxx/management/nfd-channel-status.hpp>
+#include <ndn-cxx/management/nfd-face-event-notification.hpp>
+
+namespace nfd {
+namespace tests {
+
+class FaceManagerFixture : public ManagerCommonFixture
+{
+public:
+ FaceManagerFixture()
+ : m_faceTable(m_forwarder.getFaceTable())
+ , m_manager(m_faceTable, m_dispatcher, m_validator)
+ {
+ setTopPrefixAndPrivilege("/localhost/nfd", "faces");
+ }
+
+public:
+ template<typename Face>
+ shared_ptr<Face>
+ addFace(bool wantRemoveLastNotification = false)
+ {
+ auto face = make_shared<Face>();
+ m_faceTable.add(face);
+ advanceClocks(time::milliseconds(1), 10); // wait for notification posted
+ if (wantRemoveLastNotification) {
+ m_responses.pop_back();
+ }
+ return face;
+ }
+
+protected:
+ FaceTable& m_faceTable;
+ FaceManager m_manager;
+};
+
+BOOST_FIXTURE_TEST_SUITE(Mgmt, FaceManagerFixture)
+BOOST_AUTO_TEST_SUITE(TestFaceManager)
+
+BOOST_AUTO_TEST_SUITE(DestroyFace)
+
+BOOST_AUTO_TEST_CASE(Existing)
+{
+ auto addedFace = addFace<DummyFace>(true); // clear notification for creation
+
+ auto parameters = ControlParameters().setFaceId(addedFace->getId());
+ auto command = makeControlCommandRequest("/localhost/nfd/faces/destroy", parameters);
+
+ receiveInterest(command);
+
+ BOOST_REQUIRE_EQUAL(m_responses.size(), 2); // one response and one notification
+ // notification is already tested, so ignore it
+
+ BOOST_CHECK_EQUAL(checkResponse(1, command->getName(), makeResponse(200, "OK", parameters)),
+ CheckResponseResult::OK);
+
+ BOOST_CHECK_EQUAL(addedFace->getId(), -1);
+}
+
+BOOST_AUTO_TEST_CASE(NonExisting)
+{
+ auto parameters = ControlParameters().setFaceId(65535);
+ auto command = makeControlCommandRequest("/localhost/nfd/faces/destroy", parameters);
+
+ receiveInterest(command);
+
+ BOOST_REQUIRE_EQUAL(m_responses.size(), 1);
+
+ BOOST_CHECK_EQUAL(checkResponse(0, command->getName(), makeResponse(200, "OK", parameters)),
+ CheckResponseResult::OK);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // DestroyFace
+
+BOOST_AUTO_TEST_CASE(FaceEvents)
+{
+ auto addedFace = addFace<DummyFace>(); // trigger FACE_EVENT_CREATED notification
+ BOOST_CHECK_NE(addedFace->getId(), -1);
+ int64_t faceId = addedFace->getId();
+
+ // check notification
+ {
+ Block payload;
+ ndn::nfd::FaceEventNotification notification;
+ BOOST_REQUIRE_EQUAL(m_responses.size(), 1);
+ BOOST_CHECK_NO_THROW(payload = m_responses[0].getContent().blockFromValue());
+ BOOST_CHECK_EQUAL(payload.type(), ndn::tlv::nfd::FaceEventNotification);
+ BOOST_CHECK_NO_THROW(notification.wireDecode(payload));
+ BOOST_CHECK_EQUAL(notification.getKind(), ndn::nfd::FACE_EVENT_CREATED);
+ BOOST_CHECK_EQUAL(notification.getFaceId(), faceId);
+ BOOST_CHECK_EQUAL(notification.getRemoteUri(), addedFace->getRemoteUri().toString());
+ BOOST_CHECK_EQUAL(notification.getLocalUri(), addedFace->getLocalUri().toString());
+ BOOST_CHECK_EQUAL(notification.getFaceScope(), ndn::nfd::FACE_SCOPE_NON_LOCAL);
+ BOOST_CHECK_EQUAL(notification.getFacePersistency(), ndn::nfd::FACE_PERSISTENCY_PERSISTENT);
+ BOOST_CHECK_EQUAL(notification.getLinkType(), ndn::nfd::LinkType::LINK_TYPE_POINT_TO_POINT);
+ }
+
+ addedFace->close(); // trigger FaceDestroy FACE_EVENT_DESTROYED
+ advanceClocks(time::milliseconds(1), 10);
+
+ // check notification
+ {
+ Block payload;
+ ndn::nfd::FaceEventNotification notification;
+ BOOST_REQUIRE_EQUAL(m_responses.size(), 2);
+ BOOST_CHECK_NO_THROW(payload = m_responses[1].getContent().blockFromValue());
+ BOOST_CHECK_EQUAL(payload.type(), ndn::tlv::nfd::FaceEventNotification);
+ BOOST_CHECK_NO_THROW(notification.wireDecode(payload));
+ BOOST_CHECK_EQUAL(notification.getKind(), ndn::nfd::FACE_EVENT_DESTROYED);
+ BOOST_CHECK_EQUAL(notification.getFaceId(), faceId);
+ BOOST_CHECK_EQUAL(notification.getRemoteUri(), addedFace->getRemoteUri().toString());
+ BOOST_CHECK_EQUAL(notification.getLocalUri(), addedFace->getLocalUri().toString());
+ BOOST_CHECK_EQUAL(notification.getFaceScope(), ndn::nfd::FACE_SCOPE_NON_LOCAL);
+ BOOST_CHECK_EQUAL(notification.getFacePersistency(), ndn::nfd::FACE_PERSISTENCY_PERSISTENT);
+ BOOST_CHECK_EQUAL(notification.getLinkType(), ndn::nfd::LinkType::LINK_TYPE_POINT_TO_POINT);
+ }
+ BOOST_CHECK_EQUAL(addedFace->getId(), -1);
+}
+
+BOOST_AUTO_TEST_CASE(EnableDisableLocalControl)
+{
+ auto nonLocalFace = addFace<DummyFace>(true); // clear notification
+ auto localFace = addFace<DummyLocalFace>(true); // clear notification
+ BOOST_CHECK(localFace->isLocal());
+ BOOST_CHECK(!nonLocalFace->isLocal());
+
+ std::vector<Name> commandNames;
+ auto testLocalControl = [&] (const Name& name, const ControlParameters& params, uint64_t faceId) {
+ auto command = makeControlCommandRequest(name, params,
+ [faceId] (shared_ptr<Interest> interest) {
+ interest->setIncomingFaceId(faceId);
+ });
+ receiveInterest(command);
+ commandNames.push_back(command->getName());
+ };
+
+ auto paramsF = ControlParameters().setLocalControlFeature(LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID);
+ auto paramsN = ControlParameters().setLocalControlFeature(LOCAL_CONTROL_FEATURE_NEXT_HOP_FACE_ID);
+
+ // non-existing face: 0~3
+ testLocalControl("/localhost/nfd/faces/enable-local-control", paramsF, FACEID_NULL);
+ testLocalControl("/localhost/nfd/faces/disable-local-control", paramsF, FACEID_NULL);
+ testLocalControl("/localhost/nfd/faces/enable-local-control", paramsN, FACEID_NULL);
+ testLocalControl("/localhost/nfd/faces/disable-local-control", paramsN, FACEID_NULL);
+
+ // non-local face: 4~7
+ testLocalControl("/localhost/nfd/faces/enable-local-control", paramsF, nonLocalFace->getId());
+ testLocalControl("/localhost/nfd/faces/disable-local-control", paramsF, nonLocalFace->getId());
+ testLocalControl("/localhost/nfd/faces/enable-local-control", paramsN, nonLocalFace->getId());
+ testLocalControl("/localhost/nfd/faces/disable-local-control", paramsN, nonLocalFace->getId());
+
+ // enableLocalControl for Incoming FaceId on existing local face:
+ testLocalControl("/localhost/nfd/faces/enable-local-control", paramsF, localFace->getId()); // 8
+ BOOST_CHECK(localFace->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID));
+ BOOST_CHECK(!localFace->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_NEXT_HOP_FACE_ID));
+
+ // disableLocalControl for Incoming FaceId on existing local face
+ testLocalControl("/localhost/nfd/faces/disable-local-control", paramsF, localFace->getId()); // 9
+ BOOST_CHECK(!localFace->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID));
+ BOOST_CHECK(!localFace->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_NEXT_HOP_FACE_ID));
+
+ // enableLocalControl for NextHop ID on existing local face
+ testLocalControl("/localhost/nfd/faces/enable-local-control", paramsN, localFace->getId()); // 10
+ BOOST_CHECK(!localFace->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID));
+ BOOST_CHECK(localFace->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_NEXT_HOP_FACE_ID));
+
+ // disableLocalControl for NextHop ID on existing local face
+ testLocalControl("/localhost/nfd/faces/disable-local-control", paramsN, localFace->getId()); // 11
+ BOOST_CHECK(!localFace->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID));
+ BOOST_CHECK(!localFace->isLocalControlHeaderEnabled(LOCAL_CONTROL_FEATURE_NEXT_HOP_FACE_ID));
+
+ // check responses
+ BOOST_REQUIRE_EQUAL(m_responses.size(), 12);
+ BOOST_CHECK_EQUAL(checkResponse(0, commandNames[0], ControlResponse(410, "Face not found")),
+ CheckResponseResult::OK);
+ BOOST_CHECK_EQUAL(checkResponse(1, commandNames[1], ControlResponse(410, "Face not found")),
+ CheckResponseResult::OK);
+ BOOST_CHECK_EQUAL(checkResponse(2, commandNames[2], ControlResponse(410, "Face not found")),
+ CheckResponseResult::OK);
+ BOOST_CHECK_EQUAL(checkResponse(3, commandNames[3], ControlResponse(410, "Face not found")),
+ CheckResponseResult::OK);
+ BOOST_CHECK_EQUAL(checkResponse(4, commandNames[4], ControlResponse(412, "Face is non-local")),
+ CheckResponseResult::OK);
+ BOOST_CHECK_EQUAL(checkResponse(5, commandNames[5], ControlResponse(412, "Face is non-local")),
+ CheckResponseResult::OK);
+ BOOST_CHECK_EQUAL(checkResponse(6, commandNames[6], ControlResponse(412, "Face is non-local")),
+ CheckResponseResult::OK);
+ BOOST_CHECK_EQUAL(checkResponse(7, commandNames[7], ControlResponse(412, "Face is non-local")),
+ CheckResponseResult::OK);
+ BOOST_CHECK_EQUAL(checkResponse(8, commandNames[8], makeResponse(200, "OK", paramsF)),
+ CheckResponseResult::OK);
+ BOOST_CHECK_EQUAL(checkResponse(9, commandNames[9], makeResponse(200, "OK", paramsF)),
+ CheckResponseResult::OK);
+ BOOST_CHECK_EQUAL(checkResponse(10, commandNames[10], makeResponse(200, "OK", paramsN)),
+ CheckResponseResult::OK);
+ BOOST_CHECK_EQUAL(checkResponse(11, commandNames[11], makeResponse(200, "OK", paramsN)),
+ CheckResponseResult::OK);
+}
+
+class TestFace : public DummyFace
+{
+public:
+ explicit
+ TestFace(const std::string& uri = "test://")
+ : DummyFace(uri, uri)
+ {
+ getMutableCounters().getNInInterests().set(ndn::random::generateWord64());
+ getMutableCounters().getNInDatas().set(ndn::random::generateWord64());
+ getMutableCounters().getNOutInterests().set(ndn::random::generateWord64());
+ getMutableCounters().getNOutDatas().set(ndn::random::generateWord64());
+ getMutableCounters().getNInBytes().set(ndn::random::generateWord64());
+ getMutableCounters().getNOutBytes().set(ndn::random::generateWord64());
+ }
+};
+
+// @todo Refactor when ndn::nfd::FaceStatus implementes operator!= and operator<<
+class FaceStatus : public ndn::nfd::FaceStatus
+{
+public:
+ FaceStatus(const ndn::nfd::FaceStatus& s)
+ : ndn::nfd::FaceStatus(s)
+ {
+ }
+};
+
+bool
+operator!=(const FaceStatus& left, const FaceStatus& right)
+{
+ return left.getRemoteUri() != right.getRemoteUri() ||
+ left.getLocalUri() != right.getLocalUri() ||
+ left.getFaceScope() != right.getFaceScope() ||
+ left.getFacePersistency() != right.getFacePersistency() ||
+ left.getLinkType() != right.getLinkType() ||
+ left.getNInInterests() != right.getNInInterests() ||
+ left.getNInDatas() != right.getNInDatas() ||
+ left.getNOutInterests() != right.getNOutInterests() ||
+ left.getNOutDatas() != right.getNOutDatas() ||
+ left.getNInBytes() != right.getNInBytes() ||
+ left.getNOutBytes() != right.getNOutBytes();
+}
+
+std::ostream&
+operator<<(std::ostream &os, const FaceStatus& status)
+{
+ os << "[" << status.getRemoteUri() << ", "
+ << status.getLocalUri() << ", "
+ << status.getFacePersistency() << ", "
+ << status.getLinkType() << ", "
+ << status.getNInInterests() << ", "
+ << status.getNInDatas() << ", "
+ << status.getNOutInterests() << ", "
+ << status.getNOutDatas() << ", "
+ << status.getNInBytes() << ", "
+ << status.getNOutBytes() << "]";
+ return os;
+}
+
+BOOST_AUTO_TEST_CASE(FaceDataset)
+{
+ size_t nEntries = 303;
+ for (size_t i = 0 ; i < nEntries ; i ++) {
+ addFace<TestFace>(true);
+ }
+
+ receiveInterest(makeInterest("/localhost/nfd/faces/list"));
+
+ Block content;
+ BOOST_CHECK_NO_THROW(content = concatenateResponses());
+ BOOST_CHECK_NO_THROW(content.parse());
+ BOOST_REQUIRE_EQUAL(content.elements().size(), nEntries);
+
+ std::vector<FaceStatus> expectedStatuses, receivedStatuses;
+ std::set<FaceId> faceIds;
+ for (size_t idx = 0; idx < nEntries; ++idx) {
+ BOOST_TEST_MESSAGE("processing element: " << idx);
+
+ ndn::nfd::FaceStatus decodedStatus;
+ BOOST_REQUIRE_NO_THROW(decodedStatus.wireDecode(content.elements()[idx]));
+ BOOST_REQUIRE(m_faceTable.get(decodedStatus.getFaceId()) != nullptr);
+ faceIds.insert(decodedStatus.getFaceId());
+ receivedStatuses.push_back(decodedStatus);
+ expectedStatuses.push_back(m_faceTable.get(decodedStatus.getFaceId())->getFaceStatus());
+ }
+
+ BOOST_CHECK_EQUAL(faceIds.size(), nEntries);
+ BOOST_CHECK_EQUAL_COLLECTIONS(receivedStatuses.begin(), receivedStatuses.end(),
+ expectedStatuses.begin(), expectedStatuses.end());
+}
+
+BOOST_AUTO_TEST_CASE(FaceQuery)
+{
+ auto face1 = addFace<DummyFace>(true); // dummy://
+ auto face2 = addFace<DummyLocalFace>(true); // dummy://, local
+ auto face3 = addFace<TestFace>(true); // test://
+
+ auto generateQueryName = [] (const ndn::nfd::FaceQueryFilter& filter) {
+ return Name("/localhost/nfd/faces/query").append(filter.wireEncode());
+ };
+
+ auto querySchemeName =
+ generateQueryName(ndn::nfd::FaceQueryFilter().setUriScheme("dummy"));
+ auto queryIdName =
+ generateQueryName(ndn::nfd::FaceQueryFilter().setFaceId(face1->getId()));
+ auto queryScopeName =
+ generateQueryName(ndn::nfd::FaceQueryFilter().setFaceScope(ndn::nfd::FACE_SCOPE_NON_LOCAL));
+ auto invalidQueryName =
+ Name("/localhost/nfd/faces/query").append(ndn::makeStringBlock(tlv::Content, "invalid"));
+
+ receiveInterest(makeInterest(querySchemeName)); // face1 and face2 expected
+ receiveInterest(makeInterest(queryIdName)); // face1 expected
+ receiveInterest(makeInterest(queryScopeName)); // face1 and face3 expected
+ receiveInterest(makeInterest(invalidQueryName)); // nack expected
+
+ BOOST_REQUIRE_EQUAL(m_responses.size(), 4);
+
+ Block content;
+ ndn::nfd::FaceStatus status;
+
+ content = m_responses[0].getContent();
+ BOOST_CHECK_NO_THROW(content.parse());
+ BOOST_CHECK_EQUAL(content.elements().size(), 2); // face1 and face2
+ BOOST_CHECK_NO_THROW(status.wireDecode(content.elements()[0]));
+ BOOST_CHECK_EQUAL(face1->getId(), status.getFaceId());
+ BOOST_CHECK_NO_THROW(status.wireDecode(content.elements()[1]));
+ BOOST_CHECK_EQUAL(face2->getId(), status.getFaceId());
+
+ content = m_responses[1].getContent();
+ BOOST_CHECK_NO_THROW(content.parse());
+ BOOST_CHECK_EQUAL(content.elements().size(), 1); // face1
+ BOOST_CHECK_NO_THROW(status.wireDecode(content.elements()[0]));
+ BOOST_CHECK_EQUAL(face1->getId(), status.getFaceId());
+
+ content = m_responses[2].getContent();
+ BOOST_CHECK_NO_THROW(content.parse());
+ BOOST_CHECK_EQUAL(content.elements().size(), 2); // face1 and face3
+ BOOST_CHECK_NO_THROW(status.wireDecode(content.elements()[0]));
+ BOOST_CHECK_EQUAL(face1->getId(), status.getFaceId());
+ BOOST_CHECK_NO_THROW(status.wireDecode(content.elements()[1]));
+ BOOST_CHECK_EQUAL(face3->getId(), status.getFaceId());
+
+ ControlResponse expectedResponse(400, "malformed filter"); // nack, 400, malformed filter
+ BOOST_CHECK_EQUAL(checkResponse(3, invalidQueryName, expectedResponse, tlv::ContentType_Nack),
+ CheckResponseResult::OK);
+}
+
+class TestChannel : public Channel
+{
+public:
+ TestChannel(const std::string& uri)
+ {
+ setUri(FaceUri(uri));
+ }
+};
+
+class TestProtocolFactory : public ProtocolFactory
+{
+public:
+ virtual void
+ createFace(const FaceUri& uri,
+ ndn::nfd::FacePersistency persistency,
+ const FaceCreatedCallback& onCreated,
+ const FaceConnectFailedCallback& onConnectFailed) DECL_OVERRIDE
+ {
+ }
+
+ virtual std::list<shared_ptr<const Channel>>
+ getChannels() const DECL_OVERRIDE
+ {
+ return m_channels;
+ }
+
+public:
+ shared_ptr<TestChannel>
+ addChannel(const std::string& channelUri)
+ {
+ auto channel = make_shared<TestChannel>(channelUri);
+ m_channels.push_back(channel);
+ return channel;
+ }
+
+private:
+ std::list<shared_ptr<const Channel> > m_channels;
+};
+
+BOOST_AUTO_TEST_CASE(ChannelDataset)
+{
+ auto factory = make_shared<TestProtocolFactory>();
+ m_manager.m_factories["test"] = factory;
+
+ std::map<std::string, shared_ptr<TestChannel>> addedChannels;
+ size_t nEntries = 404;
+ for (size_t i = 0 ; i < nEntries ; i ++) {
+ auto channel = factory->addChannel("test" + boost::lexical_cast<std::string>(i) + "://");
+ addedChannels[channel->getUri().toString()] = channel;
+ }
+
+ receiveInterest(makeInterest("/localhost/nfd/faces/channels"));
+
+ Block content;
+ BOOST_CHECK_NO_THROW(content = concatenateResponses());
+ BOOST_CHECK_NO_THROW(content.parse());
+ BOOST_REQUIRE_EQUAL(content.elements().size(), nEntries);
+
+ for (size_t idx = 0; idx < nEntries; ++idx) {
+ BOOST_TEST_MESSAGE("processing element: " << idx);
+
+ ndn::nfd::ChannelStatus decodedStatus;
+ BOOST_CHECK_NO_THROW(decodedStatus.wireDecode(content.elements()[idx]));
+ BOOST_CHECK(addedChannels.find(decodedStatus.getLocalUri()) != addedChannels.end());
+ }
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestFaceManager
+BOOST_AUTO_TEST_SUITE_END() // Mgmt
+
+} // namespace tests
+} // namespace nfd