mgmt refactoring: FaceManager
Change-Id: I292a3e3d702746d6e946a2844e6f88f64cd711a4
Refs: #2107
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