blob: 2da5270245893f485a999a2ac2a09ef37bc44753 [file] [log] [blame]
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
* Copyright (c) 2013-2023 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 "ndn-cxx/mgmt/nfd/control-command.hpp"
#include "tests/boost-test.hpp"
namespace ndn::tests {
using namespace ndn::nfd;
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);
// Flags and Mask must be specified together
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;
// FaceId must be valid
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);
// Persistency and Flags are required in response
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);
// Good request, bad response (Mask is forbidden but present)
ControlParameters p2;
p2.setFaceId(1)
.setFacePersistency(FACE_PERSISTENCY_PERSISTENT)
.setBaseCongestionMarkingInterval(765_ns)
.setDefaultCongestionThreshold(54321)
.setMtu(8192)
.setFlagBit(BIT_LOCAL_FIELDS_ENABLED, false);
BOOST_CHECK_NO_THROW(command.validateRequest(p2));
BOOST_CHECK_THROW(command.validateResponse(p2), ControlCommand::ArgumentError);
// Flags without Mask (good response, bad request)
p2.unsetMask();
BOOST_CHECK_THROW(command.validateRequest(p2), ControlCommand::ArgumentError);
BOOST_CHECK_NO_THROW(command.validateResponse(p2));
// FaceId is optional in request
p2.setFlagBit(BIT_LOCAL_FIELDS_ENABLED, false);
p2.unsetFaceId();
BOOST_CHECK_NO_THROW(command.validateRequest(p2));
// Name is forbidden
ControlParameters p3;
p3.setFaceId(1)
.setName("/ndn/name");
BOOST_CHECK_THROW(command.validateRequest(p3), ControlCommand::ArgumentError);
BOOST_CHECK_THROW(command.validateResponse(p3), ControlCommand::ArgumentError);
// Uri is forbidden
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);
// Empty request is valid, empty response is invalid
ControlParameters p5;
BOOST_CHECK_NO_THROW(command.validateRequest(p5));
BOOST_CHECK_THROW(command.validateResponse(p5), ControlCommand::ArgumentError);
BOOST_CHECK(!p5.hasFaceId());
// Default request, not valid response due to missing FacePersistency and Flags
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);
}
BOOST_AUTO_TEST_CASE(FaceDestroy)
{
FaceDestroyCommand command;
// Uri is forbidden
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);
// FaceId must be valid
ControlParameters p2;
p2.setFaceId(INVALID_FACE_ID);
BOOST_CHECK_THROW(command.validateRequest(p2), ControlCommand::ArgumentError);
BOOST_CHECK_THROW(command.validateResponse(p2), ControlCommand::ArgumentError);
// Good request, good response
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;
// Cost required in response
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));
// Good request, bad response (FaceId must be valid)
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);
// Default request
command.applyDefaultsToRequest(p1);
BOOST_REQUIRE(p1.hasCost());
BOOST_CHECK_EQUAL(p1.getCost(), 0);
// FaceId optional in request
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;
// Good request, good response
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));
// Good request, bad response (FaceId must be valid)
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);
// FaceId is optional in request
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;
// Good request, good response
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));
// Strategy is required in both requests and responses
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;
// Good request, good response
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));
// Strategy is forbidden
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);
// Name must have at least one component
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;
// Good request, response missing many fields
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));
// Default request
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);
// Good request, good response
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;
// Good request, good response
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));
// Good request, bad response (FaceId must be valid)
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);
// FaceId is optional in request, required in response
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 ndn::tests