management: nfd::ControlCommand
ControlCommand and its subclasses provide command-specific parameter validation.
refs #1397
Change-Id: Ieca08f5db530c14d1cd16ddd7af17e4ffe6eb344
diff --git a/src/management/nfd-control-command.hpp b/src/management/nfd-control-command.hpp
new file mode 100644
index 0000000..69407c0
--- /dev/null
+++ b/src/management/nfd-control-command.hpp
@@ -0,0 +1,386 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
+/**
+ * Copyright (C) 2013 Regents of the University of California.
+ * See COPYING for copyright and distribution information.
+ */
+
+#ifndef NDN_MANAGEMENT_NFD_CONTROL_COMMAND_HPP
+#define NDN_MANAGEMENT_NFD_CONTROL_COMMAND_HPP
+
+#include "nfd-control-parameters.hpp"
+#include "../util/command-interest-generator.hpp"
+
+namespace ndn {
+namespace nfd {
+
+/** \brief base class of NFD ControlCommand
+ * \sa http://redmine.named-data.net/projects/nfd/wiki/ControlCommand
+ */
+class ControlCommand
+{
+public:
+ /** \brief represents an error in ControlParameters
+ */
+ class ArgumentError : public std::invalid_argument
+ {
+ public:
+ explicit
+ ArgumentError(const std::string& what)
+ : std::invalid_argument(what)
+ {
+ }
+ };
+
+ /** \return Name prefix of this ControlCommand
+ */
+ const Name&
+ getPrefix() const
+ {
+ return m_prefix;
+ }
+
+ /** \brief make a Command Interest from parameters
+ */
+ Interest
+ makeCommandInterest(const ControlParameters& parameters,
+ CommandInterestGenerator& commandInterestGenerator) const
+ {
+ this->validateRequest(parameters);
+
+ Name name = m_prefix;
+ name.append(parameters.wireEncode());
+ Interest commandInterest(name);
+ commandInterestGenerator.generate(commandInterest);
+ return commandInterest;
+ }
+
+ /** \brief validate request parameters
+ * \throw ArgumentError
+ */
+ virtual void
+ validateRequest(const ControlParameters& parameters) const
+ {
+ m_requestValidator.validate(parameters);
+ }
+
+ /** \brief apply default values to missing fields in request
+ */
+ virtual void
+ applyDefaultsToRequest(ControlParameters& parameters) const
+ {
+ }
+
+ /** \brief validate response parameters
+ * \throw ArgumentError
+ */
+ virtual void
+ validateResponse(const ControlParameters& parameters) const
+ {
+ m_responseValidator.validate(parameters);
+ }
+
+ /** \brief apply default values to missing fields in response
+ */
+ virtual void
+ applyDefaultsToResponse(ControlParameters& parameters) const
+ {
+ }
+
+protected:
+ ControlCommand(const std::string& module, const std::string& verb)
+ : m_prefix("ndn:/localhost/nfd")
+ {
+ m_prefix.append(module).append(verb);
+ }
+
+ class FieldValidator
+ {
+ public:
+ FieldValidator()
+ {
+ m_required.resize(CONTROL_PARAMETER_UBOUND);
+ m_optional.resize(CONTROL_PARAMETER_UBOUND);
+ }
+
+ /** \brief declare a required field
+ */
+ FieldValidator&
+ required(ControlParameterField field)
+ {
+ m_required[field] = true;
+ return *this;
+ }
+
+ /** \brief declare an optional field
+ */
+ FieldValidator&
+ optional(ControlParameterField field)
+ {
+ m_optional[field] = true;
+ return *this;
+ }
+
+ /** \brief verify that all required fields are present,
+ * and all present fields are either required or optional
+ * \throw ArgumentError
+ */
+ void
+ validate(const ControlParameters& parameters) const
+ {
+ const std::vector<bool>& presentFields = parameters.getPresentFields();
+
+ for (size_t i = 0; i < CONTROL_PARAMETER_UBOUND; ++i) {
+ bool isPresent = presentFields[i];
+ if (m_required[i]) {
+ if (!isPresent) {
+ throw ArgumentError(CONTROL_PARAMETER_FIELD[i] + " is required but missing");
+ }
+ }
+ else if (isPresent && !m_optional[i]) {
+ throw ArgumentError(CONTROL_PARAMETER_FIELD[i] + " is forbidden but present");
+ }
+ }
+ }
+
+ private:
+ std::vector<bool> m_required;
+ std::vector<bool> m_optional;
+ };
+
+protected:
+ /** \brief FieldValidator for request ControlParameters
+ *
+ * Constructor of subclass should populate this validator.
+ */
+ FieldValidator m_requestValidator;
+ /** \brief FieldValidator for response ControlParameters
+ *
+ * Constructor of subclass should populate this validator.
+ */
+ FieldValidator m_responseValidator;
+
+private:
+ Name m_prefix;
+};
+
+
+/** \brief represents a faces/create command
+ * \sa http://redmine.named-data.net/projects/nfd/wiki/FaceMgmt#Create-a-face
+ */
+class FaceCreateCommand : public ControlCommand
+{
+public:
+ FaceCreateCommand()
+ : ControlCommand("faces", "create")
+ {
+ m_requestValidator
+ .required(CONTROL_PARAMETER_URI);
+ m_responseValidator
+ .required(CONTROL_PARAMETER_URI)
+ .required(CONTROL_PARAMETER_FACE_ID);
+ }
+
+ virtual void
+ validateResponse(const ControlParameters& parameters) const
+ {
+ this->ControlCommand::validateResponse(parameters);
+
+ if (parameters.getFaceId() == 0) {
+ throw ArgumentError("FaceId must not be zero");
+ }
+ }
+};
+
+
+/** \brief represents a faces/destroy command
+ * \sa http://redmine.named-data.net/projects/nfd/wiki/FaceMgmt#Destroy-a-face
+ */
+class FaceDestroyCommand : public ControlCommand
+{
+public:
+ FaceDestroyCommand()
+ : ControlCommand("faces", "destroy")
+ {
+ m_requestValidator
+ .required(CONTROL_PARAMETER_FACE_ID);
+ m_responseValidator = m_requestValidator;
+ }
+
+ virtual void
+ validateRequest(const ControlParameters& parameters) const
+ {
+ this->ControlCommand::validateRequest(parameters);
+
+ if (parameters.getFaceId() == 0) {
+ throw ArgumentError("FaceId must not be zero");
+ }
+ }
+
+ virtual void
+ validateResponse(const ControlParameters& parameters) const
+ {
+ this->validateRequest(parameters);
+ }
+};
+
+
+class FaceLocalControlCommand : public ControlCommand
+{
+protected:
+ explicit
+ FaceLocalControlCommand(const std::string& verb)
+ : ControlCommand("faces", verb)
+ {
+ m_requestValidator
+ .required(CONTROL_PARAMETER_LOCAL_CONTROL_FEATURE);
+ m_responseValidator = m_requestValidator;
+ }
+};
+
+
+/** \brief represents a faces/enable-local-control command
+ * \sa http://redmine.named-data.net/projects/nfd/wiki/FaceMgmt#Enable-a-LocalControlHeader-feature
+ */
+class FaceEnableLocalControlCommand : public FaceLocalControlCommand
+{
+public:
+ FaceEnableLocalControlCommand()
+ : FaceLocalControlCommand("enable-local-control")
+ {
+ }
+};
+
+
+/** \brief represents a faces/disable-local-control command
+ * \sa http://redmine.named-data.net/projects/nfd/wiki/FaceMgmt#Disable-a-LocalControlHeader-feature
+ */
+class FaceDisableLocalControlCommand : public FaceLocalControlCommand
+{
+public:
+ FaceDisableLocalControlCommand()
+ : FaceLocalControlCommand("disable-local-control")
+ {
+ }
+};
+
+
+/** \brief represents a fib/add-nexthop command
+ * \sa http://redmine.named-data.net/projects/nfd/wiki/FibMgmt#Add-a-nexthop
+ */
+class FibAddNextHopCommand : public ControlCommand
+{
+public:
+ FibAddNextHopCommand()
+ : ControlCommand("fib", "add-nexthop")
+ {
+ m_requestValidator
+ .required(CONTROL_PARAMETER_NAME)
+ .required(CONTROL_PARAMETER_FACE_ID)
+ .optional(CONTROL_PARAMETER_COST);
+ m_responseValidator
+ .required(CONTROL_PARAMETER_NAME)
+ .required(CONTROL_PARAMETER_FACE_ID)
+ .required(CONTROL_PARAMETER_COST);
+ }
+
+ virtual void
+ applyDefaultsToRequest(ControlParameters& parameters) const
+ {
+ if (!parameters.hasCost()) {
+ parameters.setCost(0);
+ }
+ }
+
+ virtual void
+ validateResponse(const ControlParameters& parameters) const
+ {
+ this->ControlCommand::validateResponse(parameters);
+
+ if (parameters.getFaceId() == 0) {
+ throw ArgumentError("FaceId must not be zero");
+ }
+ }
+};
+
+
+/** \brief represents a fib/remove-nexthop command
+ * \sa http://redmine.named-data.net/projects/nfd/wiki/FibMgmt#Remove-a-nexthop
+ */
+class FibRemoveNextHopCommand : public ControlCommand
+{
+public:
+ FibRemoveNextHopCommand()
+ : ControlCommand("fib", "remove-nexthop")
+ {
+ m_requestValidator
+ .required(CONTROL_PARAMETER_NAME)
+ .required(CONTROL_PARAMETER_FACE_ID);
+ m_responseValidator
+ .required(CONTROL_PARAMETER_NAME)
+ .required(CONTROL_PARAMETER_FACE_ID);
+ }
+
+ virtual void
+ validateResponse(const ControlParameters& parameters) const
+ {
+ this->ControlCommand::validateResponse(parameters);
+
+ if (parameters.getFaceId() == 0) {
+ throw ArgumentError("FaceId must not be zero");
+ }
+ }
+};
+
+
+/** \brief represents a strategy-choice/set command
+ * \sa http://redmine.named-data.net/projects/nfd/wiki/StrategyChoice#Set-the-strategy-for-a-namespace
+ */
+class StrategyChoiceSetCommand : public ControlCommand
+{
+public:
+ StrategyChoiceSetCommand()
+ : ControlCommand("strategy-choice", "set")
+ {
+ m_requestValidator
+ .required(CONTROL_PARAMETER_NAME)
+ .required(CONTROL_PARAMETER_STRATEGY);
+ m_responseValidator = m_requestValidator;
+ }
+};
+
+
+/** \brief represents a strategy-choice/set command
+ * \sa http://redmine.named-data.net/projects/nfd/wiki/StrategyChoice#Unset-the-strategy-for-a-namespace
+ */
+class StrategyChoiceUnsetCommand : public ControlCommand
+{
+public:
+ StrategyChoiceUnsetCommand()
+ : ControlCommand("strategy-choice", "unset")
+ {
+ m_requestValidator
+ .required(CONTROL_PARAMETER_NAME);
+ m_responseValidator = m_requestValidator;
+ }
+
+ virtual void
+ validateRequest(const ControlParameters& parameters) const
+ {
+ this->ControlCommand::validateRequest(parameters);
+
+ if (parameters.getName().size() == 0) {
+ throw ArgumentError("Name must not be ndn:/");
+ }
+ }
+
+ virtual void
+ validateResponse(const ControlParameters& parameters) const
+ {
+ this->validateRequest(parameters);
+ }
+};
+
+} // namespace nfd
+} // namespace ndn
+
+#endif // NDN_MANAGEMENT_NFD_CONTROL_COMMAND_HPP
diff --git a/src/management/nfd-control-parameters.hpp b/src/management/nfd-control-parameters.hpp
index b9e1d7b..836369b 100644
--- a/src/management/nfd-control-parameters.hpp
+++ b/src/management/nfd-control-parameters.hpp
@@ -18,6 +18,25 @@
typedef ControlParameters FibManagementOptions;
typedef ControlParameters StrategyChoiceOptions;
+enum ControlParameterField {
+ CONTROL_PARAMETER_NAME,
+ CONTROL_PARAMETER_FACE_ID,
+ CONTROL_PARAMETER_URI,
+ CONTROL_PARAMETER_LOCAL_CONTROL_FEATURE,
+ CONTROL_PARAMETER_COST,
+ CONTROL_PARAMETER_STRATEGY,
+ CONTROL_PARAMETER_UBOUND
+};
+
+const std::string CONTROL_PARAMETER_FIELD[CONTROL_PARAMETER_UBOUND] = {
+ "Name",
+ "FaceId",
+ "Uri",
+ "LocalControlFeature",
+ "Cost",
+ "Strategy",
+};
+
enum LocalControlFeature {
LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID = 1,
LOCAL_CONTROL_FEATURE_NEXT_HOP_FACE_ID = 2
@@ -35,10 +54,14 @@
}
};
- ControlParameters();
+ ControlParameters()
+ : m_hasFields(CONTROL_PARAMETER_UBOUND)
+ {
+ }
explicit
ControlParameters(const Block& block)
+ : m_hasFields(CONTROL_PARAMETER_UBOUND)
{
wireDecode(block);
}
@@ -57,13 +80,13 @@
bool
hasName() const
{
- return m_hasName;
+ return m_hasFields[CONTROL_PARAMETER_NAME];
}
const Name&
getName() const
{
- BOOST_ASSERT(m_hasName);
+ BOOST_ASSERT(this->hasName());
return m_name;
}
@@ -72,7 +95,7 @@
{
m_wire.reset();
m_name = name;
- m_hasName = true;
+ m_hasFields[CONTROL_PARAMETER_NAME] = true;
return *this;
}
@@ -80,20 +103,20 @@
unsetName()
{
m_wire.reset();
- m_hasName = false;
+ m_hasFields[CONTROL_PARAMETER_NAME] = false;
return *this;
}
bool
hasFaceId() const
{
- return m_hasFaceId;
+ return m_hasFields[CONTROL_PARAMETER_FACE_ID];
}
uint64_t
getFaceId() const
{
- BOOST_ASSERT(m_hasFaceId);
+ BOOST_ASSERT(this->hasFaceId());
return m_faceId;
}
@@ -102,7 +125,7 @@
{
m_wire.reset();
m_faceId = faceId;
- m_hasFaceId = true;
+ m_hasFields[CONTROL_PARAMETER_FACE_ID] = true;
return *this;
}
@@ -110,20 +133,20 @@
unsetFaceId()
{
m_wire.reset();
- m_hasFaceId = false;
+ m_hasFields[CONTROL_PARAMETER_FACE_ID] = false;
return *this;
}
bool
hasUri() const
{
- return m_hasUri;
+ return m_hasFields[CONTROL_PARAMETER_URI];
}
const std::string&
getUri() const
{
- BOOST_ASSERT(m_hasUri);
+ BOOST_ASSERT(this->hasUri());
return m_uri;
}
@@ -132,7 +155,7 @@
{
m_wire.reset();
m_uri = uri;
- m_hasUri = true;
+ m_hasFields[CONTROL_PARAMETER_URI] = true;
return *this;
}
@@ -140,20 +163,20 @@
unsetUri()
{
m_wire.reset();
- m_hasUri = false;
+ m_hasFields[CONTROL_PARAMETER_URI] = false;
return *this;
}
bool
hasLocalControlFeature() const
{
- return m_hasLocalControlFeature;
+ return m_hasFields[CONTROL_PARAMETER_LOCAL_CONTROL_FEATURE];
}
LocalControlFeature
getLocalControlFeature() const
{
- BOOST_ASSERT(m_hasLocalControlFeature);
+ BOOST_ASSERT(this->hasLocalControlFeature());
return m_localControlFeature;
}
@@ -162,7 +185,7 @@
{
m_wire.reset();
m_localControlFeature = localControlFeature;
- m_hasLocalControlFeature = true;
+ m_hasFields[CONTROL_PARAMETER_LOCAL_CONTROL_FEATURE] = true;
return *this;
}
@@ -170,20 +193,20 @@
unsetLocalControlFeature()
{
m_wire.reset();
- m_hasLocalControlFeature = false;
+ m_hasFields[CONTROL_PARAMETER_LOCAL_CONTROL_FEATURE] = false;
return *this;
}
bool
hasCost() const
{
- return m_hasCost;
+ return m_hasFields[CONTROL_PARAMETER_COST];
}
uint64_t
getCost() const
{
- BOOST_ASSERT(m_hasCost);
+ BOOST_ASSERT(this->hasCost());
return m_cost;
}
@@ -192,7 +215,7 @@
{
m_wire.reset();
m_cost = cost;
- m_hasCost = true;
+ m_hasFields[CONTROL_PARAMETER_COST] = true;
return *this;
}
@@ -200,20 +223,20 @@
unsetCost()
{
m_wire.reset();
- m_hasCost = false;
+ m_hasFields[CONTROL_PARAMETER_COST] = false;
return *this;
}
bool
hasStrategy() const
{
- return m_hasStrategy;
+ return m_hasFields[CONTROL_PARAMETER_STRATEGY];
}
const Name&
getStrategy() const
{
- BOOST_ASSERT(m_hasStrategy);
+ BOOST_ASSERT(this->hasStrategy());
return m_strategy;
}
@@ -222,7 +245,7 @@
{
m_wire.reset();
m_strategy = strategy;
- m_hasStrategy = true;
+ m_hasFields[CONTROL_PARAMETER_STRATEGY] = true;
return *this;
}
@@ -230,11 +253,19 @@
unsetStrategy()
{
m_wire.reset();
- m_hasStrategy = false;
+ m_hasFields[CONTROL_PARAMETER_STRATEGY] = false;
return *this;
}
+ const std::vector<bool>&
+ getPresentFields() const
+ {
+ return m_hasFields;
+ }
+
private: // fields
+ std::vector<bool> m_hasFields;
+
Name m_name;
uint64_t m_faceId;
std::string m_uri;
@@ -242,56 +273,38 @@
uint64_t m_cost;
Name m_strategy;
- bool m_hasName;
- bool m_hasFaceId;
- bool m_hasUri;
- bool m_hasLocalControlFeature;
- bool m_hasCost;
- bool m_hasStrategy;
-
private:
mutable Block m_wire;
};
-inline
-ControlParameters::ControlParameters()
- : m_hasName(false)
- , m_hasFaceId(false)
- , m_hasUri(false)
- , m_hasLocalControlFeature(false)
- , m_hasCost(false)
- , m_hasStrategy(false)
-{
-}
-
template<bool T>
inline size_t
ControlParameters::wireEncode(EncodingImpl<T>& encoder) const
{
size_t totalLength = 0;
- if (m_hasStrategy) {
+ if (this->hasStrategy()) {
totalLength += prependNestedBlock(encoder, tlv::nfd::Strategy, m_strategy);
}
- if (m_hasCost) {
+ if (this->hasCost()) {
totalLength += prependNonNegativeIntegerBlock(encoder, tlv::nfd::Cost, m_cost);
}
- if (m_hasLocalControlFeature) {
+ if (this->hasLocalControlFeature()) {
totalLength += prependNonNegativeIntegerBlock(encoder,
tlv::nfd::LocalControlFeature, m_localControlFeature);
}
- if (m_hasUri) {
+ if (this->hasUri()) {
size_t valLength = encoder.prependByteArray(
reinterpret_cast<const uint8_t*>(m_uri.c_str()), m_uri.size());
totalLength += valLength;
totalLength += encoder.prependVarNumber(valLength);
totalLength += encoder.prependVarNumber(tlv::nfd::Uri);
}
- if (m_hasFaceId) {
+ if (this->hasFaceId()) {
totalLength += prependNonNegativeIntegerBlock(encoder, tlv::nfd::FaceId, m_faceId);
}
- if (m_hasName) {
+ if (this->hasName()) {
totalLength += m_name.wireEncode(encoder);
}
@@ -327,38 +340,38 @@
Block::element_const_iterator val;
val = m_wire.find(Tlv::Name);
- m_hasName = val != m_wire.elements_end();
- if (m_hasName) {
+ m_hasFields[CONTROL_PARAMETER_NAME] = val != m_wire.elements_end();
+ if (this->hasName()) {
m_name.wireDecode(*val);
}
val = m_wire.find(tlv::nfd::FaceId);
- m_hasFaceId = val != m_wire.elements_end();
- if (m_hasFaceId) {
+ m_hasFields[CONTROL_PARAMETER_FACE_ID] = val != m_wire.elements_end();
+ if (this->hasFaceId()) {
m_faceId = static_cast<uint64_t>(readNonNegativeInteger(*val));
}
val = m_wire.find(tlv::nfd::Uri);
- m_hasUri = val != m_wire.elements_end();
- if (m_hasUri) {
+ m_hasFields[CONTROL_PARAMETER_URI] = val != m_wire.elements_end();
+ if (this->hasUri()) {
m_uri.assign(reinterpret_cast<const char*>(val->value()), val->value_size());
}
val = m_wire.find(tlv::nfd::LocalControlFeature);
- m_hasLocalControlFeature = val != m_wire.elements_end();
- if (m_hasLocalControlFeature) {
+ m_hasFields[CONTROL_PARAMETER_LOCAL_CONTROL_FEATURE] = val != m_wire.elements_end();
+ if (this->hasLocalControlFeature()) {
m_localControlFeature = static_cast<LocalControlFeature>(readNonNegativeInteger(*val));
}
val = m_wire.find(tlv::nfd::Cost);
- m_hasCost = val != m_wire.elements_end();
- if (m_hasCost) {
+ m_hasFields[CONTROL_PARAMETER_COST] = val != m_wire.elements_end();
+ if (this->hasCost()) {
m_cost = static_cast<uint64_t>(readNonNegativeInteger(*val));
}
val = m_wire.find(tlv::nfd::Strategy);
- m_hasStrategy = val != m_wire.elements_end();
- if (m_hasStrategy) {
+ m_hasFields[CONTROL_PARAMETER_STRATEGY] = val != m_wire.elements_end();
+ if (this->hasStrategy()) {
val->parse();
if (val->elements().empty()) {
throw Error("expecting Strategy/Name");
diff --git a/tests/management/nfd-control-command.cpp b/tests/management/nfd-control-command.cpp
new file mode 100644
index 0000000..eeb18ed
--- /dev/null
+++ b/tests/management/nfd-control-command.cpp
@@ -0,0 +1,178 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
+/**
+ * Copyright (C) 2013 Regents of the University of California.
+ * See COPYING for copyright and distribution information.
+ */
+
+#include "management/nfd-control-command.hpp"
+// Having a separate compilation unit is necessary to ensure .hpp can compile on its own.
+
+#include <boost/test/unit_test.hpp>
+
+namespace ndn {
+namespace nfd {
+
+BOOST_AUTO_TEST_SUITE(NfdControlCommand)
+
+BOOST_AUTO_TEST_CASE(FaceCreate)
+{
+ FaceCreateCommand command;
+ BOOST_CHECK_EQUAL(command.getPrefix(), "ndn:/localhost/nfd/faces/create");
+
+ ControlParameters p1;
+ p1.setUri("tcp4://192.0.2.1")
+ .setFaceId(4);
+ BOOST_CHECK_THROW(command.validateRequest(p1), ControlCommand::ArgumentError);
+ BOOST_CHECK_NO_THROW(command.validateResponse(p1));
+
+ ControlParameters p2;
+ p2.setName("ndn:/example");
+ BOOST_CHECK_THROW(command.validateRequest(p2), ControlCommand::ArgumentError);
+ BOOST_CHECK_THROW(command.validateResponse(p2), ControlCommand::ArgumentError);
+
+ ControlParameters p3;
+ p3.setUri("tcp4://192.0.2.1")
+ .setFaceId(0);
+ BOOST_CHECK_THROW(command.validateResponse(p3), ControlCommand::ArgumentError);
+}
+
+BOOST_AUTO_TEST_CASE(FaceDestroy)
+{
+ FaceDestroyCommand command;
+ BOOST_CHECK_EQUAL(command.getPrefix(), "ndn:/localhost/nfd/faces/destroy");
+
+ 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(0);
+ 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));
+}
+
+BOOST_AUTO_TEST_CASE(FaceEnableLocalControl)
+{
+ FaceEnableLocalControlCommand command;
+ BOOST_CHECK_EQUAL(command.getPrefix(), "ndn:/localhost/nfd/faces/enable-local-control");
+
+ ControlParameters p1;
+ p1.setLocalControlFeature(LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID);
+ BOOST_CHECK_NO_THROW(command.validateRequest(p1));
+ BOOST_CHECK_NO_THROW(command.validateResponse(p1));
+
+ ControlParameters p2;
+ p2.setLocalControlFeature(LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID)
+ .setFaceId(9);
+ BOOST_CHECK_THROW(command.validateRequest(p2), ControlCommand::ArgumentError);
+ BOOST_CHECK_THROW(command.validateResponse(p2), ControlCommand::ArgumentError);
+}
+
+BOOST_AUTO_TEST_CASE(FaceDisableLocalControl)
+{
+ FaceDisableLocalControlCommand command;
+ BOOST_CHECK_EQUAL(command.getPrefix(), "ndn:/localhost/nfd/faces/disable-local-control");
+
+ ControlParameters p1;
+ p1.setLocalControlFeature(LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID);
+ BOOST_CHECK_NO_THROW(command.validateRequest(p1));
+ BOOST_CHECK_NO_THROW(command.validateResponse(p1));
+
+ ControlParameters p2;
+ p2.setLocalControlFeature(LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID)
+ .setFaceId(9);
+ BOOST_CHECK_THROW(command.validateRequest(p2), ControlCommand::ArgumentError);
+ BOOST_CHECK_THROW(command.validateResponse(p2), ControlCommand::ArgumentError);
+}
+
+BOOST_AUTO_TEST_CASE(FibAddNextHop)
+{
+ FibAddNextHopCommand command;
+ BOOST_CHECK_EQUAL(command.getPrefix(), "ndn:/localhost/nfd/fib/add-nexthop");
+
+ ControlParameters p1;
+ p1.setName("ndn:/")
+ .setFaceId(22);
+ BOOST_CHECK_NO_THROW(command.validateRequest(p1));
+ BOOST_CHECK_THROW(command.validateResponse(p1), ControlCommand::ArgumentError);
+
+ ControlParameters p2;
+ p2.setName("ndn:/example")
+ .setFaceId(0)
+ .setCost(6);
+ BOOST_CHECK_NO_THROW(command.validateRequest(p2));
+ BOOST_CHECK_THROW(command.validateResponse(p2), ControlCommand::ArgumentError);
+
+ command.applyDefaultsToRequest(p1);
+ BOOST_REQUIRE(p1.hasCost());
+ BOOST_CHECK_EQUAL(p1.getCost(), 0);
+}
+
+BOOST_AUTO_TEST_CASE(FibRemoveNextHop)
+{
+ FibRemoveNextHopCommand command;
+ BOOST_CHECK_EQUAL(command.getPrefix(), "ndn:/localhost/nfd/fib/remove-nexthop");
+
+ ControlParameters p1;
+ p1.setName("ndn:/")
+ .setFaceId(22);
+ BOOST_CHECK_NO_THROW(command.validateRequest(p1));
+ BOOST_CHECK_NO_THROW(command.validateResponse(p1));
+
+ ControlParameters p2;
+ p2.setName("ndn:/example")
+ .setFaceId(0);
+ BOOST_CHECK_NO_THROW(command.validateRequest(p2));
+ BOOST_CHECK_THROW(command.validateResponse(p2), ControlCommand::ArgumentError);
+}
+
+BOOST_AUTO_TEST_CASE(StrategyChoiceSet)
+{
+ StrategyChoiceSetCommand command;
+ BOOST_CHECK_EQUAL(command.getPrefix(), "ndn:/localhost/nfd/strategy-choice/set");
+
+ ControlParameters p1;
+ p1.setName("ndn:/")
+ .setStrategy("ndn:/strategy/P");
+ BOOST_CHECK_NO_THROW(command.validateRequest(p1));
+ BOOST_CHECK_NO_THROW(command.validateResponse(p1));
+
+ 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;
+ BOOST_CHECK_EQUAL(command.getPrefix(), "ndn:/localhost/nfd/strategy-choice/unset");
+
+ ControlParameters p1;
+ p1.setName("ndn:/example");
+ BOOST_CHECK_NO_THROW(command.validateRequest(p1));
+ BOOST_CHECK_NO_THROW(command.validateResponse(p1));
+
+ 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_SUITE_END()
+
+} // namespace nfd
+} // namespace ndn