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/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