blob: bb6acad67484516cddb0194fd07afb9233572f5e [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/status-dataset.hpp"
#include "ndn-cxx/mgmt/nfd/controller.hpp"
#include "tests/test-common.hpp"
#include "tests/unit/mgmt/nfd/controller-fixture.hpp"
namespace ndn::tests {
using namespace ndn::nfd;
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)
{
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.
EncodingBuffer buffer;
payload2.wireEncode(buffer);
payload1.wireEncode(buffer);
auto data = this->prepareDatasetReply(prefix);
data->setContent(buffer);
face.receive(*signData(data));
}
private:
std::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 = std::make_shared<Data>(name);
data->setFreshnessPeriod(1_s);
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>(
[] (auto&&) { 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>(
[] (auto&&) { BOOST_FAIL("fetchDataset should not succeed"); },
datasetFailCallback);
this->advanceClocks(500_ms);
auto data = makeData("/localhost/nfd/faces/list/%FD%00");
data->setFreshnessPeriod(1_s);
face.receive(*data);
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>(
[] (auto&&) { 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>(
[] (auto&&) { 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>(
[] (auto&&) { 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>(
[] (auto&&) { 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>(
[] (auto&&) { 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 ndn::tests