Rename 'tests/unit-tests' directory to 'tests/unit'
Change-Id: I78ea29938259fac288781bed12fb2399ac7eba26
diff --git a/tests/unit/mgmt/control-response.t.cpp b/tests/unit/mgmt/control-response.t.cpp
new file mode 100644
index 0000000..86aa780
--- /dev/null
+++ b/tests/unit/mgmt/control-response.t.cpp
@@ -0,0 +1,61 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library 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 Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "mgmt/control-response.hpp"
+
+#include "boost-test.hpp"
+
+namespace ndn {
+namespace mgmt {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Mgmt)
+BOOST_AUTO_TEST_SUITE(TestControlResponse)
+
+static const uint8_t WIRE[] = {
+ 0x65, 0x17, // ControlResponse
+ 0x66, 0x02, // StatusCode
+ 0x01, 0x94,
+ 0x67, 0x11, // StatusText
+ 0x4e, 0x6f, 0x74, 0x68, 0x69, 0x6e, 0x67, 0x20, 0x6e, 0x6f, 0x74, 0x20,
+ 0x66, 0x6f, 0x75, 0x6e, 0x64};
+
+BOOST_AUTO_TEST_CASE(Encode)
+{
+ ControlResponse cr(404, "Nothing not found");
+ const Block& wire = cr.wireEncode();
+ BOOST_CHECK_EQUAL_COLLECTIONS(WIRE, WIRE + sizeof(WIRE),
+ wire.begin(), wire.end());
+}
+
+BOOST_AUTO_TEST_CASE(Decode)
+{
+ ControlResponse cr(Block(WIRE, sizeof(WIRE)));
+ BOOST_CHECK_EQUAL(cr.getCode(), 404);
+ BOOST_CHECK_EQUAL(cr.getText(), "Nothing not found");
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestControlResponse
+BOOST_AUTO_TEST_SUITE_END() // Mgmt
+
+} // namespace tests
+} // namespace mgmt
+} // namespace ndn
diff --git a/tests/unit/mgmt/dispatcher.t.cpp b/tests/unit/mgmt/dispatcher.t.cpp
new file mode 100644
index 0000000..c75af4d
--- /dev/null
+++ b/tests/unit/mgmt/dispatcher.t.cpp
@@ -0,0 +1,494 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library 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 Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "mgmt/dispatcher.hpp"
+#include "mgmt/nfd/control-parameters.hpp"
+#include "util/dummy-client-face.hpp"
+
+#include "boost-test.hpp"
+#include "make-interest-data.hpp"
+#include "../identity-management-time-fixture.hpp"
+
+namespace ndn {
+namespace mgmt {
+namespace tests {
+
+using namespace ndn::tests;
+
+class DispatcherFixture : public IdentityManagementTimeFixture
+{
+public:
+ DispatcherFixture()
+ : face(io, m_keyChain, {true, true})
+ , dispatcher(face, m_keyChain, security::SigningInfo())
+ , storage(dispatcher.m_storage)
+ {
+ }
+
+public:
+ util::DummyClientFace face;
+ mgmt::Dispatcher dispatcher;
+ InMemoryStorageFifo& storage;
+};
+
+class VoidParameters : public mgmt::ControlParameters
+{
+public:
+ explicit
+ VoidParameters(const Block& wire)
+ {
+ wireDecode(wire);
+ }
+
+ Block
+ wireEncode() const final
+ {
+ return Block(128);
+ }
+
+ void
+ wireDecode(const Block& wire) final
+ {
+ if (wire.type() != 128)
+ BOOST_THROW_EXCEPTION(tlv::Error("Expecting TLV type 128"));
+ }
+};
+
+static Authorization
+makeTestAuthorization()
+{
+ return [] (const Name& prefix,
+ const Interest& interest,
+ const ControlParameters* params,
+ AcceptContinuation accept,
+ RejectContinuation reject) {
+ if (interest.getName()[-1] == name::Component("valid")) {
+ accept("");
+ }
+ else {
+ if (interest.getName()[-1] == name::Component("silent")) {
+ reject(RejectReply::SILENT);
+ }
+ else {
+ reject(RejectReply::STATUS403);
+ }
+ }
+ };
+}
+
+BOOST_AUTO_TEST_SUITE(Mgmt)
+BOOST_FIXTURE_TEST_SUITE(TestDispatcher, DispatcherFixture)
+
+BOOST_AUTO_TEST_CASE(Basic)
+{
+ BOOST_CHECK_NO_THROW(dispatcher
+ .addControlCommand<VoidParameters>("test/1", makeAcceptAllAuthorization(),
+ bind([] { return true; }),
+ bind([]{})));
+ BOOST_CHECK_NO_THROW(dispatcher
+ .addControlCommand<VoidParameters>("test/2", makeAcceptAllAuthorization(),
+ bind([] { return true; }),
+ bind([]{})));
+
+ BOOST_CHECK_THROW(dispatcher
+ .addControlCommand<VoidParameters>("test", makeAcceptAllAuthorization(),
+ bind([] { return true; }),
+ bind([]{})),
+ std::out_of_range);
+
+ BOOST_CHECK_NO_THROW(dispatcher.addStatusDataset("status/1",
+ makeAcceptAllAuthorization(), bind([]{})));
+ BOOST_CHECK_NO_THROW(dispatcher.addStatusDataset("status/2",
+ makeAcceptAllAuthorization(), bind([]{})));
+ BOOST_CHECK_THROW(dispatcher.addStatusDataset("status",
+ makeAcceptAllAuthorization(), bind([]{})),
+ std::out_of_range);
+
+ BOOST_CHECK_NO_THROW(dispatcher.addNotificationStream("stream/1"));
+ BOOST_CHECK_NO_THROW(dispatcher.addNotificationStream("stream/2"));
+ BOOST_CHECK_THROW(dispatcher.addNotificationStream("stream"), std::out_of_range);
+
+
+ BOOST_CHECK_NO_THROW(dispatcher.addTopPrefix("/root/1"));
+ BOOST_CHECK_NO_THROW(dispatcher.addTopPrefix("/root/2"));
+ BOOST_CHECK_THROW(dispatcher.addTopPrefix("/root"), std::out_of_range);
+
+ BOOST_CHECK_THROW(dispatcher
+ .addControlCommand<VoidParameters>("test/3", makeAcceptAllAuthorization(),
+ bind([] { return true; }),
+ bind([]{})),
+ std::domain_error);
+
+ BOOST_CHECK_THROW(dispatcher.addStatusDataset("status/3",
+ makeAcceptAllAuthorization(), bind([]{})),
+ std::domain_error);
+
+ BOOST_CHECK_THROW(dispatcher.addNotificationStream("stream/3"), std::domain_error);
+}
+
+BOOST_AUTO_TEST_CASE(AddRemoveTopPrefix)
+{
+ std::map<std::string, size_t> nCallbackCalled;
+ dispatcher
+ .addControlCommand<VoidParameters>("test/1", makeAcceptAllAuthorization(),
+ bind([] { return true; }),
+ bind([&nCallbackCalled] { ++nCallbackCalled["test/1"]; }));
+
+ dispatcher
+ .addControlCommand<VoidParameters>("test/2", makeAcceptAllAuthorization(),
+ bind([] { return true; }),
+ bind([&nCallbackCalled] { ++nCallbackCalled["test/2"]; }));
+
+ face.receive(*makeInterest("/root/1/test/1/%80%00"));
+ advanceClocks(1_ms);
+ BOOST_CHECK_EQUAL(nCallbackCalled["test/1"], 0);
+ BOOST_CHECK_EQUAL(nCallbackCalled["test/2"], 0);
+
+ dispatcher.addTopPrefix("/root/1");
+ advanceClocks(1_ms);
+
+ face.receive(*makeInterest("/root/1/test/1/%80%00"));
+ advanceClocks(1_ms);
+ BOOST_CHECK_EQUAL(nCallbackCalled["test/1"], 1);
+ BOOST_CHECK_EQUAL(nCallbackCalled["test/2"], 0);
+
+ face.receive(*makeInterest("/root/1/test/2/%80%00"));
+ advanceClocks(1_ms);
+ BOOST_CHECK_EQUAL(nCallbackCalled["test/1"], 1);
+ BOOST_CHECK_EQUAL(nCallbackCalled["test/2"], 1);
+
+ face.receive(*makeInterest("/root/2/test/1/%80%00"));
+ face.receive(*makeInterest("/root/2/test/2/%80%00"));
+ advanceClocks(1_ms);
+ BOOST_CHECK_EQUAL(nCallbackCalled["test/1"], 1);
+ BOOST_CHECK_EQUAL(nCallbackCalled["test/2"], 1);
+
+ dispatcher.addTopPrefix("/root/2");
+ advanceClocks(1_ms);
+
+ face.receive(*makeInterest("/root/1/test/1/%80%00"));
+ advanceClocks(1_ms);
+ BOOST_CHECK_EQUAL(nCallbackCalled["test/1"], 2);
+
+ face.receive(*makeInterest("/root/2/test/1/%80%00"));
+ advanceClocks(1_ms);
+ BOOST_CHECK_EQUAL(nCallbackCalled["test/1"], 3);
+
+ dispatcher.removeTopPrefix("/root/1");
+ advanceClocks(1_ms);
+
+ face.receive(*makeInterest("/root/1/test/1/%80%00"));
+ advanceClocks(1_ms);
+ BOOST_CHECK_EQUAL(nCallbackCalled["test/1"], 3);
+
+ face.receive(*makeInterest("/root/2/test/1/%80%00"));
+ advanceClocks(1_ms);
+ BOOST_CHECK_EQUAL(nCallbackCalled["test/1"], 4);
+}
+
+BOOST_AUTO_TEST_CASE(ControlCommand)
+{
+ size_t nCallbackCalled = 0;
+ dispatcher
+ .addControlCommand<VoidParameters>("test",
+ makeTestAuthorization(),
+ bind([] { return true; }),
+ bind([&nCallbackCalled] { ++nCallbackCalled; }));
+
+ dispatcher.addTopPrefix("/root");
+ advanceClocks(1_ms);
+ face.sentData.clear();
+
+ face.receive(*makeInterest("/root/test/%80%00")); // returns 403
+ face.receive(*makeInterest("/root/test/%80%00/invalid")); // returns 403
+ face.receive(*makeInterest("/root/test/%80%00/silent")); // silently ignored
+ face.receive(*makeInterest("/root/test/.../invalid")); // silently ignored (wrong format)
+ face.receive(*makeInterest("/root/test/.../valid")); // silently ignored (wrong format)
+ advanceClocks(1_ms, 20);
+ BOOST_CHECK_EQUAL(nCallbackCalled, 0);
+ BOOST_CHECK_EQUAL(face.sentData.size(), 2);
+
+ BOOST_CHECK_EQUAL(face.sentData[0].getContentType(), tlv::ContentType_Blob);
+ BOOST_CHECK_EQUAL(ControlResponse(face.sentData[0].getContent().blockFromValue()).getCode(), 403);
+ BOOST_CHECK_EQUAL(face.sentData[1].getContentType(), tlv::ContentType_Blob);
+ BOOST_CHECK_EQUAL(ControlResponse(face.sentData[1].getContent().blockFromValue()).getCode(), 403);
+
+ face.receive(*makeInterest("/root/test/%80%00/valid"));
+ advanceClocks(1_ms, 10);
+ BOOST_CHECK_EQUAL(nCallbackCalled, 1);
+}
+
+class StatefulParameters : public mgmt::ControlParameters
+{
+public:
+ explicit
+ StatefulParameters(const Block& wire)
+ {
+ wireDecode(wire);
+ }
+
+ Block
+ wireEncode() const final
+ {
+ return Block();
+ }
+
+ void
+ wireDecode(const Block& wire) final
+ {
+ m_state = EXPECTED_STATE;
+ }
+
+ bool
+ check() const
+ {
+ return m_state == EXPECTED_STATE;
+ }
+
+private:
+ static constexpr int EXPECTED_STATE = 12602;
+ int m_state = 0;
+};
+
+BOOST_AUTO_TEST_CASE(ControlCommandAsyncAuthorization) // Bug 4059
+{
+ AcceptContinuation authorizationAccept;
+ auto authorization =
+ [&authorizationAccept] (const Name& prefix, const Interest& interest, const ControlParameters* params,
+ AcceptContinuation accept, RejectContinuation reject) {
+ authorizationAccept = accept;
+ };
+
+ auto validateParameters =
+ [] (const ControlParameters& params) {
+ return dynamic_cast<const StatefulParameters&>(params).check();
+ };
+
+ size_t nCallbackCalled = 0;
+ dispatcher
+ .addControlCommand<StatefulParameters>("test",
+ authorization,
+ validateParameters,
+ bind([&nCallbackCalled] { ++nCallbackCalled; }));
+
+ dispatcher.addTopPrefix("/root");
+ advanceClocks(1_ms);
+
+ face.receive(*makeInterest("/root/test/%80%00"));
+ BOOST_CHECK_EQUAL(nCallbackCalled, 0);
+ BOOST_REQUIRE(authorizationAccept != nullptr);
+
+ advanceClocks(1_ms);
+ authorizationAccept("");
+ advanceClocks(1_ms);
+ BOOST_CHECK_EQUAL(nCallbackCalled, 1);
+}
+
+BOOST_AUTO_TEST_CASE(StatusDataset)
+{
+ const uint8_t smallBuf[] = {0x81, 0x01, 0x01};
+ const Block smallBlock(smallBuf, sizeof(smallBuf));
+ Block largeBlock;
+ {
+ EncodingBuffer encoder;
+ for (size_t i = 0; i < 2500; ++i) {
+ encoder.prependByte(1);
+ }
+ encoder.prependVarNumber(2500);
+ encoder.prependVarNumber(129);
+ largeBlock = encoder.block();
+ }
+
+ dispatcher.addStatusDataset("test/small",
+ makeTestAuthorization(),
+ [&smallBlock] (const Name& prefix, const Interest& interest,
+ StatusDatasetContext& context) {
+ context.append(smallBlock);
+ context.append(smallBlock);
+ context.append(smallBlock);
+ context.end();
+ });
+
+ dispatcher.addStatusDataset("test/large",
+ makeTestAuthorization(),
+ [&largeBlock] (const Name& prefix, const Interest& interest,
+ StatusDatasetContext& context) {
+ context.append(largeBlock);
+ context.append(largeBlock);
+ context.append(largeBlock);
+ context.end();
+ });
+
+ dispatcher.addStatusDataset("test/reject",
+ makeTestAuthorization(),
+ [] (const Name& prefix, const Interest& interest,
+ StatusDatasetContext& context) {
+ context.reject();
+ });
+
+ dispatcher.addTopPrefix("/root");
+ advanceClocks(1_ms);
+ face.sentData.clear();
+
+ face.receive(*makeInterest("/root/test/small/%80%00")); // returns 403
+ face.receive(*makeInterest("/root/test/small/%80%00/invalid")); // returns 403
+ face.receive(*makeInterest("/root/test/small/%80%00/silent")); // silently ignored
+ advanceClocks(1_ms, 20);
+ BOOST_CHECK_EQUAL(face.sentData.size(), 2);
+
+ BOOST_CHECK_EQUAL(face.sentData[0].getContentType(), tlv::ContentType_Blob);
+ BOOST_CHECK_EQUAL(ControlResponse(face.sentData[0].getContent().blockFromValue()).getCode(), 403);
+ BOOST_CHECK_EQUAL(face.sentData[1].getContentType(), tlv::ContentType_Blob);
+ BOOST_CHECK_EQUAL(ControlResponse(face.sentData[1].getContent().blockFromValue()).getCode(), 403);
+
+ face.sentData.clear();
+
+ auto interestSmall = *makeInterest("/root/test/small/valid", true);
+ face.receive(interestSmall);
+ advanceClocks(1_ms, 10);
+
+ // one data packet is generated and sent to both places
+ BOOST_CHECK_EQUAL(face.sentData.size(), 1);
+ BOOST_CHECK_EQUAL(storage.size(), 1);
+
+ auto fetchedData = storage.find(interestSmall);
+ BOOST_REQUIRE(fetchedData != nullptr);
+ BOOST_CHECK_EQUAL(face.sentData[0].wireEncode(), fetchedData->wireEncode());
+
+ face.receive(*makeInterest(Name("/root/test/small/valid").appendVersion(10))); // should be ignored
+ face.receive(*makeInterest(Name("/root/test/small/valid").appendSegment(20))); // should be ignored
+ advanceClocks(1_ms, 10);
+ BOOST_CHECK_EQUAL(face.sentData.size(), 1);
+ BOOST_CHECK_EQUAL(storage.size(), 1);
+
+ Block content = face.sentData[0].getContent();
+ BOOST_CHECK_NO_THROW(content.parse());
+
+ BOOST_REQUIRE_EQUAL(content.elements().size(), 3);
+ BOOST_CHECK_EQUAL(content.elements()[0], smallBlock);
+ BOOST_CHECK_EQUAL(content.elements()[1], smallBlock);
+ BOOST_CHECK_EQUAL(content.elements()[2], smallBlock);
+
+ storage.erase("/", true); // clear the storage
+ face.sentData.clear();
+ face.receive(*makeInterest("/root/test/large/valid"));
+ advanceClocks(1_ms, 10);
+
+ // two data packets are generated, the first one will be sent to both places
+ // while the second one will only be inserted into the in-memory storage
+ BOOST_CHECK_EQUAL(face.sentData.size(), 1);
+ BOOST_CHECK_EQUAL(storage.size(), 2);
+
+ // segment0 should be sent through the face
+ const auto& component = face.sentData[0].getName().at(-1);
+ BOOST_CHECK(component.isSegment());
+ BOOST_CHECK_EQUAL(component.toSegment(), 0);
+
+ std::vector<Data> dataInStorage;
+ std::copy(storage.begin(), storage.end(), std::back_inserter(dataInStorage));
+
+ // the Data sent through the face should be the same as the first Data in the storage
+ BOOST_CHECK_EQUAL(face.sentData[0].getName(), dataInStorage[0].getName());
+ BOOST_CHECK_EQUAL(face.sentData[0].getContent(), dataInStorage[0].getContent());
+
+ content = [&dataInStorage] () -> Block {
+ EncodingBuffer encoder;
+ size_t valueLength = encoder.prependByteArray(dataInStorage[1].getContent().value(),
+ dataInStorage[1].getContent().value_size());
+ valueLength += encoder.prependByteArray(dataInStorage[0].getContent().value(),
+ dataInStorage[0].getContent().value_size());
+ encoder.prependVarNumber(valueLength);
+ encoder.prependVarNumber(tlv::Content);
+ return encoder.block();
+ }();
+
+ BOOST_CHECK_NO_THROW(content.parse());
+ BOOST_REQUIRE_EQUAL(content.elements().size(), 3);
+ BOOST_CHECK_EQUAL(content.elements()[0], largeBlock);
+ BOOST_CHECK_EQUAL(content.elements()[1], largeBlock);
+ BOOST_CHECK_EQUAL(content.elements()[2], largeBlock);
+
+ storage.erase("/", true);// clear the storage
+ face.sentData.clear();
+ face.receive(*makeInterest("/root/test/reject/%80%00/valid")); // returns nack
+ advanceClocks(1_ms);
+ BOOST_CHECK_EQUAL(face.sentData.size(), 1);
+ BOOST_CHECK_EQUAL(face.sentData[0].getContentType(), tlv::ContentType_Nack);
+ BOOST_CHECK_EQUAL(ControlResponse(face.sentData[0].getContent().blockFromValue()).getCode(), 400);
+ BOOST_CHECK_EQUAL(storage.size(), 0); // the nack packet will not be inserted into the in-memory storage
+}
+
+BOOST_AUTO_TEST_CASE(NotificationStream)
+{
+ const uint8_t buf[] = {0x82, 0x01, 0x02};
+ const Block block(buf, sizeof(buf));
+ auto post = dispatcher.addNotificationStream("test");
+
+ post(block);
+ advanceClocks(1_ms);
+ BOOST_CHECK_EQUAL(face.sentData.size(), 0);
+
+ dispatcher.addTopPrefix("/root");
+ advanceClocks(1_ms);
+ face.sentData.clear();
+
+ post(block);
+ advanceClocks(1_ms);
+ BOOST_CHECK_EQUAL(face.sentData.size(), 1);
+ BOOST_CHECK_EQUAL(storage.size(), 1);
+
+ post(block);
+ post(block);
+ post(block);
+ advanceClocks(1_ms, 10);
+
+ BOOST_REQUIRE_EQUAL(face.sentData.size(), 4);
+ BOOST_CHECK_EQUAL(face.sentData[0].getName(), "/root/test/%FE%00");
+ BOOST_CHECK_EQUAL(face.sentData[1].getName(), "/root/test/%FE%01");
+ BOOST_CHECK_EQUAL(face.sentData[2].getName(), "/root/test/%FE%02");
+ BOOST_CHECK_EQUAL(face.sentData[3].getName(), "/root/test/%FE%03");
+
+ BOOST_CHECK_EQUAL(face.sentData[0].getContent().blockFromValue(), block);
+ BOOST_CHECK_EQUAL(face.sentData[1].getContent().blockFromValue(), block);
+ BOOST_CHECK_EQUAL(face.sentData[2].getContent().blockFromValue(), block);
+ BOOST_CHECK_EQUAL(face.sentData[3].getContent().blockFromValue(), block);
+
+ // each version of notification will be sent to both places
+ std::vector<Data> dataInStorage;
+ std::copy(storage.begin(), storage.end(), std::back_inserter(dataInStorage));
+ BOOST_REQUIRE_EQUAL(dataInStorage.size(), 4);
+ BOOST_CHECK_EQUAL(dataInStorage[0].getName(), "/root/test/%FE%00");
+ BOOST_CHECK_EQUAL(dataInStorage[1].getName(), "/root/test/%FE%01");
+ BOOST_CHECK_EQUAL(dataInStorage[2].getName(), "/root/test/%FE%02");
+ BOOST_CHECK_EQUAL(dataInStorage[3].getName(), "/root/test/%FE%03");
+
+ BOOST_CHECK_EQUAL(dataInStorage[0].getContent().blockFromValue(), block);
+ BOOST_CHECK_EQUAL(dataInStorage[1].getContent().blockFromValue(), block);
+ BOOST_CHECK_EQUAL(dataInStorage[2].getContent().blockFromValue(), block);
+ BOOST_CHECK_EQUAL(dataInStorage[3].getContent().blockFromValue(), block);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestDispatcher
+BOOST_AUTO_TEST_SUITE_END() // Mgmt
+
+} // namespace tests
+} // namespace mgmt
+} // namespace ndn
diff --git a/tests/unit/mgmt/nfd/channel-status.t.cpp b/tests/unit/mgmt/nfd/channel-status.t.cpp
new file mode 100644
index 0000000..b3afde8
--- /dev/null
+++ b/tests/unit/mgmt/nfd/channel-status.t.cpp
@@ -0,0 +1,86 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library 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 Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "mgmt/nfd/channel-status.hpp"
+
+#include "boost-test.hpp"
+#include <boost/lexical_cast.hpp>
+
+namespace ndn {
+namespace nfd {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Mgmt)
+BOOST_AUTO_TEST_SUITE(Nfd)
+BOOST_AUTO_TEST_SUITE(TestChannelStatus)
+
+BOOST_AUTO_TEST_CASE(Encode)
+{
+ ChannelStatus status1;
+ status1.setLocalUri("udp4://192.168.2.1");
+ Block wire = status1.wireEncode();
+
+ // These octets are obtained by the snippet below.
+ // This check is intended to detect unexpected encoding change in the future.
+ // for (Buffer::const_iterator it = wire.begin(); it != wire.end(); ++it) {
+ // printf("0x%02x, ", *it);
+ // }
+ static const uint8_t expected[] = {
+ 0x82, 0x14, 0x81, 0x12, 0x75, 0x64, 0x70, 0x34, 0x3a, 0x2f, 0x2f, 0x31, 0x39, 0x32,
+ 0x2e, 0x31, 0x36, 0x38, 0x2e, 0x32, 0x2e, 0x31
+ };
+ BOOST_CHECK_EQUAL_COLLECTIONS(expected, expected + sizeof(expected),
+ wire.begin(), wire.end());
+
+ ChannelStatus status2(wire);
+ BOOST_CHECK_EQUAL(status1, status2);
+}
+
+BOOST_AUTO_TEST_CASE(Equality)
+{
+ ChannelStatus status1, status2;
+
+ status1.setLocalUri("udp4://127.0.0.1:6363");
+ status2 = status1;
+ BOOST_CHECK_EQUAL(status1, status2);
+
+ status2.setLocalUri("dev://eth0");
+ BOOST_CHECK_NE(status1, status2);
+}
+
+BOOST_AUTO_TEST_CASE(Print)
+{
+ ChannelStatus status;
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(status),
+ "Channel(LocalUri: )");
+
+ status.setLocalUri("udp4://127.0.0.1:6363");
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(status),
+ "Channel(LocalUri: udp4://127.0.0.1:6363)");
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestChannelStatus
+BOOST_AUTO_TEST_SUITE_END() // Nfd
+BOOST_AUTO_TEST_SUITE_END() // Mgmt
+
+} // namespace tests
+} // namespace nfd
+} // namespace ndn
diff --git a/tests/unit/mgmt/nfd/command-options.t.cpp b/tests/unit/mgmt/nfd/command-options.t.cpp
new file mode 100644
index 0000000..e8fe76e
--- /dev/null
+++ b/tests/unit/mgmt/nfd/command-options.t.cpp
@@ -0,0 +1,79 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library 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 Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "mgmt/nfd/command-options.hpp"
+#include "security/signing-helpers.hpp"
+
+#include "boost-test.hpp"
+
+namespace ndn {
+namespace nfd {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Mgmt)
+BOOST_AUTO_TEST_SUITE(Nfd)
+BOOST_AUTO_TEST_SUITE(TestCommandOptions)
+
+BOOST_AUTO_TEST_CASE(Timeout)
+{
+ CommandOptions co;
+ BOOST_CHECK_EQUAL(co.getTimeout(), CommandOptions::DEFAULT_TIMEOUT);
+
+ co.setTimeout(7414_ms);
+ BOOST_CHECK_EQUAL(co.getTimeout(), 7414_ms);
+
+ BOOST_CHECK_THROW(co.setTimeout(time::milliseconds::zero()), std::out_of_range);
+ BOOST_CHECK_THROW(co.setTimeout(time::milliseconds(-1)), std::out_of_range);
+ BOOST_CHECK_EQUAL(co.getTimeout(), 7414_ms); // unchanged after throw
+
+ co.setTimeout(1_ms);
+ BOOST_CHECK_EQUAL(co.getTimeout(), 1_ms);
+}
+
+BOOST_AUTO_TEST_CASE(Prefix)
+{
+ CommandOptions co;
+ BOOST_CHECK_EQUAL(co.getPrefix(), CommandOptions::DEFAULT_PREFIX);
+
+ co.setPrefix(Name()); // empty Name is okay
+ BOOST_CHECK_EQUAL(co.getPrefix(), Name());
+
+ co.setPrefix("ndn:/localhop/net/example/nfd");
+ BOOST_CHECK_EQUAL(co.getPrefix(), Name("ndn:/localhop/net/example/nfd"));
+}
+
+BOOST_AUTO_TEST_CASE(SigningInfo)
+{
+ CommandOptions co;
+ BOOST_CHECK_EQUAL(co.getSigningInfo().getSignerType(), security::SigningInfo::SIGNER_TYPE_NULL);
+
+ co.setSigningInfo(signingByIdentity("ndn:/tmp/identity"));
+ BOOST_CHECK_EQUAL(co.getSigningInfo().getSignerType(), security::SigningInfo::SIGNER_TYPE_ID);
+ BOOST_CHECK_EQUAL(co.getSigningInfo().getSignerName(), "ndn:/tmp/identity");
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestCommandOptions
+BOOST_AUTO_TEST_SUITE_END() // Nfd
+BOOST_AUTO_TEST_SUITE_END() // Mgmt
+
+} // namespace tests
+} // namespace nfd
+} // namespace ndn
diff --git a/tests/unit/mgmt/nfd/control-command.t.cpp b/tests/unit/mgmt/nfd/control-command.t.cpp
new file mode 100644
index 0000000..4db3752
--- /dev/null
+++ b/tests/unit/mgmt/nfd/control-command.t.cpp
@@ -0,0 +1,479 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library 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 Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "mgmt/nfd/control-command.hpp"
+
+#include "boost-test.hpp"
+
+namespace ndn {
+namespace nfd {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Mgmt)
+BOOST_AUTO_TEST_SUITE(Nfd)
+BOOST_AUTO_TEST_SUITE(TestControlCommand)
+
+BOOST_AUTO_TEST_CASE(FaceCreateRequest)
+{
+ FaceCreateCommand command;
+
+ // good with required fields only
+ ControlParameters p1;
+ p1.setUri("tcp4://192.0.2.1:6363");
+ BOOST_CHECK_NO_THROW(command.validateRequest(p1));
+ BOOST_CHECK(Name("/PREFIX/faces/create").isPrefixOf(command.getRequestName("/PREFIX", p1)));
+
+ // good with optional fields
+ ControlParameters p2(p1);
+ p2.setLocalUri("tcp4://192.0.2.2:32114")
+ .setFacePersistency(FACE_PERSISTENCY_PERMANENT)
+ .setBaseCongestionMarkingInterval(100_ms)
+ .setDefaultCongestionThreshold(10000)
+ .setMtu(8192)
+ .setFlags(0x3)
+ .setMask(0x1);
+ BOOST_CHECK_NO_THROW(command.validateRequest(p1));
+
+ // Uri is required
+ ControlParameters p3;
+ BOOST_CHECK_THROW(command.validateRequest(p3), ControlCommand::ArgumentError);
+
+ // Name is forbidden
+ ControlParameters p4(p1);
+ p4.setName("/example");
+ BOOST_CHECK_THROW(command.validateRequest(p4), ControlCommand::ArgumentError);
+
+ // Flags and Mask must be specified together
+ ControlParameters p5(p1);
+ p5.setFlags(0x3);
+ BOOST_CHECK_THROW(command.validateRequest(p5), ControlCommand::ArgumentError);
+
+ ControlParameters p6(p1);
+ p6.setMask(0x1);
+ BOOST_CHECK_THROW(command.validateRequest(p6), ControlCommand::ArgumentError);
+}
+
+BOOST_AUTO_TEST_CASE(FaceCreateResponse)
+{
+ FaceCreateCommand command;
+
+ // good
+ ControlParameters p1;
+ p1.setFaceId(3208)
+ .setUri("tcp4://192.0.2.1:6363")
+ .setLocalUri("tcp4://192.0.2.2:32114")
+ .setFacePersistency(FACE_PERSISTENCY_PERMANENT)
+ .setBaseCongestionMarkingInterval(500_ns)
+ .setDefaultCongestionThreshold(12345)
+ .setMtu(2048)
+ .setFlags(0x3);
+ BOOST_CHECK_NO_THROW(command.validateResponse(p1));
+
+ // Name is forbidden
+ ControlParameters p2(p1);
+ p2.setName("/example");
+ BOOST_CHECK_THROW(command.validateResponse(p2), ControlCommand::ArgumentError);
+
+ // Mask is forbidden
+ ControlParameters p3(p1);
+ p3.setMask(0x1);
+ BOOST_CHECK_THROW(command.validateResponse(p3), ControlCommand::ArgumentError);
+
+ // FaceId must be valid
+ ControlParameters p4(p1);
+ p4.setFaceId(INVALID_FACE_ID);
+ BOOST_CHECK_THROW(command.validateResponse(p4), ControlCommand::ArgumentError);
+
+ // LocalUri is required
+ ControlParameters p5(p1);
+ p5.unsetLocalUri();
+ BOOST_CHECK_THROW(command.validateResponse(p5), ControlCommand::ArgumentError);
+}
+
+BOOST_AUTO_TEST_CASE(FaceUpdate)
+{
+ FaceUpdateCommand command;
+
+ ControlParameters p1;
+ p1.setFaceId(0);
+ BOOST_CHECK_NO_THROW(command.validateRequest(p1));
+ p1.setFaceId(INVALID_FACE_ID);
+ BOOST_CHECK_THROW(command.validateResponse(p1), ControlCommand::ArgumentError);
+
+ p1.setFaceId(1);
+ BOOST_CHECK_NO_THROW(command.validateRequest(p1));
+ BOOST_CHECK_THROW(command.validateResponse(p1), ControlCommand::ArgumentError);
+ command.applyDefaultsToRequest(p1);
+ BOOST_CHECK_EQUAL(p1.getFaceId(), 1);
+
+ ControlParameters p2;
+ p2.setFaceId(1)
+ .setFacePersistency(FACE_PERSISTENCY_PERSISTENT)
+ .setBaseCongestionMarkingInterval(765_ns)
+ .setDefaultCongestionThreshold(54321)
+ .setFlagBit(BIT_LOCAL_FIELDS_ENABLED, false);
+ BOOST_CHECK_NO_THROW(command.validateRequest(p2));
+ BOOST_CHECK_THROW(command.validateResponse(p2), ControlCommand::ArgumentError); // Mask forbidden but present
+
+ // Flags without Mask
+ p2.unsetMask();
+ BOOST_CHECK_THROW(command.validateRequest(p2), ControlCommand::ArgumentError);
+ BOOST_CHECK_NO_THROW(command.validateResponse(p2));
+
+ p2.setFlagBit(BIT_LOCAL_FIELDS_ENABLED, false);
+ p2.unsetFaceId();
+ BOOST_CHECK_NO_THROW(command.validateRequest(p2));
+
+ ControlParameters p3;
+ p3.setFaceId(1)
+ .setName("/ndn/name");
+ BOOST_CHECK_THROW(command.validateRequest(p3), ControlCommand::ArgumentError);
+ BOOST_CHECK_THROW(command.validateResponse(p3), ControlCommand::ArgumentError);
+
+ ControlParameters p4;
+ p4.setFaceId(1)
+ .setUri("tcp4://192.0.2.1");
+ BOOST_CHECK_THROW(command.validateRequest(p4), ControlCommand::ArgumentError);
+ BOOST_CHECK_THROW(command.validateResponse(p4), ControlCommand::ArgumentError);
+
+ ControlParameters p5;
+ BOOST_CHECK_NO_THROW(command.validateRequest(p5));
+ BOOST_CHECK_THROW(command.validateResponse(p5), ControlCommand::ArgumentError);
+ BOOST_CHECK(!p5.hasFaceId());
+ command.applyDefaultsToRequest(p5);
+ BOOST_REQUIRE(p5.hasFaceId());
+ BOOST_CHECK_NO_THROW(command.validateRequest(p5));
+ BOOST_CHECK_THROW(command.validateResponse(p5), ControlCommand::ArgumentError);
+ BOOST_CHECK_EQUAL(p5.getFaceId(), 0);
+
+ ControlParameters p6;
+ p6.setFaceId(1)
+ .setMtu(1024);
+ BOOST_CHECK_THROW(command.validateRequest(p6), ControlCommand::ArgumentError);
+ BOOST_CHECK_THROW(command.validateResponse(p6), ControlCommand::ArgumentError);
+}
+
+BOOST_AUTO_TEST_CASE(FaceDestroy)
+{
+ FaceDestroyCommand command;
+
+ ControlParameters p1;
+ p1.setUri("tcp4://192.0.2.1")
+ .setFaceId(4);
+ BOOST_CHECK_THROW(command.validateRequest(p1), ControlCommand::ArgumentError);
+ BOOST_CHECK_THROW(command.validateResponse(p1), ControlCommand::ArgumentError);
+
+ ControlParameters p2;
+ p2.setFaceId(INVALID_FACE_ID);
+ BOOST_CHECK_THROW(command.validateRequest(p2), ControlCommand::ArgumentError);
+ BOOST_CHECK_THROW(command.validateResponse(p2), ControlCommand::ArgumentError);
+
+ ControlParameters p3;
+ p3.setFaceId(6);
+ BOOST_CHECK_NO_THROW(command.validateRequest(p3));
+ BOOST_CHECK_NO_THROW(command.validateResponse(p3));
+ Name n3;
+ BOOST_CHECK_NO_THROW(n3 = command.getRequestName("/PREFIX", p3));
+ BOOST_CHECK(Name("ndn:/PREFIX/faces/destroy").isPrefixOf(n3));
+}
+
+BOOST_AUTO_TEST_CASE(FibAddNextHop)
+{
+ FibAddNextHopCommand command;
+
+ ControlParameters p1;
+ p1.setName("ndn:/")
+ .setFaceId(22);
+ BOOST_CHECK_NO_THROW(command.validateRequest(p1));
+ BOOST_CHECK_THROW(command.validateResponse(p1), ControlCommand::ArgumentError);
+ Name n1;
+ BOOST_CHECK_NO_THROW(n1 = command.getRequestName("/PREFIX", p1));
+ BOOST_CHECK(Name("ndn:/PREFIX/fib/add-nexthop").isPrefixOf(n1));
+
+ ControlParameters p2;
+ p2.setName("ndn:/example")
+ .setFaceId(0)
+ .setCost(6);
+ BOOST_CHECK_NO_THROW(command.validateRequest(p2));
+ p2.setFaceId(INVALID_FACE_ID);
+ BOOST_CHECK_THROW(command.validateResponse(p2), ControlCommand::ArgumentError);
+
+ command.applyDefaultsToRequest(p1);
+ BOOST_REQUIRE(p1.hasCost());
+ BOOST_CHECK_EQUAL(p1.getCost(), 0);
+
+ p1.unsetFaceId();
+ BOOST_CHECK_NO_THROW(command.validateRequest(p1));
+ command.applyDefaultsToRequest(p1);
+ BOOST_REQUIRE(p1.hasFaceId());
+ BOOST_CHECK_EQUAL(p1.getFaceId(), 0);
+}
+
+BOOST_AUTO_TEST_CASE(FibRemoveNextHop)
+{
+ FibRemoveNextHopCommand command;
+
+ ControlParameters p1;
+ p1.setName("ndn:/")
+ .setFaceId(22);
+ BOOST_CHECK_NO_THROW(command.validateRequest(p1));
+ BOOST_CHECK_NO_THROW(command.validateResponse(p1));
+ Name n1;
+ BOOST_CHECK_NO_THROW(n1 = command.getRequestName("/PREFIX", p1));
+ BOOST_CHECK(Name("ndn:/PREFIX/fib/remove-nexthop").isPrefixOf(n1));
+
+ ControlParameters p2;
+ p2.setName("ndn:/example")
+ .setFaceId(0);
+ BOOST_CHECK_NO_THROW(command.validateRequest(p2));
+ p2.setFaceId(INVALID_FACE_ID);
+ BOOST_CHECK_THROW(command.validateResponse(p2), ControlCommand::ArgumentError);
+
+ p1.unsetFaceId();
+ BOOST_CHECK_NO_THROW(command.validateRequest(p1));
+ command.applyDefaultsToRequest(p1);
+ BOOST_REQUIRE(p1.hasFaceId());
+ BOOST_CHECK_EQUAL(p1.getFaceId(), 0);
+}
+
+BOOST_AUTO_TEST_CASE(CsConfigRequest)
+{
+ CsConfigCommand command;
+
+ // good empty request
+ ControlParameters p1;
+ command.validateRequest(p1);
+ BOOST_CHECK(Name("/PREFIX/cs/config").isPrefixOf(command.getRequestName("/PREFIX", p1)));
+
+ // good full request
+ ControlParameters p2;
+ p2.setCapacity(1574);
+ p2.setFlagBit(BIT_CS_ENABLE_ADMIT, true);
+ p2.setFlagBit(BIT_CS_ENABLE_SERVE, true);
+ command.validateRequest(p2);
+
+ // bad request: Flags but no Mask
+ ControlParameters p3(p2);
+ p3.unsetMask();
+ BOOST_CHECK_THROW(command.validateRequest(p3), ControlCommand::ArgumentError);
+
+ // bad request: Mask but no Flags
+ ControlParameters p4(p2);
+ p4.unsetFlags();
+ BOOST_CHECK_THROW(command.validateRequest(p4), ControlCommand::ArgumentError);
+
+ // bad request: forbidden field
+ ControlParameters p5(p2);
+ p5.setName("/example");
+ BOOST_CHECK_THROW(command.validateRequest(p5), ControlCommand::ArgumentError);
+}
+
+BOOST_AUTO_TEST_CASE(CsConfigResponse)
+{
+ CsConfigCommand command;
+
+ // bad empty response
+ ControlParameters p1;
+ BOOST_CHECK_THROW(command.validateResponse(p1), ControlCommand::ArgumentError);
+
+ // bad response: Mask not allowed
+ ControlParameters p2;
+ p2.setCapacity(1574);
+ p2.setFlagBit(BIT_CS_ENABLE_ADMIT, true);
+ p2.setFlagBit(BIT_CS_ENABLE_SERVE, true);
+ BOOST_CHECK_THROW(command.validateResponse(p2), ControlCommand::ArgumentError);
+
+ // good response
+ ControlParameters p3(p2);
+ p3.unsetMask();
+ command.validateResponse(p3);
+}
+
+BOOST_AUTO_TEST_CASE(CsEraseRequest)
+{
+ CsEraseCommand command;
+
+ // good no-limit request
+ ControlParameters p1;
+ p1.setName("/u4LYPNU8Q");
+ command.validateRequest(p1);
+ BOOST_CHECK(Name("/PREFIX/cs/erase").isPrefixOf(command.getRequestName("/PREFIX", p1)));
+
+ // good limited request
+ ControlParameters p2;
+ p2.setName("/IMw1RaLF");
+ p2.setCount(177);
+ command.validateRequest(p2);
+
+ // bad request: zero entry
+ ControlParameters p3;
+ p3.setName("/ahMID1jcib");
+ p3.setCount(0);
+ BOOST_CHECK_THROW(command.validateRequest(p3), ControlCommand::ArgumentError);
+
+ // bad request: forbidden field
+ ControlParameters p4(p2);
+ p4.setCapacity(278);
+ BOOST_CHECK_THROW(command.validateRequest(p3), ControlCommand::ArgumentError);
+}
+
+BOOST_AUTO_TEST_CASE(CsEraseResponse)
+{
+ CsEraseCommand command;
+
+ // good normal response
+ ControlParameters p1;
+ p1.setName("/TwiIwCdR");
+ p1.setCount(1);
+ command.validateResponse(p1);
+
+ // good limit exceeded request
+ ControlParameters p2;
+ p2.setName("/NMsiy44pr");
+ p2.setCapacity(360);
+ p2.setCount(360);
+ command.validateResponse(p2);
+
+ // good zero-entry response
+ ControlParameters p3;
+ p3.setName("/5f1LRPh1L");
+ p3.setCount(0);
+ command.validateResponse(p3);
+
+ // bad request: missing Count
+ ControlParameters p4(p1);
+ p4.unsetCount();
+ BOOST_CHECK_THROW(command.validateResponse(p4), ControlCommand::ArgumentError);
+
+ // bad request: zero capacity
+ ControlParameters p5(p1);
+ p5.setCapacity(0);
+ BOOST_CHECK_THROW(command.validateResponse(p5), ControlCommand::ArgumentError);
+}
+
+BOOST_AUTO_TEST_CASE(StrategyChoiceSet)
+{
+ StrategyChoiceSetCommand command;
+
+ ControlParameters p1;
+ p1.setName("ndn:/")
+ .setStrategy("ndn:/strategy/P");
+ BOOST_CHECK_NO_THROW(command.validateRequest(p1));
+ BOOST_CHECK_NO_THROW(command.validateResponse(p1));
+ Name n1;
+ BOOST_CHECK_NO_THROW(n1 = command.getRequestName("/PREFIX", p1));
+ BOOST_CHECK(Name("ndn:/PREFIX/strategy-choice/set").isPrefixOf(n1));
+
+ ControlParameters p2;
+ p2.setName("ndn:/example");
+ BOOST_CHECK_THROW(command.validateRequest(p2), ControlCommand::ArgumentError);
+ BOOST_CHECK_THROW(command.validateResponse(p2), ControlCommand::ArgumentError);
+}
+
+BOOST_AUTO_TEST_CASE(StrategyChoiceUnset)
+{
+ StrategyChoiceUnsetCommand command;
+
+ ControlParameters p1;
+ p1.setName("ndn:/example");
+ BOOST_CHECK_NO_THROW(command.validateRequest(p1));
+ BOOST_CHECK_NO_THROW(command.validateResponse(p1));
+ Name n1;
+ BOOST_CHECK_NO_THROW(n1 = command.getRequestName("/PREFIX", p1));
+ BOOST_CHECK(Name("ndn:/PREFIX/strategy-choice/unset").isPrefixOf(n1));
+
+ ControlParameters p2;
+ p2.setName("ndn:/example")
+ .setStrategy("ndn:/strategy/P");
+ BOOST_CHECK_THROW(command.validateRequest(p2), ControlCommand::ArgumentError);
+ BOOST_CHECK_THROW(command.validateResponse(p2), ControlCommand::ArgumentError);
+
+ ControlParameters p3;
+ p3.setName("ndn:/");
+ BOOST_CHECK_THROW(command.validateRequest(p3), ControlCommand::ArgumentError);
+ BOOST_CHECK_THROW(command.validateResponse(p3), ControlCommand::ArgumentError);
+}
+
+BOOST_AUTO_TEST_CASE(RibRegister)
+{
+ RibRegisterCommand command;
+
+ ControlParameters p1;
+ p1.setName("ndn:/");
+ BOOST_CHECK_NO_THROW(command.validateRequest(p1));
+ BOOST_CHECK_THROW(command.validateResponse(p1), ControlCommand::ArgumentError);
+ Name n1;
+ BOOST_CHECK_NO_THROW(n1 = command.getRequestName("/PREFIX", p1));
+ BOOST_CHECK(Name("ndn:/PREFIX/rib/register").isPrefixOf(n1));
+
+ command.applyDefaultsToRequest(p1);
+ BOOST_REQUIRE(p1.hasOrigin());
+ BOOST_CHECK_EQUAL(p1.getOrigin(), ROUTE_ORIGIN_APP);
+ BOOST_REQUIRE(p1.hasCost());
+ BOOST_CHECK_EQUAL(p1.getCost(), 0);
+ BOOST_REQUIRE(p1.hasFlags());
+ BOOST_CHECK_EQUAL(p1.getFlags(), static_cast<uint64_t>(ROUTE_FLAG_CHILD_INHERIT));
+ BOOST_CHECK_EQUAL(p1.hasExpirationPeriod(), false);
+
+ ControlParameters p2;
+ p2.setName("ndn:/example")
+ .setFaceId(2)
+ .setCost(6);
+ BOOST_CHECK_NO_THROW(command.validateRequest(p2));
+ command.applyDefaultsToRequest(p2);
+ BOOST_CHECK_EQUAL(p2.hasExpirationPeriod(), false);
+ BOOST_CHECK_NO_THROW(command.validateResponse(p2));
+}
+
+BOOST_AUTO_TEST_CASE(RibUnregister)
+{
+ RibUnregisterCommand command;
+
+ ControlParameters p1;
+ p1.setName("ndn:/")
+ .setFaceId(22)
+ .setOrigin(ROUTE_ORIGIN_STATIC);
+ BOOST_CHECK_NO_THROW(command.validateRequest(p1));
+ BOOST_CHECK_NO_THROW(command.validateResponse(p1));
+ Name n1;
+ BOOST_CHECK_NO_THROW(n1 = command.getRequestName("/PREFIX", p1));
+ BOOST_CHECK(Name("ndn:/PREFIX/rib/unregister").isPrefixOf(n1));
+
+ ControlParameters p2;
+ p2.setName("ndn:/example")
+ .setFaceId(0)
+ .setOrigin(ROUTE_ORIGIN_APP);
+ BOOST_CHECK_NO_THROW(command.validateRequest(p2));
+ p2.setFaceId(INVALID_FACE_ID);
+ BOOST_CHECK_THROW(command.validateResponse(p2), ControlCommand::ArgumentError);
+
+ p2.unsetFaceId();
+ BOOST_CHECK_NO_THROW(command.validateRequest(p2));
+ BOOST_CHECK_THROW(command.validateResponse(p2), ControlCommand::ArgumentError);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestControlCommand
+BOOST_AUTO_TEST_SUITE_END() // Nfd
+BOOST_AUTO_TEST_SUITE_END() // Mgmt
+
+} // namespace tests
+} // namespace nfd
+} // namespace ndn
diff --git a/tests/unit/mgmt/nfd/control-parameters.t.cpp b/tests/unit/mgmt/nfd/control-parameters.t.cpp
new file mode 100644
index 0000000..6030060
--- /dev/null
+++ b/tests/unit/mgmt/nfd/control-parameters.t.cpp
@@ -0,0 +1,247 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library 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 Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "mgmt/nfd/control-parameters.hpp"
+
+#include "boost-test.hpp"
+
+namespace ndn {
+namespace nfd {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Mgmt)
+BOOST_AUTO_TEST_SUITE(Nfd)
+BOOST_AUTO_TEST_SUITE(TestControlParameters)
+
+BOOST_AUTO_TEST_CASE(Fields)
+{
+ ControlParameters input;
+
+ ControlParameters decoded(input.wireEncode());
+ BOOST_CHECK_EQUAL(decoded.hasName(), false);
+ BOOST_CHECK_EQUAL(decoded.hasFaceId(), false);
+ BOOST_CHECK_EQUAL(decoded.hasUri(), false);
+ BOOST_CHECK_EQUAL(decoded.hasLocalUri(), false);
+ BOOST_CHECK_EQUAL(decoded.hasOrigin(), false);
+ BOOST_CHECK_EQUAL(decoded.hasCost(), false);
+ BOOST_CHECK_EQUAL(decoded.hasCapacity(), false);
+ BOOST_CHECK_EQUAL(decoded.hasCount(), false);
+ BOOST_CHECK_EQUAL(decoded.hasFlags(), false);
+ BOOST_CHECK_EQUAL(decoded.hasMask(), false);
+ BOOST_CHECK_EQUAL(decoded.hasStrategy(), false);
+ BOOST_CHECK_EQUAL(decoded.hasExpirationPeriod(), false);
+ BOOST_CHECK_EQUAL(decoded.hasFacePersistency(), false);
+
+ input.setName("/name");
+ input.setFaceId(2634);
+ input.setUri("udp4://192.0.2.1:6363");
+ input.setLocalUri("udp4://192.0.2.2:6363");
+ input.setOrigin(ROUTE_ORIGIN_NLSR);
+ input.setCost(1388);
+ input.setCapacity(2632);
+ input.setCount(3100);
+ input.setFlags(0xAFC4);
+ input.setMask(0xF7A1);
+ input.setStrategy("/strategy-name");
+ input.setExpirationPeriod(1800000_ms);
+ input.setFacePersistency(FacePersistency::FACE_PERSISTENCY_PERSISTENT);
+
+ decoded.wireDecode(input.wireEncode());
+ BOOST_CHECK_EQUAL(decoded.hasName(), true);
+ BOOST_CHECK_EQUAL(decoded.hasFaceId(), true);
+ BOOST_CHECK_EQUAL(decoded.hasUri(), true);
+ BOOST_CHECK_EQUAL(decoded.hasLocalUri(), true);
+ BOOST_CHECK_EQUAL(decoded.hasOrigin(), true);
+ BOOST_CHECK_EQUAL(decoded.hasCost(), true);
+ BOOST_CHECK_EQUAL(decoded.hasCapacity(), true);
+ BOOST_CHECK_EQUAL(decoded.hasCount(), true);
+ BOOST_CHECK_EQUAL(decoded.hasFlags(), true);
+ BOOST_CHECK_EQUAL(decoded.hasMask(), true);
+ BOOST_CHECK_EQUAL(decoded.hasStrategy(), true);
+ BOOST_CHECK_EQUAL(decoded.hasExpirationPeriod(), true);
+ BOOST_CHECK_EQUAL(decoded.hasFacePersistency(), true);
+
+ BOOST_CHECK_EQUAL(decoded.getName(), "/name");
+ BOOST_CHECK_EQUAL(decoded.getFaceId(), 2634);
+ BOOST_CHECK_EQUAL(decoded.getUri(), "udp4://192.0.2.1:6363");
+ BOOST_CHECK_EQUAL(decoded.getLocalUri(), "udp4://192.0.2.2:6363");
+ BOOST_CHECK_EQUAL(decoded.getOrigin(), ROUTE_ORIGIN_NLSR);
+ BOOST_CHECK_EQUAL(decoded.getCost(), 1388);
+ BOOST_CHECK_EQUAL(decoded.getCapacity(), 2632);
+ BOOST_CHECK_EQUAL(decoded.getCount(), 3100);
+ BOOST_CHECK_EQUAL(decoded.getFlags(), 0xAFC4);
+ BOOST_CHECK_EQUAL(decoded.getMask(), 0xF7A1);
+ BOOST_CHECK_EQUAL(decoded.getStrategy(), "/strategy-name");
+ BOOST_CHECK_EQUAL(decoded.getExpirationPeriod(), 1800000_ms);
+ BOOST_CHECK_EQUAL(decoded.getFacePersistency(), FacePersistency::FACE_PERSISTENCY_PERSISTENT);
+
+ input.unsetName();
+ input.unsetFaceId();
+ input.unsetUri();
+ input.unsetLocalUri();
+ input.unsetOrigin();
+ input.unsetCost();
+ input.unsetCapacity();
+ input.unsetCount();
+ input.unsetFlags();
+ input.unsetMask();
+ input.unsetStrategy();
+ input.unsetExpirationPeriod();
+ input.unsetFacePersistency();
+ BOOST_CHECK_EQUAL(input.hasName(), false);
+ BOOST_CHECK_EQUAL(input.hasFaceId(), false);
+ BOOST_CHECK_EQUAL(input.hasUri(), false);
+ BOOST_CHECK_EQUAL(input.hasLocalUri(), false);
+ BOOST_CHECK_EQUAL(input.hasOrigin(), false);
+ BOOST_CHECK_EQUAL(input.hasCost(), false);
+ BOOST_CHECK_EQUAL(input.hasCapacity(), false);
+ BOOST_CHECK_EQUAL(input.hasCount(), false);
+ BOOST_CHECK_EQUAL(input.hasFlags(), false);
+ BOOST_CHECK_EQUAL(input.hasMask(), false);
+ BOOST_CHECK_EQUAL(input.hasStrategy(), false);
+ BOOST_CHECK_EQUAL(input.hasExpirationPeriod(), false);
+ BOOST_CHECK_EQUAL(input.hasFacePersistency(), false);
+}
+
+BOOST_AUTO_TEST_CASE(FlagsAndMask)
+{
+ ControlParameters p;
+
+ BOOST_CHECK(!p.hasFlags());
+ BOOST_CHECK(!p.hasMask());
+ BOOST_CHECK(!p.hasFlagBit(0));
+ BOOST_CHECK(!p.getFlagBit(0));
+
+ // Set bit 2 to true (change Mask)
+ p.setFlagBit(2, true);
+ // 2^2 = 4
+ BOOST_CHECK_EQUAL(p.getFlags(), 4);
+ BOOST_CHECK_EQUAL(p.getMask(), 4);
+ BOOST_CHECK(p.hasFlagBit(2));
+ BOOST_CHECK(p.getFlagBit(2));
+ BOOST_CHECK(!p.hasFlagBit(1));
+ BOOST_CHECK(!p.getFlagBit(1));
+
+ // Set bit 3 to true (no change to Mask)
+ p.setFlagBit(3, true, false);
+ // 2^3 + 2^2 = 12
+ BOOST_CHECK_EQUAL(p.getFlags(), 12);
+ // 2^2 = 4
+ BOOST_CHECK_EQUAL(p.getMask(), 4);
+ BOOST_CHECK(!p.hasFlagBit(3));
+ BOOST_CHECK(p.getFlagBit(3));
+ BOOST_CHECK(p.hasFlagBit(2));
+ BOOST_CHECK(p.getFlagBit(2));
+
+ // Set bit 1 to false (change Mask)
+ p.setFlagBit(1, false);
+ // 2^3 + 2^2 = 12
+ BOOST_CHECK_EQUAL(p.getFlags(), 12);
+ // 2^2 + 2^1 = 6
+ BOOST_CHECK_EQUAL(p.getMask(), 6);
+ BOOST_CHECK(!p.hasFlagBit(3));
+ BOOST_CHECK(p.getFlagBit(3));
+ BOOST_CHECK(p.hasFlagBit(2));
+ BOOST_CHECK(p.getFlagBit(2));
+ BOOST_CHECK(p.hasFlagBit(1));
+ BOOST_CHECK(!p.getFlagBit(1));
+
+ // Set bit 2 to false (change Mask)
+ p.setFlagBit(2, false);
+ // 2^3 = 8
+ BOOST_CHECK_EQUAL(p.getFlags(), 8);
+ // 2^2 + 2^1 = 6
+ BOOST_CHECK_EQUAL(p.getMask(), 6);
+ BOOST_CHECK(!p.hasFlagBit(3));
+ BOOST_CHECK(p.getFlagBit(3));
+ BOOST_CHECK(p.hasFlagBit(2));
+ BOOST_CHECK(!p.getFlagBit(2));
+ BOOST_CHECK(p.hasFlagBit(1));
+ BOOST_CHECK(!p.getFlagBit(1));
+
+ // Set bit 0 to true (change Mask)
+ p.setFlagBit(0, true);
+ // 2^3 + 2^0 = 9
+ BOOST_CHECK_EQUAL(p.getFlags(), 9);
+ // 2^2 + 2^1 + 2^0 = 7
+ BOOST_CHECK_EQUAL(p.getMask(), 7);
+ BOOST_CHECK(p.hasFlagBit(0));
+ BOOST_CHECK(p.getFlagBit(0));
+
+ // Unset bit 0
+ p.unsetFlagBit(0);
+ BOOST_REQUIRE(p.hasFlags());
+ BOOST_REQUIRE(p.hasMask());
+ // 2^3 + 2^0 = 9
+ BOOST_CHECK_EQUAL(p.getFlags(), 9);
+ // 2^2 + 2^1 = 6
+ BOOST_CHECK_EQUAL(p.getMask(), 6);
+ BOOST_CHECK(p.hasFlagBit(1));
+ BOOST_CHECK(!p.hasFlagBit(0));
+ BOOST_CHECK(p.getFlagBit(0));
+
+ // Unset bit 3 (already unset in Mask, so no change)
+ p.unsetFlagBit(3);
+ BOOST_REQUIRE(p.hasFlags());
+ BOOST_REQUIRE(p.hasMask());
+ // 2^3 + 2^0 = 9
+ BOOST_CHECK_EQUAL(p.getFlags(), 9);
+ // 2^2 + 2^1 = 6
+ BOOST_CHECK_EQUAL(p.getMask(), 6);
+ BOOST_CHECK(!p.hasFlagBit(3));
+ BOOST_CHECK(p.getFlagBit(3));
+
+ // Unset bit 2
+ p.unsetFlagBit(2);
+ BOOST_REQUIRE(p.hasFlags());
+ BOOST_REQUIRE(p.hasMask());
+ // 2^3 + 2^0 = 9
+ BOOST_CHECK_EQUAL(p.getFlags(), 9);
+ // 2^1 = 2
+ BOOST_CHECK_EQUAL(p.getMask(), 2);
+ BOOST_CHECK(!p.hasFlagBit(2));
+ BOOST_CHECK(!p.getFlagBit(2));
+
+ // Unset bit 1
+ // Flags and Mask fields will be deleted as Mask is now 0
+ p.unsetFlagBit(1);
+ BOOST_CHECK(!p.hasFlags());
+ BOOST_CHECK(!p.hasMask());
+ BOOST_CHECK(!p.hasFlagBit(3));
+ BOOST_CHECK(!p.getFlagBit(3));
+ BOOST_CHECK(!p.hasFlagBit(2));
+ BOOST_CHECK(!p.getFlagBit(2));
+ BOOST_CHECK(!p.hasFlagBit(1));
+ BOOST_CHECK(!p.getFlagBit(1));
+ BOOST_CHECK(!p.hasFlagBit(0));
+ BOOST_CHECK(!p.getFlagBit(0));
+
+ BOOST_CHECK_THROW(p.setFlagBit(64, true), std::out_of_range);
+ BOOST_CHECK_THROW(p.unsetFlagBit(64), std::out_of_range);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestControlParameters
+BOOST_AUTO_TEST_SUITE_END() // Nfd
+BOOST_AUTO_TEST_SUITE_END() // Mgmt
+
+} // namespace tests
+} // namespace nfd
+} // namespace ndn
diff --git a/tests/unit/mgmt/nfd/controller-fixture.hpp b/tests/unit/mgmt/nfd/controller-fixture.hpp
new file mode 100644
index 0000000..0d14296
--- /dev/null
+++ b/tests/unit/mgmt/nfd/controller-fixture.hpp
@@ -0,0 +1,90 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library 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 Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#ifndef NDN_TESTS_MGMT_NFD_CONTROLLER_FIXTURE_HPP
+#define NDN_TESTS_MGMT_NFD_CONTROLLER_FIXTURE_HPP
+
+#include "mgmt/nfd/controller.hpp"
+#include "util/dummy-client-face.hpp"
+#include "security/v2/certificate-fetcher-offline.hpp"
+
+#include "boost-test.hpp"
+#include "dummy-validator.hpp"
+#include "../../identity-management-time-fixture.hpp"
+
+namespace ndn {
+namespace nfd {
+namespace tests {
+
+using namespace ndn::tests;
+
+class ControllerFixture : public IdentityManagementTimeFixture
+{
+protected:
+ ControllerFixture()
+ : face(io, m_keyChain)
+ , m_validator(true)
+ , controller(face, m_keyChain, m_validator)
+ , commandFailCallback(bind(&ControllerFixture::recordCommandFail, this, _1))
+ , datasetFailCallback(bind(&ControllerFixture::recordDatasetFail, this, _1, _2))
+ {
+ Name identityName("/localhost/ControllerFixture");
+ m_keyChain.setDefaultIdentity(this->addIdentity(identityName));
+ }
+
+ /** \brief controls whether Controller's validator should accept or reject validation requests
+ *
+ * Initially, the validator accepts all requests.
+ * Setting \p false causes validator to reject all requests.
+ */
+ void
+ setValidationResult(bool shouldAccept)
+ {
+ m_validator.getPolicy().setResult(shouldAccept);
+ }
+
+private:
+ void
+ recordCommandFail(const ControlResponse& response)
+ {
+ failCodes.push_back(response.getCode());
+ }
+
+ void
+ recordDatasetFail(uint32_t code, const std::string& reason)
+ {
+ failCodes.push_back(code);
+ }
+
+protected:
+ ndn::util::DummyClientFace face;
+ DummyValidator m_validator;
+ Controller controller;
+ Controller::CommandFailCallback commandFailCallback;
+ Controller::DatasetFailCallback datasetFailCallback;
+ std::vector<uint32_t> failCodes;
+};
+
+} // namespace tests
+} // namespace nfd
+} // namespace ndn
+
+#endif // NDN_TESTS_MGMT_NFD_CONTROLLER_FIXTURE_HPP
diff --git a/tests/unit/mgmt/nfd/controller.t.cpp b/tests/unit/mgmt/nfd/controller.t.cpp
new file mode 100644
index 0000000..e9a3c4f
--- /dev/null
+++ b/tests/unit/mgmt/nfd/controller.t.cpp
@@ -0,0 +1,273 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library 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 Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "mgmt/nfd/controller.hpp"
+#include "mgmt/nfd/control-response.hpp"
+
+#include "controller-fixture.hpp"
+#include "make-interest-data.hpp"
+
+namespace ndn {
+namespace nfd {
+namespace tests {
+
+using namespace ndn::tests;
+
+BOOST_AUTO_TEST_SUITE(Mgmt)
+BOOST_AUTO_TEST_SUITE(Nfd)
+
+class CommandFixture : public ControllerFixture
+{
+protected:
+ CommandFixture()
+ : succeedCallback(bind(&CommandFixture::succeed, this, _1))
+ {
+ }
+
+ void
+ respond(const ControlResponse& responsePayload)
+ {
+ auto responseData = makeData(face.sentInterests.at(0).getName());
+ responseData->setContent(responsePayload.wireEncode());
+ face.receive(*responseData);
+ this->advanceClocks(1_ms);
+ }
+
+private:
+ void
+ succeed(const ControlParameters& parameters)
+ {
+ succeeds.push_back(parameters);
+ }
+
+protected:
+ Controller::CommandSucceedCallback succeedCallback;
+ std::vector<ControlParameters> succeeds;
+};
+
+// This test suite focuses on ControlCommand functionality of Controller.
+// Individual commands are tested in nfd-control-command.t.cpp
+// StatusDataset functionality is tested in nfd-status-dataset.t.cpp
+BOOST_FIXTURE_TEST_SUITE(TestController, CommandFixture)
+
+static ControlParameters
+makeFaceCreateResponse()
+{
+ ControlParameters resp;
+ resp.setFaceId(22)
+ .setUri("tcp4://192.0.2.1:6363")
+ .setLocalUri("tcp4://192.0.2.2:10847")
+ .setFacePersistency(ndn::nfd::FacePersistency::FACE_PERSISTENCY_PERSISTENT)
+ .setFlags(0x7);
+ return resp;
+}
+
+BOOST_AUTO_TEST_CASE(Success)
+{
+ ControlParameters parameters;
+ parameters.setUri("tcp4://192.0.2.1:6363");
+
+ controller.start<FaceCreateCommand>(parameters, succeedCallback, commandFailCallback);
+ this->advanceClocks(1_ms);
+
+ BOOST_REQUIRE_EQUAL(face.sentInterests.size(), 1);
+ const Interest& requestInterest = face.sentInterests[0];
+
+ FaceCreateCommand command;
+ BOOST_CHECK(Name("/localhost/nfd/faces/create").isPrefixOf(requestInterest.getName()));
+ // 9 components: ndn:/localhost/nfd/faces/create/<parameters>/<signed Interest x4>
+ BOOST_REQUIRE_EQUAL(requestInterest.getName().size(), 9);
+ ControlParameters request;
+ // 4th component: <parameters>
+ BOOST_REQUIRE_NO_THROW(request.wireDecode(requestInterest.getName().at(4).blockFromValue()));
+ BOOST_CHECK_NO_THROW(command.validateRequest(request));
+ BOOST_CHECK_EQUAL(request.getUri(), parameters.getUri());
+ BOOST_CHECK_EQUAL(requestInterest.getInterestLifetime(), CommandOptions::DEFAULT_TIMEOUT);
+
+ ControlParameters responseBody = makeFaceCreateResponse();
+ ControlResponse responsePayload(201, "created");
+ responsePayload.setBody(responseBody.wireEncode());
+ this->respond(responsePayload);
+
+ BOOST_CHECK_EQUAL(failCodes.size(), 0);
+ BOOST_REQUIRE_EQUAL(succeeds.size(), 1);
+ BOOST_CHECK_EQUAL(succeeds.back().getUri(), responseBody.getUri());
+ BOOST_CHECK_EQUAL(succeeds.back().getFaceId(), responseBody.getFaceId());
+}
+
+BOOST_AUTO_TEST_CASE(SuccessNoCallback)
+{
+ ControlParameters parameters;
+ parameters.setUri("tcp4://192.0.2.1:6363");
+
+ controller.start<FaceCreateCommand>(parameters, nullptr, commandFailCallback);
+ this->advanceClocks(1_ms);
+
+ ControlParameters responseBody = makeFaceCreateResponse();
+ ControlResponse responsePayload(201, "created");
+ responsePayload.setBody(responseBody.wireEncode());
+ this->respond(responsePayload);
+
+ BOOST_CHECK_EQUAL(failCodes.size(), 0);
+}
+
+BOOST_AUTO_TEST_CASE(OptionsPrefix)
+{
+ ControlParameters parameters;
+ parameters.setName("/ndn/com/example");
+ parameters.setFaceId(400);
+
+ CommandOptions options;
+ options.setPrefix("/localhop/net/example/router1/nfd");
+
+ controller.start<RibRegisterCommand>(parameters, succeedCallback, commandFailCallback, options);
+ this->advanceClocks(1_ms);
+
+ BOOST_REQUIRE_EQUAL(face.sentInterests.size(), 1);
+ const Interest& requestInterest = face.sentInterests[0];
+
+ FaceCreateCommand command;
+ BOOST_CHECK(Name("/localhop/net/example/router1/nfd/rib/register").isPrefixOf(requestInterest.getName()));
+}
+
+BOOST_AUTO_TEST_CASE(InvalidRequest)
+{
+ ControlParameters parameters;
+ parameters.setName("ndn:/should-not-have-this-field");
+ // Uri is missing
+
+ BOOST_CHECK_THROW(controller.start<FaceCreateCommand>(
+ parameters, succeedCallback, commandFailCallback),
+ ControlCommand::ArgumentError);
+}
+
+BOOST_AUTO_TEST_CASE(ValidationFailure)
+{
+ this->setValidationResult(false);
+
+ ControlParameters parameters;
+ parameters.setUri("tcp4://192.0.2.1:6363");
+
+ controller.start<FaceCreateCommand>(parameters, succeedCallback, commandFailCallback);
+ this->advanceClocks(1_ms);
+
+ ControlParameters responseBody = makeFaceCreateResponse();
+ ControlResponse responsePayload(201, "created");
+ responsePayload.setBody(responseBody.wireEncode());
+ this->respond(responsePayload);
+
+ BOOST_CHECK_EQUAL(succeeds.size(), 0);
+ BOOST_REQUIRE_EQUAL(failCodes.size(), 1);
+ BOOST_CHECK_EQUAL(failCodes.back(), Controller::ERROR_VALIDATION);
+}
+
+BOOST_AUTO_TEST_CASE(ErrorCode)
+{
+ ControlParameters parameters;
+ parameters.setUri("tcp4://192.0.2.1:6363");
+
+ controller.start<FaceCreateCommand>(parameters, succeedCallback, commandFailCallback);
+ this->advanceClocks(1_ms);
+
+ ControlResponse responsePayload(401, "Not Authenticated");
+ this->respond(responsePayload);
+
+ BOOST_CHECK_EQUAL(succeeds.size(), 0);
+ BOOST_REQUIRE_EQUAL(failCodes.size(), 1);
+ BOOST_CHECK_EQUAL(failCodes.back(), 401);
+}
+
+BOOST_AUTO_TEST_CASE(InvalidResponse)
+{
+ ControlParameters parameters;
+ parameters.setUri("tcp4://192.0.2.1:6363");
+
+ controller.start<FaceCreateCommand>(parameters, succeedCallback, commandFailCallback);
+ this->advanceClocks(1_ms);
+
+ ControlParameters responseBody = makeFaceCreateResponse();
+ responseBody.unsetFaceId() // FaceId is missing
+ .setName("ndn:/should-not-have-this-field");
+ ControlResponse responsePayload(201, "created");
+ responsePayload.setBody(responseBody.wireEncode());
+ this->respond(responsePayload);
+
+ BOOST_CHECK_EQUAL(succeeds.size(), 0);
+ BOOST_REQUIRE_EQUAL(failCodes.size(), 1);
+}
+
+BOOST_AUTO_TEST_CASE(Nack)
+{
+ ControlParameters parameters;
+ parameters.setUri("tcp4://192.0.2.1:6363");
+
+ controller.start<FaceCreateCommand>(parameters, succeedCallback, commandFailCallback);
+ this->advanceClocks(1_ms);
+
+ BOOST_REQUIRE_EQUAL(face.sentInterests.size(), 1);
+ const Interest& requestInterest = face.sentInterests[0];
+
+ auto responseNack = makeNack(requestInterest, lp::NackReason::NO_ROUTE);
+ face.receive(responseNack);
+ this->advanceClocks(1_ms);
+
+ BOOST_REQUIRE_EQUAL(failCodes.size(), 1);
+ BOOST_CHECK_EQUAL(failCodes.back(), Controller::ERROR_NACK);
+}
+
+BOOST_AUTO_TEST_CASE(Timeout)
+{
+ ControlParameters parameters;
+ parameters.setUri("tcp4://192.0.2.1:6363");
+
+ CommandOptions options;
+ options.setTimeout(50_ms);
+
+ controller.start<FaceCreateCommand>(parameters, succeedCallback, commandFailCallback, options);
+ this->advanceClocks(1_ms); // express Interest
+ this->advanceClocks(51_ms); // timeout
+
+ BOOST_REQUIRE_EQUAL(failCodes.size(), 1);
+ BOOST_CHECK_EQUAL(failCodes.back(), Controller::ERROR_TIMEOUT);
+}
+
+BOOST_AUTO_TEST_CASE(FailureNoCallback)
+{
+ ControlParameters parameters;
+ parameters.setUri("tcp4://192.0.2.1:6363");
+
+ CommandOptions options;
+ options.setTimeout(50_ms);
+
+ controller.start<FaceCreateCommand>(parameters, succeedCallback, nullptr, options);
+ this->advanceClocks(1_ms); // express Interest
+ this->advanceClocks(51_ms); // timeout
+
+ BOOST_CHECK_EQUAL(succeeds.size(), 0);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestController
+BOOST_AUTO_TEST_SUITE_END() // Nfd
+BOOST_AUTO_TEST_SUITE_END() // Mgmt
+
+} // namespace tests
+} // namespace nfd
+} // namespace ndn
diff --git a/tests/unit/mgmt/nfd/cs-info.t.cpp b/tests/unit/mgmt/nfd/cs-info.t.cpp
new file mode 100644
index 0000000..46a0ed3
--- /dev/null
+++ b/tests/unit/mgmt/nfd/cs-info.t.cpp
@@ -0,0 +1,127 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library 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 Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "mgmt/nfd/cs-info.hpp"
+
+#include "boost-test.hpp"
+#include <boost/lexical_cast.hpp>
+
+namespace ndn {
+namespace nfd {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Mgmt)
+BOOST_AUTO_TEST_SUITE(Nfd)
+BOOST_AUTO_TEST_SUITE(TestCsInfo)
+
+static CsInfo
+makeCsInfo()
+{
+ return CsInfo()
+ .setCapacity(20177)
+ .setEnableAdmit(false)
+ .setEnableServe(true)
+ .setNEntries(5509)
+ .setNHits(12951)
+ .setNMisses(28179);
+}
+
+BOOST_AUTO_TEST_CASE(Encode)
+{
+ CsInfo csi1 = makeCsInfo();
+ Block wire = csi1.wireEncode();
+
+ static const uint8_t EXPECTED[] = {
+ 0x80, 0x13, // CsInfo
+ 0x83, 0x02, 0x4E, 0xD1, // Capacity
+ 0x6C, 0x01, 0x02, // Flags
+ 0x87, 0x02, 0x15, 0x85, // NCsEntries
+ 0x81, 0x02, 0x32, 0x97, // NHits
+ 0x82, 0x02, 0x6E, 0x13, // NMisses
+ };
+ BOOST_CHECK_EQUAL_COLLECTIONS(wire.begin(), wire.end(), EXPECTED, EXPECTED + sizeof(EXPECTED));
+
+ CsInfo csi2(wire);
+ BOOST_CHECK_EQUAL(csi2.getCapacity(), 20177);
+ BOOST_CHECK_EQUAL(csi2.getEnableAdmit(), false);
+ BOOST_CHECK_EQUAL(csi2.getEnableServe(), true);
+ BOOST_CHECK_EQUAL(csi2.getNEntries(), 5509);
+ BOOST_CHECK_EQUAL(csi2.getNHits(), 12951);
+ BOOST_CHECK_EQUAL(csi2.getNMisses(), 28179);
+}
+
+BOOST_AUTO_TEST_CASE(Equality)
+{
+ CsInfo csi1, csi2;
+ BOOST_CHECK_EQUAL(csi1, csi2);
+
+ csi1 = makeCsInfo();
+ BOOST_CHECK_NE(csi1, csi2);
+ csi2 = csi1;
+ BOOST_CHECK_EQUAL(csi1, csi2);
+
+ csi2.setCapacity(csi2.getCapacity() + 1);
+ BOOST_CHECK_NE(csi1, csi2);
+ csi2 = csi1;
+
+ csi2.setEnableAdmit(!csi2.getEnableAdmit());
+ BOOST_CHECK_NE(csi1, csi2);
+ csi2 = csi1;
+
+ csi2.setEnableServe(!csi2.getEnableServe());
+ BOOST_CHECK_NE(csi1, csi2);
+ csi2 = csi1;
+
+ csi2.setNEntries(csi2.getNEntries() + 1);
+ BOOST_CHECK_NE(csi1, csi2);
+ csi2 = csi1;
+
+ csi2.setNHits(csi2.getNHits() + 1);
+ BOOST_CHECK_NE(csi1, csi2);
+ csi2 = csi1;
+
+ csi2.setNMisses(csi2.getNMisses() + 1);
+ BOOST_CHECK_NE(csi1, csi2);
+ csi2 = csi1;
+}
+
+BOOST_AUTO_TEST_CASE(Print)
+{
+ CsInfo csi;
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(csi),
+ "CS: 0 entries, 0 max, admit disabled, serve disabled, 0 hits, 0 misses");
+
+ csi = makeCsInfo();
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(csi),
+ "CS: 5509 entries, 20177 max, admit disabled, serve enabled, 12951 hits, 28179 misses");
+
+ csi.setEnableAdmit(true).setNHits(1).setNMisses(1);
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(csi),
+ "CS: 5509 entries, 20177 max, admit enabled, serve enabled, 1 hit, 1 miss");
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestCsInfo
+BOOST_AUTO_TEST_SUITE_END() // Nfd
+BOOST_AUTO_TEST_SUITE_END() // Mgmt
+
+} // namespace tests
+} // namespace nfd
+} // namespace ndn
diff --git a/tests/unit/mgmt/nfd/face-event-notification.t.cpp b/tests/unit/mgmt/nfd/face-event-notification.t.cpp
new file mode 100644
index 0000000..4da762b
--- /dev/null
+++ b/tests/unit/mgmt/nfd/face-event-notification.t.cpp
@@ -0,0 +1,269 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library 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 Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "mgmt/nfd/face-event-notification.hpp"
+
+#include "boost-test.hpp"
+#include <boost/lexical_cast.hpp>
+
+namespace ndn {
+namespace nfd {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Mgmt)
+BOOST_AUTO_TEST_SUITE(Nfd)
+BOOST_AUTO_TEST_SUITE(TestFaceEventNotification)
+
+BOOST_AUTO_TEST_CASE(Traits)
+{
+ FaceEventNotification notification;
+ BOOST_CHECK_EQUAL(notification.getFaceScope(), FACE_SCOPE_NON_LOCAL);
+ BOOST_CHECK_EQUAL(notification.getFacePersistency(), FACE_PERSISTENCY_PERSISTENT);
+ BOOST_CHECK_EQUAL(notification.getLinkType(), LINK_TYPE_POINT_TO_POINT);
+
+ notification.setFaceScope(FACE_SCOPE_LOCAL);
+ BOOST_CHECK_EQUAL(notification.getFaceScope(), FACE_SCOPE_LOCAL);
+ BOOST_CHECK_EQUAL(notification.getFacePersistency(), FACE_PERSISTENCY_PERSISTENT);
+ BOOST_CHECK_EQUAL(notification.getLinkType(), LINK_TYPE_POINT_TO_POINT);
+
+ notification.setFacePersistency(FACE_PERSISTENCY_ON_DEMAND);
+ BOOST_CHECK_EQUAL(notification.getFaceScope(), FACE_SCOPE_LOCAL);
+ BOOST_CHECK_EQUAL(notification.getFacePersistency(), FACE_PERSISTENCY_ON_DEMAND);
+ BOOST_CHECK_EQUAL(notification.getLinkType(), LINK_TYPE_POINT_TO_POINT);
+
+ notification.setFacePersistency(FACE_PERSISTENCY_PERMANENT);
+ BOOST_CHECK_EQUAL(notification.getFaceScope(), FACE_SCOPE_LOCAL);
+ BOOST_CHECK_EQUAL(notification.getFacePersistency(), FACE_PERSISTENCY_PERMANENT);
+ BOOST_CHECK_EQUAL(notification.getLinkType(), LINK_TYPE_POINT_TO_POINT);
+
+ notification.setLinkType(LINK_TYPE_MULTI_ACCESS);
+ BOOST_CHECK_EQUAL(notification.getFaceScope(), FACE_SCOPE_LOCAL);
+ BOOST_CHECK_EQUAL(notification.getFacePersistency(), FACE_PERSISTENCY_PERMANENT);
+ BOOST_CHECK_EQUAL(notification.getLinkType(), LINK_TYPE_MULTI_ACCESS);
+}
+
+BOOST_AUTO_TEST_CASE(Created)
+{
+ FaceEventNotification notification1;
+ notification1.setKind(FACE_EVENT_CREATED)
+ .setFaceId(20)
+ .setRemoteUri("tcp4://192.0.2.1:55555")
+ .setLocalUri("tcp4://192.0.2.2:6363")
+ .setFaceScope(FACE_SCOPE_LOCAL)
+ .setFacePersistency(FACE_PERSISTENCY_ON_DEMAND)
+ .setLinkType(LINK_TYPE_MULTI_ACCESS)
+ .setFlags(0x3);
+ Block wire = notification1.wireEncode();
+
+ // These octets are obtained by the snippet below.
+ // This check is intended to detect unexpected encoding change in the future.
+ // for (Buffer::const_iterator it = wire.begin(); it != wire.end(); ++it) {
+ // printf("0x%02x, ", *it);
+ // }
+ static const uint8_t expected[] = {
+ 0xc0, 0x41, 0xc1, 0x01, 0x01, 0x69, 0x01, 0x14, 0x72, 0x16,
+ 0x74, 0x63, 0x70, 0x34, 0x3a, 0x2f, 0x2f, 0x31, 0x39, 0x32,
+ 0x2e, 0x30, 0x2e, 0x32, 0x2e, 0x31, 0x3a, 0x35, 0x35, 0x35,
+ 0x35, 0x35, 0x81, 0x15, 0x74, 0x63, 0x70, 0x34, 0x3a, 0x2f,
+ 0x2f, 0x31, 0x39, 0x32, 0x2e, 0x30, 0x2e, 0x32, 0x2e, 0x32,
+ 0x3a, 0x36, 0x33, 0x36, 0x33, 0x84, 0x01, 0x01, 0x85, 0x01,
+ 0x01, 0x86, 0x01, 0x01, 0x6c, 0x01, 0x03,
+ };
+ BOOST_CHECK_EQUAL_COLLECTIONS(expected, expected + sizeof(expected),
+ wire.begin(), wire.end());
+
+ FaceEventNotification notification2(wire);
+ BOOST_CHECK_EQUAL(notification1, notification2);
+
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(notification2),
+ "FaceEvent(Kind: created,\n"
+ " FaceId: 20,\n"
+ " RemoteUri: tcp4://192.0.2.1:55555,\n"
+ " LocalUri: tcp4://192.0.2.2:6363,\n"
+ " FaceScope: local,\n"
+ " FacePersistency: on-demand,\n"
+ " LinkType: multi-access,\n"
+ " Flags: 0x3\n"
+ " )");
+}
+
+BOOST_AUTO_TEST_CASE(Destroyed)
+{
+ FaceEventNotification notification1;
+ notification1.setKind(FACE_EVENT_DESTROYED)
+ .setFaceId(20)
+ .setRemoteUri("tcp4://192.0.2.1:55555")
+ .setLocalUri("tcp4://192.0.2.2:6363")
+ .setFaceScope(FACE_SCOPE_LOCAL)
+ .setFacePersistency(FACE_PERSISTENCY_ON_DEMAND)
+ .setLinkType(LINK_TYPE_MULTI_ACCESS)
+ .setFlags(0x4);
+ Block wire = notification1.wireEncode();
+
+ // These octets are obtained by the snippet below.
+ // This check is intended to detect unexpected encoding change in the future.
+ // for (Buffer::const_iterator it = wire.begin(); it != wire.end(); ++it) {
+ // printf("0x%02x, ", *it);
+ // }
+ static const uint8_t expected[] = {
+ 0xc0, 0x41, 0xc1, 0x01, 0x02, 0x69, 0x01, 0x14, 0x72, 0x16,
+ 0x74, 0x63, 0x70, 0x34, 0x3a, 0x2f, 0x2f, 0x31, 0x39, 0x32,
+ 0x2e, 0x30, 0x2e, 0x32, 0x2e, 0x31, 0x3a, 0x35, 0x35, 0x35,
+ 0x35, 0x35, 0x81, 0x15, 0x74, 0x63, 0x70, 0x34, 0x3a, 0x2f,
+ 0x2f, 0x31, 0x39, 0x32, 0x2e, 0x30, 0x2e, 0x32, 0x2e, 0x32,
+ 0x3a, 0x36, 0x33, 0x36, 0x33, 0x84, 0x01, 0x01, 0x85, 0x01,
+ 0x01, 0x86, 0x01, 0x01, 0x6c, 0x01, 0x04,
+ };
+ BOOST_CHECK_EQUAL_COLLECTIONS(expected, expected + sizeof(expected),
+ wire.begin(), wire.end());
+
+ FaceEventNotification notification2(wire);
+ BOOST_CHECK_EQUAL(notification1, notification2);
+
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(notification2),
+ "FaceEvent(Kind: destroyed,\n"
+ " FaceId: 20,\n"
+ " RemoteUri: tcp4://192.0.2.1:55555,\n"
+ " LocalUri: tcp4://192.0.2.2:6363,\n"
+ " FaceScope: local,\n"
+ " FacePersistency: on-demand,\n"
+ " LinkType: multi-access,\n"
+ " Flags: 0x4\n"
+ " )");
+}
+
+BOOST_AUTO_TEST_CASE(Up)
+{
+ FaceEventNotification notification1;
+ notification1.setKind(FACE_EVENT_UP)
+ .setFaceId(20)
+ .setRemoteUri("tcp4://192.0.2.1:55555")
+ .setLocalUri("tcp4://192.0.2.2:6363")
+ .setFaceScope(FACE_SCOPE_LOCAL)
+ .setFacePersistency(FACE_PERSISTENCY_ON_DEMAND)
+ .setLinkType(LINK_TYPE_MULTI_ACCESS)
+ .setFlags(0x05);
+ Block wire = notification1.wireEncode();
+
+ // These octets are obtained by the snippet below.
+ // This check is intended to detect unexpected encoding change in the future.
+ // for (Buffer::const_iterator it = wire.begin(); it != wire.end(); ++it) {
+ // printf("0x%02x, ", *it);
+ // }
+ static const uint8_t expected[] = {
+ 0xc0, 0x41, 0xc1, 0x01, 0x03, 0x69, 0x01, 0x14, 0x72, 0x16,
+ 0x74, 0x63, 0x70, 0x34, 0x3a, 0x2f, 0x2f, 0x31, 0x39, 0x32,
+ 0x2e, 0x30, 0x2e, 0x32, 0x2e, 0x31, 0x3a, 0x35, 0x35, 0x35,
+ 0x35, 0x35, 0x81, 0x15, 0x74, 0x63, 0x70, 0x34, 0x3a, 0x2f,
+ 0x2f, 0x31, 0x39, 0x32, 0x2e, 0x30, 0x2e, 0x32, 0x2e, 0x32,
+ 0x3a, 0x36, 0x33, 0x36, 0x33, 0x84, 0x01, 0x01, 0x85, 0x01,
+ 0x01, 0x86, 0x01, 0x01, 0x6c, 0x01, 0x05,
+ };
+ BOOST_CHECK_EQUAL_COLLECTIONS(expected, expected + sizeof(expected),
+ wire.begin(), wire.end());
+
+ FaceEventNotification notification2(wire);
+ BOOST_CHECK_EQUAL(notification1, notification2);
+
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(notification2),
+ "FaceEvent(Kind: up,\n"
+ " FaceId: 20,\n"
+ " RemoteUri: tcp4://192.0.2.1:55555,\n"
+ " LocalUri: tcp4://192.0.2.2:6363,\n"
+ " FaceScope: local,\n"
+ " FacePersistency: on-demand,\n"
+ " LinkType: multi-access,\n"
+ " Flags: 0x5\n"
+ " )");
+}
+
+BOOST_AUTO_TEST_CASE(Down)
+{
+ FaceEventNotification notification1;
+ notification1.setKind(FACE_EVENT_DOWN)
+ .setFaceId(20)
+ .setRemoteUri("tcp4://192.0.2.1:55555")
+ .setLocalUri("tcp4://192.0.2.2:6363")
+ .setFaceScope(FACE_SCOPE_LOCAL)
+ .setFacePersistency(FACE_PERSISTENCY_ON_DEMAND)
+ .setLinkType(LINK_TYPE_MULTI_ACCESS)
+ .setFlags(0x06);
+ Block wire = notification1.wireEncode();
+
+ // These octets are obtained by the snippet below.
+ // This check is intended to detect unexpected encoding change in the future.
+ // for (Buffer::const_iterator it = wire.begin(); it != wire.end(); ++it) {
+ // printf("0x%02x, ", *it);
+ // }
+ static const uint8_t expected[] = {
+ 0xc0, 0x41, 0xc1, 0x01, 0x04, 0x69, 0x01, 0x14, 0x72, 0x16,
+ 0x74, 0x63, 0x70, 0x34, 0x3a, 0x2f, 0x2f, 0x31, 0x39, 0x32,
+ 0x2e, 0x30, 0x2e, 0x32, 0x2e, 0x31, 0x3a, 0x35, 0x35, 0x35,
+ 0x35, 0x35, 0x81, 0x15, 0x74, 0x63, 0x70, 0x34, 0x3a, 0x2f,
+ 0x2f, 0x31, 0x39, 0x32, 0x2e, 0x30, 0x2e, 0x32, 0x2e, 0x32,
+ 0x3a, 0x36, 0x33, 0x36, 0x33, 0x84, 0x01, 0x01, 0x85, 0x01,
+ 0x01, 0x86, 0x01, 0x01, 0x6c, 0x01, 0x06,
+ };
+ BOOST_CHECK_EQUAL_COLLECTIONS(expected, expected + sizeof(expected),
+ wire.begin(), wire.end());
+
+ FaceEventNotification notification2(wire);
+ BOOST_CHECK_EQUAL(notification1, notification2);
+
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(notification2),
+ "FaceEvent(Kind: down,\n"
+ " FaceId: 20,\n"
+ " RemoteUri: tcp4://192.0.2.1:55555,\n"
+ " LocalUri: tcp4://192.0.2.2:6363,\n"
+ " FaceScope: local,\n"
+ " FacePersistency: on-demand,\n"
+ " LinkType: multi-access,\n"
+ " Flags: 0x6\n"
+ " )");
+}
+
+BOOST_AUTO_TEST_CASE(Equality)
+{
+ FaceEventNotification notification1, notification2;
+ BOOST_CHECK_EQUAL(notification1, notification2);
+
+ notification1.setKind(FACE_EVENT_CREATED)
+ .setFaceId(123)
+ .setRemoteUri("tcp4://192.0.2.1:55555")
+ .setLocalUri("tcp4://192.0.2.2:6363");
+ notification2 = notification1;
+ BOOST_CHECK_EQUAL(notification1, notification2);
+
+ notification2.setFaceId(42);
+ BOOST_CHECK_NE(notification1, notification2);
+
+ notification2 = notification1;
+ notification2.setKind(FACE_EVENT_DESTROYED);
+ BOOST_CHECK_NE(notification1, notification2);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestFaceEventNotification
+BOOST_AUTO_TEST_SUITE_END() // Nfd
+BOOST_AUTO_TEST_SUITE_END() // Mgmt
+
+} // namespace tests
+} // namespace nfd
+} // namespace ndn
diff --git a/tests/unit/mgmt/nfd/face-query-filter.t.cpp b/tests/unit/mgmt/nfd/face-query-filter.t.cpp
new file mode 100644
index 0000000..3b9b2a2
--- /dev/null
+++ b/tests/unit/mgmt/nfd/face-query-filter.t.cpp
@@ -0,0 +1,143 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library 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 Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "mgmt/nfd/face-query-filter.hpp"
+
+#include "boost-test.hpp"
+#include <boost/lexical_cast.hpp>
+
+namespace ndn {
+namespace nfd {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Mgmt)
+BOOST_AUTO_TEST_SUITE(Nfd)
+BOOST_AUTO_TEST_SUITE(TestFaceQueryFilter)
+
+BOOST_AUTO_TEST_CASE(Encode)
+{
+ FaceQueryFilter filter1;
+ BOOST_CHECK_EQUAL(filter1.hasFaceId(), false);
+ BOOST_CHECK_EQUAL(filter1.hasUriScheme(), false);
+ BOOST_CHECK_EQUAL(filter1.hasRemoteUri(), false);
+ BOOST_CHECK_EQUAL(filter1.hasLocalUri(), false);
+ BOOST_CHECK_EQUAL(filter1.hasFaceScope(), false);
+ BOOST_CHECK_EQUAL(filter1.hasFacePersistency(), false);
+ BOOST_CHECK_EQUAL(filter1.hasLinkType(), false);
+
+ filter1.setFaceId(100)
+ .setUriScheme("tcp4")
+ .setRemoteUri("tcp4://192.0.2.1:6363")
+ .setLocalUri("tcp4://192.0.2.2:55555")
+ .setFaceScope(FACE_SCOPE_LOCAL)
+ .setFacePersistency(FACE_PERSISTENCY_ON_DEMAND)
+ .setLinkType(LINK_TYPE_MULTI_ACCESS);
+
+ Block wire = filter1.wireEncode();
+
+ // These octets are obtained by the snippet below.
+ // This check is intended to detect unexpected encoding change in the future.
+ // for (Buffer::const_iterator it = wire.begin(); it != wire.end(); ++it) {
+ // printf("0x%02x, ", *it);
+ // }
+ static const uint8_t expected[] = {
+ 0x96, 0x41, 0x69, 0x01, 0x64, 0x83, 0x04, 0x74, 0x63, 0x70,
+ 0x34, 0x72, 0x15, 0x74, 0x63, 0x70, 0x34, 0x3a, 0x2f, 0x2f,
+ 0x31, 0x39, 0x32, 0x2e, 0x30, 0x2e, 0x32, 0x2e, 0x31, 0x3a,
+ 0x36, 0x33, 0x36, 0x33, 0x81, 0x16, 0x74, 0x63, 0x70, 0x34,
+ 0x3a, 0x2f, 0x2f, 0x31, 0x39, 0x32, 0x2e, 0x30, 0x2e, 0x32,
+ 0x2e, 0x32, 0x3a, 0x35, 0x35, 0x35, 0x35, 0x35, 0x84, 0x01,
+ 0x01, 0x85, 0x01, 0x01, 0x86, 0x01, 0x01,
+ };
+
+ BOOST_CHECK_EQUAL_COLLECTIONS(expected, expected + sizeof(expected),
+ wire.begin(), wire.end());
+
+ FaceQueryFilter filter2(wire);
+ BOOST_CHECK_EQUAL(filter1.getFaceId(), filter2.getFaceId());
+ BOOST_CHECK_EQUAL(filter1.getUriScheme(), filter2.getUriScheme());
+ BOOST_CHECK_EQUAL(filter1.getRemoteUri(), filter2.getRemoteUri());
+ BOOST_CHECK_EQUAL(filter1.getLocalUri(), filter2.getLocalUri());
+ BOOST_CHECK_EQUAL(filter1.getFaceScope(), filter2.getFaceScope());
+ BOOST_CHECK_EQUAL(filter1.getFacePersistency(), filter2.getFacePersistency());
+ BOOST_CHECK_EQUAL(filter1.getLinkType(), filter2.getLinkType());
+}
+
+BOOST_AUTO_TEST_CASE(Equality)
+{
+ FaceQueryFilter filter1, filter2;
+ BOOST_CHECK_EQUAL(filter1.empty(), true);
+ BOOST_CHECK_EQUAL(filter1, filter2);
+
+ filter1.setFaceId(100)
+ .setUriScheme("tcp4")
+ .setRemoteUri("tcp4://192.0.2.1:6363")
+ .setLocalUri("tcp4://192.0.2.2:55555")
+ .setFaceScope(FACE_SCOPE_LOCAL)
+ .setFacePersistency(FACE_PERSISTENCY_ON_DEMAND)
+ .setLinkType(LINK_TYPE_MULTI_ACCESS);
+ BOOST_CHECK_EQUAL(filter1.empty(), false);
+ BOOST_CHECK_NE(filter1, filter2);
+
+ filter2 = filter1;
+ BOOST_CHECK_EQUAL(filter1, filter2);
+
+ filter2.setFaceScope(FACE_SCOPE_NON_LOCAL);
+ BOOST_CHECK_NE(filter1, filter2);
+}
+
+BOOST_AUTO_TEST_CASE(Print)
+{
+ FaceQueryFilter filter;
+ filter.setFaceId(100)
+ .setUriScheme("tcp4")
+ .setRemoteUri("tcp4://192.0.2.1:6363")
+ .setLocalUri("tcp4://192.0.2.2:55555")
+ .setFaceScope(FACE_SCOPE_LOCAL)
+ .setFacePersistency(FACE_PERSISTENCY_ON_DEMAND)
+ .setLinkType(LINK_TYPE_MULTI_ACCESS);
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(filter),
+ "FaceQueryFilter(FaceID: 100,\n"
+ "UriScheme: tcp4,\n"
+ "RemoteUri: tcp4://192.0.2.1:6363,\n"
+ "LocalUri: tcp4://192.0.2.2:55555,\n"
+ "FaceScope: local,\n"
+ "FacePersistency: on-demand,\n"
+ "LinkType: multi-access,\n"
+ ")");
+
+ filter.unsetFaceId()
+ .unsetUriScheme()
+ .unsetRemoteUri()
+ .unsetLocalUri()
+ .unsetFaceScope()
+ .unsetFacePersistency()
+ .unsetLinkType();
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(filter), "FaceQueryFilter()");
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestFaceQueryFilter
+BOOST_AUTO_TEST_SUITE_END() // Nfd
+BOOST_AUTO_TEST_SUITE_END() // Mgmt
+
+} // namespace tests
+} // namespace nfd
+} // namespace ndn
diff --git a/tests/unit/mgmt/nfd/face-status.t.cpp b/tests/unit/mgmt/nfd/face-status.t.cpp
new file mode 100644
index 0000000..0ed7aca
--- /dev/null
+++ b/tests/unit/mgmt/nfd/face-status.t.cpp
@@ -0,0 +1,179 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library 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 Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "mgmt/nfd/face-status.hpp"
+
+#include "boost-test.hpp"
+#include <boost/lexical_cast.hpp>
+
+namespace ndn {
+namespace nfd {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Mgmt)
+BOOST_AUTO_TEST_SUITE(Nfd)
+BOOST_AUTO_TEST_SUITE(TestFaceStatus)
+
+static FaceStatus
+makeFaceStatus()
+{
+ return FaceStatus()
+ .setFaceId(100)
+ .setRemoteUri("tcp4://192.0.2.1:6363")
+ .setLocalUri("tcp4://192.0.2.2:55555")
+ .setFaceScope(FACE_SCOPE_LOCAL)
+ .setFacePersistency(FACE_PERSISTENCY_ON_DEMAND)
+ .setLinkType(LINK_TYPE_MULTI_ACCESS)
+ .setExpirationPeriod(10_s)
+ .setBaseCongestionMarkingInterval(5_ns)
+ .setDefaultCongestionThreshold(7)
+ .setMtu(9)
+ .setNInInterests(10)
+ .setNInData(200)
+ .setNInNacks(1)
+ .setNOutInterests(3000)
+ .setNOutData(4)
+ .setNOutNacks(2)
+ .setNInBytes(1329719163)
+ .setNOutBytes(999110448)
+ .setFlags(0x7);
+}
+
+BOOST_AUTO_TEST_CASE(Encode)
+{
+ FaceStatus status1 = makeFaceStatus();
+ Block wire = status1.wireEncode();
+
+ // These octets are obtained by the snippet below.
+ // This check is intended to detect unexpected encoding change in the future.
+ // for (Buffer::const_iterator it = wire.begin(); it != wire.end(); ++it) {
+ // printf("0x%02x, ", *it);
+ // }
+ static const uint8_t expected[] = {
+ 0x80, 0x6a, 0x69, 0x01, 0x64, 0x72, 0x15, 0x74, 0x63, 0x70,
+ 0x34, 0x3a, 0x2f, 0x2f, 0x31, 0x39, 0x32, 0x2e, 0x30, 0x2e,
+ 0x32, 0x2e, 0x31, 0x3a, 0x36, 0x33, 0x36, 0x33, 0x81, 0x16,
+ 0x74, 0x63, 0x70, 0x34, 0x3a, 0x2f, 0x2f, 0x31, 0x39, 0x32,
+ 0x2e, 0x30, 0x2e, 0x32, 0x2e, 0x32, 0x3a, 0x35, 0x35, 0x35,
+ 0x35, 0x35, 0x6d, 0x02, 0x27, 0x10, 0x84, 0x01, 0x01, 0x85,
+ 0x01, 0x01, 0x86, 0x01, 0x01, 0x87, 0x01, 0x05, 0x88, 0x01,
+ 0x07, 0x89, 0x01, 0x09, 0x90, 0x01, 0x0a, 0x91, 0x01, 0xc8,
+ 0x97, 0x01, 0x01, 0x92, 0x02, 0x0b, 0xb8, 0x93, 0x01, 0x04,
+ 0x98, 0x01, 0x02, 0x94, 0x04, 0x4f, 0x41, 0xe7, 0x7b, 0x95,
+ 0x04, 0x3b, 0x8d, 0x37, 0x30, 0x6c, 0x01, 0x07,
+ };
+ BOOST_CHECK_EQUAL_COLLECTIONS(expected, expected + sizeof(expected),
+ wire.begin(), wire.end());
+
+ FaceStatus status2(wire);
+ BOOST_CHECK_EQUAL(status1, status2);
+}
+
+BOOST_AUTO_TEST_CASE(Equality)
+{
+ FaceStatus status1, status2;
+
+ status1 = makeFaceStatus();
+ status2 = status1;
+ BOOST_CHECK_EQUAL(status1, status2);
+
+ status2.setFaceId(42);
+ BOOST_CHECK_NE(status1, status2);
+}
+
+BOOST_AUTO_TEST_CASE(Print)
+{
+ FaceStatus status;
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(status),
+ "Face(FaceId: 0,\n"
+ " RemoteUri: ,\n"
+ " LocalUri: ,\n"
+ " ExpirationPeriod: infinite,\n"
+ " FaceScope: non-local,\n"
+ " FacePersistency: persistent,\n"
+ " LinkType: point-to-point,\n"
+ " Flags: 0x0,\n"
+ " Counters: {Interests: {in: 0, out: 0},\n"
+ " Data: {in: 0, out: 0},\n"
+ " Nacks: {in: 0, out: 0},\n"
+ " bytes: {in: 0, out: 0}}\n"
+ " )");
+
+ status = makeFaceStatus();
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(status),
+ "Face(FaceId: 100,\n"
+ " RemoteUri: tcp4://192.0.2.1:6363,\n"
+ " LocalUri: tcp4://192.0.2.2:55555,\n"
+ " ExpirationPeriod: 10000 milliseconds,\n"
+ " FaceScope: local,\n"
+ " FacePersistency: on-demand,\n"
+ " LinkType: multi-access,\n"
+ " BaseCongestionMarkingInterval: 5 nanoseconds,\n"
+ " DefaultCongestionThreshold: 7 bytes,\n"
+ " Mtu: 9 bytes,\n"
+ " Flags: 0x7,\n"
+ " Counters: {Interests: {in: 10, out: 3000},\n"
+ " Data: {in: 200, out: 4},\n"
+ " Nacks: {in: 1, out: 2},\n"
+ " bytes: {in: 1329719163, out: 999110448}}\n"
+ " )");
+}
+
+BOOST_AUTO_TEST_CASE(ExpirationPeriod)
+{
+ FaceStatus status;
+ BOOST_CHECK_EQUAL(status.hasExpirationPeriod(), false);
+
+ status.setExpirationPeriod(1_min);
+ BOOST_REQUIRE_EQUAL(status.hasExpirationPeriod(), true);
+ BOOST_CHECK_EQUAL(status.getExpirationPeriod(), 1_min);
+
+ status.unsetExpirationPeriod();
+ BOOST_CHECK_EQUAL(status.hasExpirationPeriod(), false);
+}
+
+BOOST_AUTO_TEST_CASE(FlagBit)
+{
+ FaceStatus status;
+ status.setFlags(0x7);
+ BOOST_CHECK_EQUAL(status.getFlags(), 0x7);
+
+ BOOST_CHECK(status.getFlagBit(0));
+ BOOST_CHECK(status.getFlagBit(1));
+ BOOST_CHECK(status.getFlagBit(2));
+ BOOST_CHECK(!status.getFlagBit(3));
+
+ status.setFlagBit(3, true);
+ BOOST_CHECK_EQUAL(status.getFlags(), 0xf);
+ BOOST_CHECK(status.getFlagBit(3));
+
+ status.setFlagBit(1, false);
+ BOOST_CHECK_EQUAL(status.getFlags(), 0xd);
+ BOOST_CHECK(!status.getFlagBit(1));
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestFaceStatus
+BOOST_AUTO_TEST_SUITE_END() // Nfd
+BOOST_AUTO_TEST_SUITE_END() // Mgmt
+
+} // namespace tests
+} // namespace nfd
+} // namespace ndn
diff --git a/tests/unit/mgmt/nfd/fib-entry.t.cpp b/tests/unit/mgmt/nfd/fib-entry.t.cpp
new file mode 100644
index 0000000..fa7ec69
--- /dev/null
+++ b/tests/unit/mgmt/nfd/fib-entry.t.cpp
@@ -0,0 +1,196 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library 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 Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "mgmt/nfd/fib-entry.hpp"
+
+#include "boost-test.hpp"
+#include <boost/lexical_cast.hpp>
+
+namespace ndn {
+namespace nfd {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Mgmt)
+BOOST_AUTO_TEST_SUITE(Nfd)
+BOOST_AUTO_TEST_SUITE(TestFibEntry)
+
+static FibEntry
+makeFibEntry()
+{
+ std::vector<NextHopRecord> nexthops;
+ for (size_t i = 1; i < 4; i++) {
+ nexthops.push_back(NextHopRecord()
+ .setFaceId(i * 10)
+ .setCost(i * 100 + 100));
+ }
+
+ return FibEntry()
+ .setPrefix("/this/is/a/test")
+ .setNextHopRecords(nexthops.begin(), nexthops.end());
+}
+
+BOOST_AUTO_TEST_CASE(NextHopRecordEncode)
+{
+ NextHopRecord record1;
+ record1.setFaceId(10)
+ .setCost(200);
+ const Block& wire = record1.wireEncode();
+
+ static const uint8_t expected[] = {
+ 0x81, 0x06, 0x69, 0x01, 0x0a, 0x6a, 0x01, 0xc8
+ };
+ BOOST_CHECK_EQUAL_COLLECTIONS(expected, expected + sizeof(expected),
+ wire.begin(), wire.end());
+
+ NextHopRecord record2(wire);
+ BOOST_CHECK_EQUAL(record1, record2);
+}
+
+BOOST_AUTO_TEST_CASE(NextHopRecordEquality)
+{
+ NextHopRecord record1, record2;
+
+ record1.setFaceId(10)
+ .setCost(200);
+ record2 = record1;
+ BOOST_CHECK_EQUAL(record1, record2);
+
+ record2.setFaceId(42);
+ BOOST_CHECK_NE(record1, record2);
+
+ record2 = record1;
+ record2.setCost(42);
+ BOOST_CHECK_NE(record1, record2);
+}
+
+BOOST_AUTO_TEST_CASE(FibEntryNoNextHopsEncode)
+{
+ FibEntry entry1;
+ entry1.setPrefix("/this/is/a/test");
+ BOOST_REQUIRE(entry1.getNextHopRecords().empty());
+ const Block& wire = entry1.wireEncode();
+
+ static const uint8_t expected[] = {
+ 0x80, 0x15, 0x07, 0x13, 0x08, 0x04, 0x74, 0x68, 0x69, 0x73,
+ 0x08, 0x02, 0x69, 0x73, 0x08, 0x01, 0x61, 0x08, 0x04, 0x74,
+ 0x65, 0x73, 0x74
+ };
+ BOOST_CHECK_EQUAL_COLLECTIONS(expected, expected + sizeof(expected),
+ wire.begin(), wire.end());
+
+ FibEntry entry2(wire);
+ BOOST_CHECK_EQUAL(entry1, entry2);
+}
+
+BOOST_AUTO_TEST_CASE(FibEntryEncode)
+{
+ FibEntry entry1 = makeFibEntry();
+ NextHopRecord oneMore;
+ oneMore.setFaceId(40);
+ oneMore.setCost(500);
+ entry1.addNextHopRecord(oneMore);
+ const Block& wire = entry1.wireEncode();
+
+ static const uint8_t expected[] = {
+ 0x80, 0x38, 0x07, 0x13, 0x08, 0x04, 0x74, 0x68, 0x69, 0x73, 0x08, 0x02, 0x69, 0x73, 0x08, 0x01,
+ 0x61, 0x08, 0x04, 0x74, 0x65, 0x73, 0x74, 0x81, 0x06, 0x69, 0x01, 0x0a, 0x6a, 0x01, 0xc8, 0x81,
+ 0x07, 0x69, 0x01, 0x14, 0x6a, 0x02, 0x01, 0x2c, 0x81, 0x07, 0x69, 0x01, 0x1e, 0x6a, 0x02, 0x01,
+ 0x90, 0x81, 0x07, 0x69, 0x01, 0x28, 0x6a, 0x02, 0x01, 0xf4
+ };
+ BOOST_CHECK_EQUAL_COLLECTIONS(expected, expected + sizeof(expected),
+ wire.begin(), wire.end());
+
+ FibEntry entry2(wire);
+ BOOST_CHECK_EQUAL(entry1, entry2);
+}
+
+BOOST_AUTO_TEST_CASE(FibEntryEquality)
+{
+ FibEntry entry1, entry2;
+ BOOST_CHECK_EQUAL(entry1, entry2);
+
+ entry1 = entry2 = makeFibEntry();
+ BOOST_CHECK_EQUAL(entry1, entry2);
+ BOOST_CHECK_EQUAL(entry2, entry1);
+
+ entry2.setPrefix("/another/prefix");
+ BOOST_CHECK_NE(entry1, entry2);
+
+ entry2 = entry1;
+ std::vector<NextHopRecord> empty;
+ entry2.setNextHopRecords(empty.begin(), empty.end());
+ BOOST_CHECK_NE(entry1, entry2);
+ BOOST_CHECK_NE(entry2, entry1);
+
+ entry2 = entry1;
+ auto nh1 = NextHopRecord()
+ .setFaceId(1)
+ .setCost(1000);
+ entry1.addNextHopRecord(nh1);
+ BOOST_CHECK_NE(entry1, entry2);
+ BOOST_CHECK_NE(entry2, entry1);
+
+ auto nh42 = NextHopRecord()
+ .setFaceId(42)
+ .setCost(42);
+ entry1.addNextHopRecord(nh42);
+ entry2.addNextHopRecord(nh42)
+ .addNextHopRecord(nh1);
+ BOOST_CHECK_EQUAL(entry1, entry2); // order of NextHopRecords is irrelevant
+ BOOST_CHECK_EQUAL(entry2, entry1);
+
+ entry1 = entry2 = makeFibEntry();
+ entry1.addNextHopRecord(nh1)
+ .addNextHopRecord(nh42);
+ entry2.addNextHopRecord(nh42)
+ .addNextHopRecord(nh42);
+ BOOST_CHECK_NE(entry1, entry2); // match each NextHopRecord at most once
+ BOOST_CHECK_NE(entry2, entry1);
+}
+
+BOOST_AUTO_TEST_CASE(Print)
+{
+ NextHopRecord record;
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(record),
+ "NextHopRecord(FaceId: 0, Cost: 0)");
+
+ FibEntry entry;
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(entry),
+ "FibEntry(Prefix: /,\n"
+ " NextHops: []\n"
+ " )");
+
+ entry = makeFibEntry();
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(entry),
+ "FibEntry(Prefix: /this/is/a/test,\n"
+ " NextHops: [NextHopRecord(FaceId: 10, Cost: 200),\n"
+ " NextHopRecord(FaceId: 20, Cost: 300),\n"
+ " NextHopRecord(FaceId: 30, Cost: 400)]\n"
+ " )");
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestFibEntry
+BOOST_AUTO_TEST_SUITE_END() // Nfd
+BOOST_AUTO_TEST_SUITE_END() // Mgmt
+
+} // namespace tests
+} // namespace nfd
+} // namespace ndn
diff --git a/tests/unit/mgmt/nfd/forwarder-status.t.cpp b/tests/unit/mgmt/nfd/forwarder-status.t.cpp
new file mode 100644
index 0000000..b2c3d48
--- /dev/null
+++ b/tests/unit/mgmt/nfd/forwarder-status.t.cpp
@@ -0,0 +1,141 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library 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 Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "mgmt/nfd/forwarder-status.hpp"
+
+#include "boost-test.hpp"
+#include <boost/lexical_cast.hpp>
+
+namespace ndn {
+namespace nfd {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Mgmt)
+BOOST_AUTO_TEST_SUITE(Nfd)
+BOOST_AUTO_TEST_SUITE(TestForwarderStatus)
+
+static ForwarderStatus
+makeForwarderStatus()
+{
+ return ForwarderStatus()
+ .setNfdVersion("0.5.1-14-g05dd444")
+ .setStartTimestamp(time::fromUnixTimestamp(time::milliseconds(375193249325LL)))
+ .setCurrentTimestamp(time::fromUnixTimestamp(time::milliseconds(886109034272LL)))
+ .setNNameTreeEntries(1849943160)
+ .setNFibEntries(621739748)
+ .setNPitEntries(482129741)
+ .setNMeasurementsEntries(1771725298)
+ .setNCsEntries(1264968688)
+ .setNInInterests(612811615)
+ .setNInData(1843576050)
+ .setNInNacks(1234)
+ .setNOutInterests(952144445)
+ .setNOutData(138198826)
+ .setNOutNacks(4321)
+ .setNSatisfiedInterests(961020)
+ .setNUnsatisfiedInterests(941024);
+}
+
+BOOST_AUTO_TEST_CASE(Encode)
+{
+ ForwarderStatus status1 = makeForwarderStatus();
+ Block wire = status1.wireEncode();
+
+ // These octets are obtained by the snippet below.
+ // This check is intended to detect unexpected encoding change in the future.
+ // for (Buffer::const_iterator it = wire.begin(); it != wire.end(); ++it) {
+ // printf("0x%02x, ", *it);
+ // }
+ static const uint8_t expected[] = {
+ 0x15, 0x71, 0x80, 0x11, 0x30, 0x2e, 0x35, 0x2e, 0x31, 0x2d, 0x31, 0x34,
+ 0x2d, 0x67, 0x30, 0x35, 0x64, 0x64, 0x34, 0x34, 0x34, 0x81, 0x08, 0x00,
+ 0x00, 0x00, 0x57, 0x5b, 0x42, 0xa6, 0x2d, 0x82, 0x08, 0x00, 0x00, 0x00,
+ 0xce, 0x50, 0x36, 0xd7, 0x20, 0x83, 0x04, 0x6e, 0x43, 0xe4, 0x78, 0x84,
+ 0x04, 0x25, 0x0e, 0xfe, 0xe4, 0x85, 0x04, 0x1c, 0xbc, 0xb7, 0x4d, 0x86,
+ 0x04, 0x69, 0x9a, 0x61, 0xf2, 0x87, 0x04, 0x4b, 0x65, 0xe3, 0xf0, 0x90,
+ 0x04, 0x24, 0x86, 0xc3, 0x5f, 0x91, 0x04, 0x6d, 0xe2, 0xbc, 0xf2, 0x97,
+ 0x02, 0x04, 0xd2, 0x92, 0x04, 0x38, 0xc0, 0x92, 0x3d, 0x93, 0x04, 0x08,
+ 0x3c, 0xbf, 0x2a, 0x98, 0x02, 0x10, 0xe1, 0x99, 0x04, 0x00, 0x0e, 0xa9,
+ 0xfc, 0x9a, 0x04, 0x00, 0x0e, 0x5b, 0xe0,
+ };
+ BOOST_CHECK_EQUAL_COLLECTIONS(expected, expected + sizeof(expected),
+ wire.begin(), wire.end());
+
+ ForwarderStatus status2(wire);
+ BOOST_CHECK_EQUAL(status1, status2);
+}
+
+BOOST_AUTO_TEST_CASE(Equality)
+{
+ ForwarderStatus status1, status2;
+
+ status1 = makeForwarderStatus();
+ status2 = status1;
+ BOOST_CHECK_EQUAL(status1, status2);
+
+ status2.setNPitEntries(42);
+ BOOST_CHECK_NE(status1, status2);
+}
+
+BOOST_AUTO_TEST_CASE(Print)
+{
+ ForwarderStatus status;
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(status),
+ "GeneralStatus(NfdVersion: ,\n"
+ " StartTimestamp: 0 nanoseconds since Jan 1, 1970,\n"
+ " CurrentTimestamp: 0 nanoseconds since Jan 1, 1970,\n"
+ " Counters: {NameTreeEntries: 0,\n"
+ " FibEntries: 0,\n"
+ " PitEntries: 0,\n"
+ " MeasurementsEntries: 0,\n"
+ " CsEntries: 0,\n"
+ " Interests: {in: 0, out: 0},\n"
+ " Data: {in: 0, out: 0},\n"
+ " Nacks: {in: 0, out: 0},\n"
+ " SatisfiedInterests: 0,\n"
+ " UnsatisfiedInterests: 0}\n"
+ " )");
+
+ status = makeForwarderStatus();
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(status),
+ "GeneralStatus(NfdVersion: 0.5.1-14-g05dd444,\n"
+ " StartTimestamp: 375193249325000000 nanoseconds since Jan 1, 1970,\n"
+ " CurrentTimestamp: 886109034272000000 nanoseconds since Jan 1, 1970,\n"
+ " Counters: {NameTreeEntries: 1849943160,\n"
+ " FibEntries: 621739748,\n"
+ " PitEntries: 482129741,\n"
+ " MeasurementsEntries: 1771725298,\n"
+ " CsEntries: 1264968688,\n"
+ " Interests: {in: 612811615, out: 952144445},\n"
+ " Data: {in: 1843576050, out: 138198826},\n"
+ " Nacks: {in: 1234, out: 4321},\n"
+ " SatisfiedInterests: 961020,\n"
+ " UnsatisfiedInterests: 941024}\n"
+ " )");
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestForwarderStatus
+BOOST_AUTO_TEST_SUITE_END() // Nfd
+BOOST_AUTO_TEST_SUITE_END() // Mgmt
+
+} // namespace tests
+} // namespace nfd
+} // namespace ndn
diff --git a/tests/unit/mgmt/nfd/rib-entry.t.cpp b/tests/unit/mgmt/nfd/rib-entry.t.cpp
new file mode 100644
index 0000000..f2ab51a
--- /dev/null
+++ b/tests/unit/mgmt/nfd/rib-entry.t.cpp
@@ -0,0 +1,240 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library 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 Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "mgmt/nfd/rib-entry.hpp"
+
+#include "boost-test.hpp"
+#include <boost/lexical_cast.hpp>
+
+namespace ndn {
+namespace nfd {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Mgmt)
+BOOST_AUTO_TEST_SUITE(Nfd)
+BOOST_AUTO_TEST_SUITE(TestRibEntry)
+
+static Route
+makeRoute()
+{
+ return Route()
+ .setFaceId(1)
+ .setOrigin(ROUTE_ORIGIN_NLSR)
+ .setCost(100)
+ .setFlags(ROUTE_FLAG_CAPTURE);
+}
+
+static RibEntry
+makeRibEntry()
+{
+ return RibEntry()
+ .setName("/hello/world")
+ .addRoute(makeRoute()
+ .setExpirationPeriod(10_s));
+}
+
+BOOST_AUTO_TEST_CASE(RouteEncode)
+{
+ Route route1 = makeRoute();
+ route1.setExpirationPeriod(10_s);
+ const Block& wire = route1.wireEncode();
+
+ static const uint8_t expected[] = {
+ 0x81, 0x10, 0x69, 0x01, 0x01, 0x6f, 0x01, 0x80, 0x6a, 0x01, 0x64, 0x6c, 0x01, 0x02,
+ 0x6d, 0x02, 0x27, 0x10
+ };
+ BOOST_CHECK_EQUAL_COLLECTIONS(expected, expected + sizeof(expected),
+ wire.begin(), wire.end());
+
+ Route route2(wire);
+ BOOST_CHECK_EQUAL(route1, route2);
+}
+
+BOOST_AUTO_TEST_CASE(RouteNoExpirationPeriodEncode)
+{
+ Route route1 = makeRoute();
+ const Block& wire = route1.wireEncode();
+
+ static const uint8_t expected[] = {
+ 0x81, 0x0C, 0x69, 0x01, 0x01, 0x6f, 0x01, 0x80, 0x6a, 0x01, 0x64, 0x6c, 0x01, 0x02
+ };
+ BOOST_CHECK_EQUAL_COLLECTIONS(expected, expected + sizeof(expected),
+ wire.begin(), wire.end());
+
+ Route route2(wire);
+ BOOST_CHECK_EQUAL(route1, route2);
+}
+
+BOOST_AUTO_TEST_CASE(RouteExpirationPeriod)
+{
+ Route route;
+ BOOST_CHECK_EQUAL(route.hasExpirationPeriod(), false);
+ BOOST_CHECK_EQUAL(route.getExpirationPeriod(), time::milliseconds::max());
+
+ route.setExpirationPeriod(1_min);
+ BOOST_CHECK_EQUAL(route.hasExpirationPeriod(), true);
+ BOOST_CHECK_EQUAL(route.getExpirationPeriod(), 1_min);
+
+ route.setExpirationPeriod(time::milliseconds::max());
+ BOOST_CHECK_EQUAL(route.hasExpirationPeriod(), false);
+ BOOST_CHECK_EQUAL(route.getExpirationPeriod(), time::milliseconds::max());
+
+ route.setExpirationPeriod(1_min);
+ BOOST_CHECK_EQUAL(route.hasExpirationPeriod(), true);
+
+ route.unsetExpirationPeriod();
+ BOOST_CHECK_EQUAL(route.hasExpirationPeriod(), false);
+}
+
+BOOST_AUTO_TEST_CASE(RouteEquality)
+{
+ Route route1, route2;
+
+ route1 = makeRoute();
+ route2 = route1;
+ BOOST_CHECK_EQUAL(route1, route2);
+
+ route2.setFaceId(42);
+ BOOST_CHECK_NE(route1, route2);
+
+ route2 = route1;
+ route2.setExpirationPeriod(1_min);
+ BOOST_CHECK_NE(route1, route2);
+}
+
+BOOST_AUTO_TEST_CASE(RibEntryEncode)
+{
+ RibEntry entry1 = makeRibEntry();
+ entry1.addRoute(Route()
+ .setFaceId(2)
+ .setOrigin(ROUTE_ORIGIN_APP)
+ .setCost(32)
+ .setFlags(ROUTE_FLAG_CHILD_INHERIT)
+ .setExpirationPeriod(5_s));
+ const Block& wire = entry1.wireEncode();
+
+ static const uint8_t expected[] = {
+ 0x80, 0x34, 0x07, 0x0e, 0x08, 0x05, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x08, 0x05, 0x77,
+ 0x6f, 0x72, 0x6c, 0x64, 0x81, 0x10, 0x69, 0x01, 0x01, 0x6f, 0x01, 0x80, 0x6a, 0x01,
+ 0x64, 0x6c, 0x01, 0x02, 0x6d, 0x02, 0x27, 0x10, 0x81, 0x10, 0x69, 0x01, 0x02, 0x6f,
+ 0x01, 0x00, 0x6a, 0x01, 0x20, 0x6c, 0x01, 0x01, 0x6d, 0x02, 0x13, 0x88
+ };
+ BOOST_CHECK_EQUAL_COLLECTIONS(expected, expected + sizeof(expected),
+ wire.begin(), wire.end());
+
+ RibEntry entry2(wire);
+ BOOST_CHECK_EQUAL(entry1, entry2);
+}
+
+BOOST_AUTO_TEST_CASE(RibEntryClearRoutes)
+{
+ RibEntry entry;
+ entry.setName("/hello/world");
+ BOOST_CHECK_EQUAL(entry.getRoutes().size(), 0);
+
+ Route route1;
+ route1.setFaceId(42);
+ entry.addRoute(route1);
+ BOOST_REQUIRE_EQUAL(entry.getRoutes().size(), 1);
+ BOOST_CHECK_EQUAL(entry.getRoutes().front(), route1);
+
+ entry.clearRoutes();
+ BOOST_CHECK_EQUAL(entry.getRoutes().size(), 0);
+}
+
+BOOST_AUTO_TEST_CASE(RibEntryEquality)
+{
+ RibEntry entry1, entry2;
+ BOOST_CHECK_EQUAL(entry1, entry2);
+
+ entry1 = entry2 = makeRibEntry();
+ BOOST_CHECK_EQUAL(entry1, entry2);
+ BOOST_CHECK_EQUAL(entry2, entry1);
+
+ entry2.setName("/different/name");
+ BOOST_CHECK_NE(entry1, entry2);
+
+ entry2 = entry1;
+ std::vector<Route> empty;
+ entry2.setRoutes(empty.begin(), empty.end());
+ BOOST_CHECK_NE(entry1, entry2);
+ BOOST_CHECK_NE(entry2, entry1);
+
+ entry2 = entry1;
+ auto r1 = Route()
+ .setFaceId(1)
+ .setCost(1000);
+ entry1.addRoute(r1);
+ BOOST_CHECK_NE(entry1, entry2);
+ BOOST_CHECK_NE(entry2, entry1);
+
+ auto r42 = Route()
+ .setFaceId(42)
+ .setCost(42);
+ entry1.addRoute(r42);
+ entry2.addRoute(r42)
+ .addRoute(r1);
+ BOOST_CHECK_EQUAL(entry1, entry2); // order of Routes is irrelevant
+ BOOST_CHECK_EQUAL(entry2, entry1);
+
+ entry1 = entry2 = makeRibEntry();
+ entry1.addRoute(r1)
+ .addRoute(r42);
+ entry2.addRoute(r42)
+ .addRoute(r42);
+ BOOST_CHECK_NE(entry1, entry2); // match each Route at most once
+ BOOST_CHECK_NE(entry2, entry1);
+}
+
+BOOST_AUTO_TEST_CASE(Print)
+{
+ Route route;
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(route),
+ "Route(FaceId: 0, Origin: app, Cost: 0, Flags: 0x1, ExpirationPeriod: infinite)");
+
+ RibEntry entry;
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(entry),
+ "RibEntry(Prefix: /,\n"
+ " Routes: []\n"
+ " )");
+
+ entry = makeRibEntry();
+ entry.addRoute(Route()
+ .setFaceId(2)
+ .setOrigin(ROUTE_ORIGIN_STATIC)
+ .setCost(32)
+ .setFlags(ROUTE_FLAG_CHILD_INHERIT));
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(entry),
+ "RibEntry(Prefix: /hello/world,\n"
+ " Routes: [Route(FaceId: 1, Origin: nlsr, Cost: 100, Flags: 0x2, "
+ "ExpirationPeriod: 10000 milliseconds),\n"
+ " Route(FaceId: 2, Origin: static, Cost: 32, Flags: 0x1, "
+ "ExpirationPeriod: infinite)]\n"
+ " )");
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestRibEntry
+BOOST_AUTO_TEST_SUITE_END() // Nfd
+BOOST_AUTO_TEST_SUITE_END() // Mgmt
+
+} // namespace tests
+} // namespace nfd
+} // namespace ndn
diff --git a/tests/unit/mgmt/nfd/status-dataset.t.cpp b/tests/unit/mgmt/nfd/status-dataset.t.cpp
new file mode 100644
index 0000000..a436de0
--- /dev/null
+++ b/tests/unit/mgmt/nfd/status-dataset.t.cpp
@@ -0,0 +1,486 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library 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 Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "mgmt/nfd/status-dataset.hpp"
+#include "mgmt/nfd/controller.hpp"
+
+#include "controller-fixture.hpp"
+#include "make-interest-data.hpp"
+
+namespace ndn {
+namespace nfd {
+namespace tests {
+
+using namespace ndn::tests;
+
+BOOST_AUTO_TEST_SUITE(Mgmt)
+BOOST_AUTO_TEST_SUITE(Nfd)
+
+class ControllerStatusDatasetFixture : public ControllerFixture
+{
+protected:
+ /** \brief send one WireEncodable as Data reply
+ * \param prefix dataset prefix without version and segment
+ * \param payload payload block
+ * \note payload must fit in one Data
+ */
+ template<typename T>
+ void
+ sendDataset(const Name& prefix, const T& payload)
+ {
+ BOOST_CONCEPT_ASSERT((WireEncodable<T>));
+
+ auto data = this->prepareDatasetReply(prefix);
+ data->setContent(payload.wireEncode());
+ face.receive(*signData(data));
+ }
+
+ /** \brief send two WireEncodables as Data reply
+ * \param prefix dataset prefix without version and segment
+ * \param payload1 first vector item
+ * \param payload2 second vector item
+ * \note all payloads must fit in one Data
+ */
+ template<typename T1, typename T2>
+ void
+ sendDataset(const Name& prefix, const T1& payload1, const T2& payload2)
+ {
+ // The test suite allows up to two items, and put them in the same Data packet,
+ // because this test suite focuses on Controller::fetch<StatusDataset>,
+ // and is not intended to cover SegmentFetcher behavior.
+
+ BOOST_CONCEPT_ASSERT((WireEncodable<T1>));
+ BOOST_CONCEPT_ASSERT((WireEncodable<T2>));
+
+ ndn::encoding::EncodingBuffer buffer;
+ payload2.wireEncode(buffer);
+ payload1.wireEncode(buffer);
+
+ auto data = this->prepareDatasetReply(prefix);
+ data->setContent(buffer.buf(), buffer.size());
+ face.receive(*signData(data));
+ }
+
+private:
+ shared_ptr<Data>
+ prepareDatasetReply(const Name& prefix)
+ {
+ Name name = prefix;
+ name.appendVersion().appendSegment(0);
+
+ // These warnings assist in debugging a `hasResult` check failure.
+ // They usually indicate a misspelled prefix or incorrect timing in the test case.
+ if (face.sentInterests.size() < 1) {
+ BOOST_WARN_MESSAGE(false, "no Interest expressed");
+ }
+ else {
+ BOOST_WARN_MESSAGE(face.sentInterests.back().getName().isPrefixOf(name),
+ "last Interest " << face.sentInterests.back().getName() <<
+ " cannot be satisfied by this Data " << name);
+ }
+
+ auto data = make_shared<Data>(name);
+ data->setFinalBlock(name[-1]);
+ return data;
+ }
+};
+
+BOOST_FIXTURE_TEST_SUITE(TestStatusDataset, ControllerStatusDatasetFixture)
+
+BOOST_AUTO_TEST_SUITE(Failures)
+
+BOOST_AUTO_TEST_CASE(Timeout)
+{
+ CommandOptions options;
+ options.setTimeout(3000_ms);
+ controller.fetch<FaceDataset>(
+ [] (const std::vector<FaceStatus>& result) { BOOST_FAIL("fetchDataset should not succeed"); },
+ datasetFailCallback,
+ options);
+ this->advanceClocks(500_ms);
+ BOOST_CHECK_EQUAL(controller.m_fetchers.size(), 1);
+
+ this->advanceClocks(500_ms, 6);
+ BOOST_CHECK_EQUAL(controller.m_fetchers.size(), 0);
+ BOOST_REQUIRE_EQUAL(failCodes.size(), 1);
+ BOOST_CHECK_EQUAL(failCodes.back(), Controller::ERROR_TIMEOUT);
+}
+
+BOOST_AUTO_TEST_CASE(DataHasNoSegment)
+{
+ controller.fetch<FaceDataset>(
+ [] (const std::vector<FaceStatus>& result) { BOOST_FAIL("fetchDataset should not succeed"); },
+ datasetFailCallback);
+ this->advanceClocks(500_ms);
+
+ face.receive(*makeData("/localhost/nfd/faces/list/%FD%00"));
+ this->advanceClocks(500_ms);
+
+ BOOST_REQUIRE_EQUAL(failCodes.size(), 1);
+ BOOST_CHECK_EQUAL(failCodes.back(), Controller::ERROR_SERVER);
+}
+
+BOOST_AUTO_TEST_CASE(ValidationFailure)
+{
+ this->setValidationResult(false);
+
+ controller.fetch<FaceDataset>(
+ [] (const std::vector<FaceStatus>& result) { BOOST_FAIL("fetchDataset should not succeed"); },
+ datasetFailCallback);
+ this->advanceClocks(500_ms);
+
+ FaceStatus payload;
+ payload.setFaceId(5744);
+ this->sendDataset("/localhost/nfd/faces/list", payload);
+ this->advanceClocks(500_ms);
+
+ BOOST_REQUIRE_EQUAL(failCodes.size(), 1);
+ BOOST_CHECK_EQUAL(failCodes.back(), Controller::ERROR_VALIDATION);
+}
+
+BOOST_AUTO_TEST_CASE(Nack)
+{
+ controller.fetch<FaceDataset>(
+ [] (const std::vector<FaceStatus>& result) { BOOST_FAIL("fetchDataset should not succeed"); },
+ datasetFailCallback);
+ this->advanceClocks(500_ms);
+
+ BOOST_REQUIRE_EQUAL(face.sentInterests.size(), 1);
+ face.receive(lp::Nack(face.sentInterests.back()));
+ this->advanceClocks(500_ms);
+
+ BOOST_REQUIRE_EQUAL(failCodes.size(), 1);
+ BOOST_CHECK_EQUAL(failCodes.back(), Controller::ERROR_NACK);
+}
+
+BOOST_AUTO_TEST_CASE(ParseError1)
+{
+ controller.fetch<FaceDataset>(
+ [] (const std::vector<FaceStatus>& result) { BOOST_FAIL("fetchDataset should not succeed"); },
+ datasetFailCallback);
+ this->advanceClocks(500_ms);
+
+ Name payload; // Name is not valid FaceStatus
+ this->sendDataset("/localhost/nfd/faces/list", payload);
+ this->advanceClocks(500_ms);
+
+ BOOST_REQUIRE_EQUAL(failCodes.size(), 1);
+ BOOST_CHECK_EQUAL(failCodes.back(), Controller::ERROR_SERVER);
+}
+
+BOOST_AUTO_TEST_CASE(ParseError2)
+{
+ controller.fetch<FaceDataset>(
+ [] (const std::vector<FaceStatus>& result) { BOOST_FAIL("fetchDataset should not succeed"); },
+ datasetFailCallback);
+ this->advanceClocks(500_ms);
+
+ FaceStatus payload1;
+ payload1.setFaceId(10930);
+ Name payload2; // Name is not valid FaceStatus
+ this->sendDataset("/localhost/nfd/faces/list", payload1, payload2);
+ this->advanceClocks(500_ms);
+
+ BOOST_REQUIRE_EQUAL(failCodes.size(), 1);
+ BOOST_CHECK_EQUAL(failCodes.back(), Controller::ERROR_SERVER);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // Failures
+
+BOOST_AUTO_TEST_SUITE(NoCallback)
+
+BOOST_AUTO_TEST_CASE(Success)
+{
+ controller.fetch<FaceDataset>(
+ nullptr,
+ datasetFailCallback);
+ this->advanceClocks(500_ms);
+ BOOST_CHECK_EQUAL(controller.m_fetchers.size(), 1);
+
+ FaceStatus payload;
+ payload.setFaceId(2577);
+ this->sendDataset("/localhost/nfd/faces/list", payload);
+ BOOST_CHECK_NO_THROW(this->advanceClocks(500_ms));
+
+ BOOST_CHECK_EQUAL(failCodes.size(), 0);
+ BOOST_CHECK_EQUAL(controller.m_fetchers.size(), 0);
+}
+
+BOOST_AUTO_TEST_CASE(Failure)
+{
+ CommandOptions options;
+ options.setTimeout(3000_ms);
+ controller.fetch<FaceDataset>(
+ [] (const std::vector<FaceStatus>& result) { BOOST_FAIL("fetchDataset should not succeed"); },
+ nullptr,
+ options);
+ BOOST_CHECK_NO_THROW(this->advanceClocks(500_ms, 7));
+}
+
+BOOST_AUTO_TEST_SUITE_END() // NoCallback
+
+BOOST_AUTO_TEST_SUITE(Datasets)
+
+BOOST_AUTO_TEST_CASE(StatusGeneral)
+{
+ bool hasResult = false;
+ controller.fetch<ForwarderGeneralStatusDataset>(
+ [&hasResult] (const ForwarderStatus& result) {
+ hasResult = true;
+ BOOST_CHECK_EQUAL(result.getNfdVersion(), "0.4.2");
+ },
+ datasetFailCallback);
+ this->advanceClocks(500_ms);
+
+ ForwarderStatus payload;
+ payload.setNfdVersion("0.4.2");
+ this->sendDataset("/localhost/nfd/status/general", payload);
+ this->advanceClocks(500_ms);
+
+ BOOST_CHECK(hasResult);
+ BOOST_CHECK_EQUAL(failCodes.size(), 0);
+}
+
+BOOST_AUTO_TEST_CASE(FaceList)
+{
+ bool hasResult = false;
+ controller.fetch<FaceDataset>(
+ [&hasResult] (const std::vector<FaceStatus>& result) {
+ hasResult = true;
+ BOOST_CHECK_EQUAL(result.size(), 2);
+ BOOST_CHECK_EQUAL(result.front().getFaceId(), 24485);
+ },
+ datasetFailCallback);
+ this->advanceClocks(500_ms);
+
+ FaceStatus payload1;
+ payload1.setFaceId(24485);
+ FaceStatus payload2;
+ payload2.setFaceId(12987);
+ this->sendDataset("/localhost/nfd/faces/list", payload1, payload2);
+ this->advanceClocks(500_ms);
+
+ BOOST_CHECK(hasResult);
+ BOOST_CHECK_EQUAL(failCodes.size(), 0);
+}
+
+BOOST_AUTO_TEST_CASE(FaceQuery)
+{
+ FaceQueryFilter filter;
+ filter.setUriScheme("udp4");
+ bool hasResult = false;
+ controller.fetch<FaceQueryDataset>(
+ filter,
+ [&hasResult] (const std::vector<FaceStatus>& result) {
+ hasResult = true;
+ BOOST_CHECK_EQUAL(result.size(), 1);
+ BOOST_CHECK_EQUAL(result.front().getFaceId(), 8795);
+ },
+ datasetFailCallback);
+ this->advanceClocks(500_ms);
+
+ Name prefix("/localhost/nfd/faces/query");
+ prefix.append(filter.wireEncode());
+ FaceStatus payload;
+ payload.setFaceId(8795);
+ this->sendDataset(prefix, payload);
+ this->advanceClocks(500_ms);
+
+ BOOST_CHECK(hasResult);
+ BOOST_CHECK_EQUAL(failCodes.size(), 0);
+}
+
+BOOST_AUTO_TEST_CASE(FaceQueryWithOptions)
+{
+ FaceQueryFilter filter;
+ filter.setUriScheme("udp4");
+ CommandOptions options;
+ options.setTimeout(3000_ms);
+ bool hasResult = false;
+ controller.fetch<FaceQueryDataset>(
+ filter,
+ [&hasResult] (const std::vector<FaceStatus>& result) {
+ hasResult = true;
+ BOOST_CHECK_EQUAL(result.size(), 1);
+ BOOST_CHECK_EQUAL(result.front().getFaceId(), 14022);
+ },
+ datasetFailCallback,
+ options);
+ this->advanceClocks(500_ms);
+
+ Name prefix("/localhost/nfd/faces/query");
+ prefix.append(filter.wireEncode());
+ FaceStatus payload;
+ payload.setFaceId(14022);
+ this->sendDataset(prefix, payload);
+ this->advanceClocks(500_ms);
+
+ BOOST_CHECK(hasResult);
+ BOOST_CHECK_EQUAL(failCodes.size(), 0);
+}
+
+BOOST_AUTO_TEST_CASE(FaceChannels)
+{
+ bool hasResult = false;
+ controller.fetch<ChannelDataset>(
+ [&hasResult] (const std::vector<ChannelStatus>& result) {
+ hasResult = true;
+ BOOST_CHECK_EQUAL(result.size(), 2);
+ BOOST_CHECK_EQUAL(result.front().getLocalUri(), "tcp4://192.0.2.1:6363");
+ },
+ datasetFailCallback);
+ this->advanceClocks(500_ms);
+
+ ChannelStatus payload1;
+ payload1.setLocalUri("tcp4://192.0.2.1:6363");
+ ChannelStatus payload2;
+ payload2.setLocalUri("udp4://192.0.2.1:6363");
+ this->sendDataset("/localhost/nfd/faces/channels", payload1, payload2);
+ this->advanceClocks(500_ms);
+
+ BOOST_CHECK(hasResult);
+ BOOST_CHECK_EQUAL(failCodes.size(), 0);
+}
+
+BOOST_AUTO_TEST_CASE(FibList)
+{
+ bool hasResult = false;
+ controller.fetch<FibDataset>(
+ [&hasResult] (const std::vector<FibEntry>& result) {
+ hasResult = true;
+ BOOST_CHECK_EQUAL(result.size(), 2);
+ BOOST_CHECK_EQUAL(result.front().getPrefix(), "/wYs7fzYcfG");
+ },
+ datasetFailCallback);
+ this->advanceClocks(500_ms);
+
+ FibEntry payload1;
+ payload1.setPrefix("/wYs7fzYcfG");
+ FibEntry payload2;
+ payload2.setPrefix("/LKvmnzY5S");
+ this->sendDataset("/localhost/nfd/fib/list", payload1, payload2);
+ this->advanceClocks(500_ms);
+
+ BOOST_CHECK(hasResult);
+ BOOST_CHECK_EQUAL(failCodes.size(), 0);
+}
+
+BOOST_AUTO_TEST_CASE(CsInfo)
+{
+ using ndn::nfd::CsInfo;
+
+ bool hasResult = false;
+ controller.fetch<CsInfoDataset>(
+ [&hasResult] (const CsInfo& result) {
+ hasResult = true;
+ BOOST_CHECK_EQUAL(result.getNHits(), 4539);
+ },
+ datasetFailCallback);
+ this->advanceClocks(500_ms);
+
+ CsInfo payload;
+ payload.setNHits(4539);
+ this->sendDataset("/localhost/nfd/cs/info", payload);
+ this->advanceClocks(500_ms);
+
+ BOOST_CHECK(hasResult);
+ BOOST_CHECK_EQUAL(failCodes.size(), 0);
+}
+
+BOOST_AUTO_TEST_CASE(StrategyChoiceList)
+{
+ bool hasResult = false;
+ controller.fetch<StrategyChoiceDataset>(
+ [&hasResult] (const std::vector<StrategyChoice>& result) {
+ hasResult = true;
+ BOOST_CHECK_EQUAL(result.size(), 2);
+ BOOST_CHECK_EQUAL(result.front().getName(), "/8MLz6N3B");
+ },
+ datasetFailCallback);
+ this->advanceClocks(500_ms);
+
+ StrategyChoice payload1;
+ payload1.setName("/8MLz6N3B");
+ StrategyChoice payload2;
+ payload2.setName("/svqcBu0YwU");
+ this->sendDataset("/localhost/nfd/strategy-choice/list", payload1, payload2);
+ this->advanceClocks(500_ms);
+
+ BOOST_CHECK(hasResult);
+ BOOST_CHECK_EQUAL(failCodes.size(), 0);
+}
+
+BOOST_AUTO_TEST_CASE(RibList)
+{
+ bool hasResult = false;
+ controller.fetch<RibDataset>(
+ [&hasResult] (const std::vector<RibEntry>& result) {
+ hasResult = true;
+ BOOST_CHECK_EQUAL(result.size(), 2);
+ BOOST_CHECK_EQUAL(result.front().getName(), "/zXxBth97ee");
+ },
+ datasetFailCallback);
+ this->advanceClocks(500_ms);
+
+ RibEntry payload1;
+ payload1.setName("/zXxBth97ee");
+ RibEntry payload2;
+ payload2.setName("/rJ8CvUpr4G");
+ this->sendDataset("/localhost/nfd/rib/list", payload1, payload2);
+ this->advanceClocks(500_ms);
+
+ BOOST_CHECK(hasResult);
+ BOOST_CHECK_EQUAL(failCodes.size(), 0);
+}
+
+BOOST_AUTO_TEST_CASE(RibListWithOptions)
+{
+ CommandOptions options;
+ options.setPrefix("/localhop/nfd");
+ bool hasResult = false;
+ controller.fetch<RibDataset>(
+ [&hasResult] (const std::vector<RibEntry>& result) {
+ hasResult = true;
+ BOOST_CHECK_EQUAL(result.size(), 1);
+ BOOST_CHECK_EQUAL(result.front().getName(), "/e6L5K4ascd");
+ },
+ datasetFailCallback,
+ options);
+ this->advanceClocks(500_ms);
+
+ RibEntry payload;
+ payload.setName("/e6L5K4ascd");
+ this->sendDataset("/localhop/nfd/rib/list", payload);
+ this->advanceClocks(500_ms);
+
+ BOOST_CHECK(hasResult);
+ BOOST_CHECK_EQUAL(failCodes.size(), 0);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // Datasets
+
+BOOST_AUTO_TEST_SUITE_END() // TestStatusDataset
+BOOST_AUTO_TEST_SUITE_END() // Nfd
+BOOST_AUTO_TEST_SUITE_END() // Mgmt
+
+} // namespace tests
+} // namespace nfd
+} // namespace ndn
diff --git a/tests/unit/mgmt/nfd/strategy-choice.t.cpp b/tests/unit/mgmt/nfd/strategy-choice.t.cpp
new file mode 100644
index 0000000..9996541
--- /dev/null
+++ b/tests/unit/mgmt/nfd/strategy-choice.t.cpp
@@ -0,0 +1,96 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library 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 Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "mgmt/nfd/strategy-choice.hpp"
+
+#include "boost-test.hpp"
+#include <boost/lexical_cast.hpp>
+
+namespace ndn {
+namespace nfd {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Mgmt)
+BOOST_AUTO_TEST_SUITE(Nfd)
+BOOST_AUTO_TEST_SUITE(TestStrategyChoice)
+
+BOOST_AUTO_TEST_CASE(Encode)
+{
+ StrategyChoice sc1;
+ sc1.setName("/hello/world")
+ .setStrategy("/some/non/existing/strategy/name");
+ Block wire = sc1.wireEncode();
+
+ // These octets are obtained by the snippet below.
+ // This check is intended to detect unexpected encoding change in the future.
+ // for (Buffer::const_iterator it = wire.begin(); it != wire.end(); ++it) {
+ // printf("0x%02x, ", *it);
+ // }
+ static const uint8_t expected[] = {
+ 0x80, 0x39, 0x07, 0x0e, 0x08, 0x05, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x08, 0x05, 0x77,
+ 0x6f, 0x72, 0x6c, 0x64, 0x6b, 0x27, 0x07, 0x25, 0x08, 0x04, 0x73, 0x6f, 0x6d, 0x65,
+ 0x08, 0x03, 0x6e, 0x6f, 0x6e, 0x08, 0x08, 0x65, 0x78, 0x69, 0x73, 0x74, 0x69, 0x6e,
+ 0x67, 0x08, 0x08, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x08, 0x04, 0x6e,
+ 0x61, 0x6d, 0x65
+ };
+ BOOST_CHECK_EQUAL_COLLECTIONS(expected, expected + sizeof(expected),
+ wire.begin(), wire.end());
+
+ StrategyChoice sc2(wire);
+ BOOST_CHECK_EQUAL(sc1, sc2);
+}
+
+BOOST_AUTO_TEST_CASE(Equality)
+{
+ StrategyChoice sc1, sc2;
+
+ sc1.setName("/A")
+ .setStrategy("/strategyP");
+ sc2 = sc1;
+ BOOST_CHECK_EQUAL(sc1, sc2);
+
+ sc2.setName("/B");
+ BOOST_CHECK_NE(sc1, sc2);
+
+ sc2 = sc1;
+ sc2.setStrategy("/strategyQ");
+ BOOST_CHECK_NE(sc1, sc2);
+}
+
+BOOST_AUTO_TEST_CASE(Print)
+{
+ StrategyChoice sc;
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(sc),
+ "StrategyChoice(Name: /, Strategy: /)");
+
+ sc.setName("/A")
+ .setStrategy("/strategyP");
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(sc),
+ "StrategyChoice(Name: /A, Strategy: /strategyP)");
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestStrategyChoice
+BOOST_AUTO_TEST_SUITE_END() // Nfd
+BOOST_AUTO_TEST_SUITE_END() // Mgmt
+
+} // namespace tests
+} // namespace nfd
+} // namespace ndn
diff --git a/tests/unit/mgmt/status-dataset-context.t.cpp b/tests/unit/mgmt/status-dataset-context.t.cpp
new file mode 100644
index 0000000..34e6fc1
--- /dev/null
+++ b/tests/unit/mgmt/status-dataset-context.t.cpp
@@ -0,0 +1,309 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library 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 Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "mgmt/status-dataset-context.hpp"
+
+#include "boost-test.hpp"
+#include "make-interest-data.hpp"
+
+namespace ndn {
+namespace mgmt {
+namespace tests {
+
+using namespace ndn::tests;
+
+class StatusDatasetContextFixture
+{
+private:
+ struct SendDataArgs
+ {
+ Name dataName;
+ Block content;
+ time::milliseconds imsFresh;
+ bool isFinalBlock;
+ };
+
+protected:
+ StatusDatasetContextFixture()
+ : interest(makeInterest("/test/context/interest"))
+ , contentBlock(makeStringBlock(tlv::Content, "/test/data/content"))
+ , context(*interest,
+ [this] (const Name& dataName, const Block& content,
+ time::milliseconds imsFresh, bool isFinalBlock) {
+ SendDataArgs args{dataName, content, imsFresh, isFinalBlock};
+ sendDataHistory.push_back(args);
+ },
+ [this] (const ControlResponse& resp) {
+ sendNackHistory.push_back(resp);
+ })
+ , defaultImsFresh(1000_ms)
+ {
+ }
+
+ Name
+ makeSegmentName(size_t segmentNo) const
+ {
+ auto name = context.getPrefix();
+ return name.appendSegment(segmentNo);
+ }
+
+ Block
+ concatenateDataContent() const
+ {
+ EncodingBuffer encoder;
+ size_t valueLength = 0;
+ for (const auto& args : sendDataHistory) {
+ const auto& content = args.content;
+ valueLength += encoder.appendByteArray(content.value(), content.value_size());
+ }
+ encoder.prependVarNumber(valueLength);
+ encoder.prependVarNumber(tlv::Content);
+ return encoder.block();
+ }
+
+protected:
+ std::vector<SendDataArgs> sendDataHistory;
+ std::vector<ControlResponse> sendNackHistory;
+ shared_ptr<Interest> interest;
+ Block contentBlock;
+ mgmt::StatusDatasetContext context;
+ time::milliseconds defaultImsFresh;
+};
+
+BOOST_AUTO_TEST_SUITE(Mgmt)
+BOOST_FIXTURE_TEST_SUITE(TestStatusDatasetContext, StatusDatasetContextFixture)
+
+BOOST_AUTO_TEST_SUITE(Prefix)
+
+BOOST_AUTO_TEST_CASE(Get)
+{
+ Name dataName = context.getPrefix();
+ BOOST_CHECK(dataName[-1].isVersion());
+ BOOST_CHECK_EQUAL(dataName.getPrefix(-1), interest->getName());
+}
+
+BOOST_AUTO_TEST_CASE(SetValid)
+{
+ Name validPrefix = Name(interest->getName()).append("/valid");
+ BOOST_CHECK_NO_THROW(context.setPrefix(validPrefix));
+ BOOST_CHECK(context.getPrefix()[-1].isVersion());
+ BOOST_CHECK_EQUAL(context.getPrefix().getPrefix(-1), validPrefix);
+}
+
+BOOST_AUTO_TEST_CASE(SetInvalid)
+{
+ Name invalidPrefix = Name(interest->getName()).getPrefix(-1).append("/invalid");
+ BOOST_CHECK_THROW(context.setPrefix(invalidPrefix), std::invalid_argument);
+}
+
+BOOST_AUTO_TEST_CASE(SetValidWithAppendCalled)
+{
+ Name validPrefix = Name(interest->getName()).append("/valid");
+ context.append(contentBlock);
+ BOOST_CHECK_THROW(context.setPrefix(validPrefix), std::domain_error);
+}
+
+BOOST_AUTO_TEST_CASE(SetValidWithEndCalled)
+{
+ Name validPrefix = Name(interest->getName()).append("/valid");
+ context.end();
+ BOOST_CHECK_THROW(context.setPrefix(validPrefix), std::domain_error);
+}
+
+BOOST_AUTO_TEST_CASE(SetValidWithRejectCalled)
+{
+ Name validPrefix = Name(interest->getName()).append("/valid");
+ context.reject();
+ BOOST_CHECK_THROW(context.setPrefix(validPrefix), std::domain_error);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // Prefix
+
+BOOST_AUTO_TEST_SUITE(Expiry)
+
+BOOST_AUTO_TEST_CASE(GetAndSet)
+{
+ auto period = 9527_ms;
+ BOOST_CHECK_EQUAL(context.getExpiry(), 1000_ms);
+ BOOST_CHECK_EQUAL(context.setExpiry(period).getExpiry(), period);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // Expiry
+
+BOOST_AUTO_TEST_SUITE(Respond)
+
+BOOST_AUTO_TEST_CASE(Basic)
+{
+ BOOST_CHECK_NO_THROW(context.append(contentBlock));
+ BOOST_CHECK(sendDataHistory.empty()); // does not call end yet
+
+ BOOST_CHECK_NO_THROW(context.end());
+
+ BOOST_REQUIRE_EQUAL(sendDataHistory.size(), 1);
+ const auto& args = sendDataHistory[0];
+
+ BOOST_CHECK_EQUAL(args.dataName, makeSegmentName(0));
+ BOOST_CHECK_EQUAL(args.content.blockFromValue(), contentBlock);
+ BOOST_CHECK_EQUAL(args.imsFresh, defaultImsFresh);
+ BOOST_CHECK_EQUAL(args.isFinalBlock, true);
+}
+
+BOOST_AUTO_TEST_CASE(Large)
+{
+ static Block largeBlock = [] () -> Block {
+ EncodingBuffer encoder;
+ size_t maxBlockSize = MAX_NDN_PACKET_SIZE >> 1;
+ for (size_t i = 0; i < maxBlockSize; ++i) {
+ encoder.prependByte(1);
+ }
+ encoder.prependVarNumber(maxBlockSize);
+ encoder.prependVarNumber(tlv::Content);
+ return encoder.block();
+ }();
+
+ BOOST_CHECK_NO_THROW(context.append(largeBlock));
+ BOOST_CHECK_NO_THROW(context.end());
+
+ // two segments are generated
+ BOOST_REQUIRE_EQUAL(sendDataHistory.size(), 2);
+
+ // check segment0
+ BOOST_CHECK_EQUAL(sendDataHistory[0].dataName, makeSegmentName(0));
+ BOOST_CHECK_EQUAL(sendDataHistory[0].imsFresh, defaultImsFresh);
+ BOOST_CHECK_EQUAL(sendDataHistory[0].isFinalBlock, false);
+
+ // check segment1
+ BOOST_CHECK_EQUAL(sendDataHistory[1].dataName, makeSegmentName(1));
+ BOOST_CHECK_EQUAL(sendDataHistory[1].imsFresh, defaultImsFresh);
+ BOOST_CHECK_EQUAL(sendDataHistory[1].isFinalBlock, true);
+
+ // check data content
+ auto contentLargeBlock = concatenateDataContent();
+ BOOST_CHECK_NO_THROW(contentLargeBlock.parse());
+ BOOST_REQUIRE_EQUAL(contentLargeBlock.elements().size(), 1);
+ BOOST_CHECK_EQUAL(contentLargeBlock.elements()[0], largeBlock);
+}
+
+BOOST_AUTO_TEST_CASE(MultipleSmall)
+{
+ size_t nBlocks = 100;
+ for (size_t i = 0 ; i < nBlocks ; i ++) {
+ BOOST_CHECK_NO_THROW(context.append(contentBlock));
+ }
+ BOOST_CHECK_NO_THROW(context.end());
+
+ // check data to in-memory storage
+ BOOST_REQUIRE_EQUAL(sendDataHistory.size(), 1);
+ BOOST_CHECK_EQUAL(sendDataHistory[0].dataName, makeSegmentName(0));
+ BOOST_CHECK_EQUAL(sendDataHistory[0].imsFresh, defaultImsFresh);
+ BOOST_CHECK_EQUAL(sendDataHistory[0].isFinalBlock, true);
+
+ auto contentMultiBlocks = concatenateDataContent();
+ BOOST_CHECK_NO_THROW(contentMultiBlocks.parse());
+ BOOST_CHECK_EQUAL(contentMultiBlocks.elements().size(), nBlocks);
+ for (auto&& element : contentMultiBlocks.elements()) {
+ BOOST_CHECK_EQUAL(element, contentBlock);
+ }
+}
+
+BOOST_AUTO_TEST_SUITE_END() // Respond
+
+BOOST_AUTO_TEST_SUITE(Reject)
+
+BOOST_AUTO_TEST_CASE(Basic)
+{
+ BOOST_CHECK_NO_THROW(context.reject());
+ BOOST_REQUIRE_EQUAL(sendNackHistory.size(), 1);
+ BOOST_CHECK_EQUAL(sendNackHistory[0].getCode(), 400);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // Reject
+
+class AbnormalStateTestFixture
+{
+protected:
+ AbnormalStateTestFixture()
+ : context(Interest("/abnormal-state"), bind([]{}), bind([]{}))
+ {
+ }
+
+protected:
+ mgmt::StatusDatasetContext context;
+};
+
+BOOST_FIXTURE_TEST_SUITE(AbnormalState, AbnormalStateTestFixture)
+
+BOOST_AUTO_TEST_CASE(AppendReject)
+{
+ const uint8_t buf[] = {0x82, 0x01, 0x02};
+ BOOST_CHECK_NO_THROW(context.append(Block(buf, sizeof(buf))));
+ BOOST_CHECK_THROW(context.reject(), std::domain_error);
+}
+
+BOOST_AUTO_TEST_CASE(AppendEndReject)
+{
+ const uint8_t buf[] = {0x82, 0x01, 0x02};
+ BOOST_CHECK_NO_THROW(context.append(Block(buf, sizeof(buf))));
+ BOOST_CHECK_NO_THROW(context.end());
+ BOOST_CHECK_THROW(context.reject(), std::domain_error);
+}
+
+BOOST_AUTO_TEST_CASE(EndAppend)
+{
+ BOOST_CHECK_NO_THROW(context.end());
+ // end, append -> error
+ const uint8_t buf[] = {0x82, 0x01, 0x02};
+ BOOST_CHECK_THROW(context.append(Block(buf, sizeof(buf))), std::domain_error);
+}
+
+BOOST_AUTO_TEST_CASE(EndEnd)
+{
+ BOOST_CHECK_NO_THROW(context.end());
+ BOOST_CHECK_THROW(context.end(), std::domain_error);
+}
+
+BOOST_AUTO_TEST_CASE(EndReject)
+{
+ BOOST_CHECK_NO_THROW(context.end());
+ BOOST_CHECK_THROW(context.reject(), std::domain_error);
+}
+
+BOOST_AUTO_TEST_CASE(RejectAppend)
+{
+ BOOST_CHECK_NO_THROW(context.reject());
+ const uint8_t buf[] = {0x82, 0x01, 0x02};
+ BOOST_CHECK_THROW(context.append(Block(buf, sizeof(buf))), std::domain_error);
+}
+
+BOOST_AUTO_TEST_CASE(RejectEnd)
+{
+ BOOST_CHECK_NO_THROW(context.reject());
+ BOOST_CHECK_THROW(context.end(), std::domain_error);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // AbnormalState
+
+BOOST_AUTO_TEST_SUITE_END() // TestStatusDatasetContext
+BOOST_AUTO_TEST_SUITE_END() // Mgmt
+
+} // namespace tests
+} // namespace mgmt
+} // namespace ndn