blob: f219729dab841bd6641efa3916d52ffd218d225a [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/status-dataset-context.hpp"
#include "tests/test-common.hpp"
namespace ndn::tests {
using namespace ndn::mgmt;
class StatusDatasetContextFixture
{
private:
struct SendDataArgs
{
Name dataName;
Block content;
bool isFinalBlock;
};
protected:
StatusDatasetContextFixture()
: interest(makeInterest("/test/context/interest"))
, context(*interest,
[this] (auto&&... args) {
sendDataHistory.push_back({std::forward<decltype(args)>(args)...});
},
[this] (const auto& resp) {
sendNackHistory.push_back(resp);
})
{
}
Name
makeSegmentName(size_t segmentNo) const
{
auto name = context.getPrefix();
return name.appendSegment(segmentNo);
}
ConstBufferPtr
concatenateDataContent() const
{
auto buf = std::make_shared<Buffer>();
for (const auto& args : sendDataHistory) {
buf->insert(buf->end(), args.content.value_begin(), args.content.value_end());
}
return buf;
}
protected:
shared_ptr<Interest> interest;
StatusDatasetContext context;
std::vector<SendDataArgs> sendDataHistory;
std::vector<ControlResponse> sendNackHistory;
};
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_EQUAL(context.getPrefix().getPrefix(-1), validPrefix);
BOOST_CHECK(context.getPrefix()[-1].isVersion());
// trailing version component is preserved
validPrefix.appendVersion(42);
BOOST_CHECK_NO_THROW(context.setPrefix(validPrefix));
BOOST_CHECK_EQUAL(context.getPrefix(), validPrefix);
}
BOOST_AUTO_TEST_CASE(SetInvalid)
{
// Interest name is not a prefix of invalidPrefix
Name invalidPrefix = Name(interest->getName()).getPrefix(-1).append("/invalid");
BOOST_CHECK_EXCEPTION(context.setPrefix(invalidPrefix), std::invalid_argument, [] (const auto& e) {
return e.what() == "prefix must start with the Interest's name"s;
});
// invalidPrefix contains a segment component
invalidPrefix = Name(interest->getName()).appendSegment(1);
BOOST_CHECK_EXCEPTION(context.setPrefix(invalidPrefix), std::invalid_argument, [] (const auto& e) {
return e.what() == "prefix must not contain a segment component"s;
});
}
BOOST_AUTO_TEST_CASE(SetValidAfterAppend)
{
Name validPrefix = Name(interest->getName()).append("/valid");
context.append({0x12, 0x34});
BOOST_CHECK_EXCEPTION(context.setPrefix(validPrefix), std::logic_error, [] (const auto& e) {
return e.what() == "cannot call setPrefix() after append/end/reject"s;
});
}
BOOST_AUTO_TEST_CASE(SetValidAfterEnd)
{
Name validPrefix = Name(interest->getName()).append("/valid");
context.end();
BOOST_CHECK_EXCEPTION(context.setPrefix(validPrefix), std::logic_error, [] (const auto& e) {
return e.what() == "cannot call setPrefix() after append/end/reject"s;
});
}
BOOST_AUTO_TEST_CASE(SetValidAfterReject)
{
Name validPrefix = Name(interest->getName()).append("/valid");
context.reject();
BOOST_CHECK_EXCEPTION(context.setPrefix(validPrefix), std::logic_error, [] (const auto& e) {
return e.what() == "cannot call setPrefix() after append/end/reject"s;
});
}
BOOST_AUTO_TEST_SUITE_END() // Prefix
BOOST_AUTO_TEST_SUITE(Respond)
BOOST_AUTO_TEST_CASE(Basic)
{
const auto testBlock = makeStringBlock(tlv::Content, "TEST");
context.append(testBlock);
BOOST_CHECK(sendDataHistory.empty()); // end() not called yet
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(), testBlock);
BOOST_CHECK_EQUAL(args.isFinalBlock, true);
}
BOOST_AUTO_TEST_CASE(Empty)
{
context.append({});
BOOST_TEST(sendDataHistory.empty()); // end() not called yet
context.end();
BOOST_TEST_REQUIRE(sendDataHistory.size() == 1);
const auto& args = sendDataHistory[0];
BOOST_TEST(args.dataName == makeSegmentName(0));
BOOST_TEST(args.content.value_size() == 0);
BOOST_TEST(args.isFinalBlock);
}
BOOST_AUTO_TEST_CASE(Large)
{
const std::vector<uint8_t> big(10000, 'A');
context.append(big);
BOOST_CHECK_EQUAL(sendDataHistory.size(), 1);
context.end();
BOOST_REQUIRE_EQUAL(sendDataHistory.size(), 2);
// check segment 0
BOOST_CHECK_EQUAL(sendDataHistory[0].dataName, makeSegmentName(0));
BOOST_CHECK_EQUAL(sendDataHistory[0].isFinalBlock, false);
// check segment 1
BOOST_CHECK_EQUAL(sendDataHistory[1].dataName, makeSegmentName(1));
BOOST_CHECK_EQUAL(sendDataHistory[1].isFinalBlock, true);
// check data content
BOOST_TEST(*concatenateDataContent() == big, boost::test_tools::per_element());
}
BOOST_AUTO_TEST_CASE(MultipleSmall)
{
const size_t nBlocks = 1000;
const auto contentBlock = makeStringBlock(0xFFFF, "Test Data Content");
for (size_t i = 0 ; i < nBlocks ; i ++) {
context.append(contentBlock);
}
context.end();
BOOST_TEST_REQUIRE(sendDataHistory.size() == 3);
BOOST_TEST(sendDataHistory[0].dataName == makeSegmentName(0));
BOOST_TEST(!sendDataHistory[0].isFinalBlock);
BOOST_TEST(sendDataHistory[1].dataName == makeSegmentName(1));
BOOST_TEST(!sendDataHistory[1].isFinalBlock);
BOOST_TEST(sendDataHistory[2].dataName == makeSegmentName(2));
BOOST_TEST(sendDataHistory[2].isFinalBlock);
Block contentMultiBlocks(tlv::Content, concatenateDataContent());
contentMultiBlocks.parse();
BOOST_TEST(contentMultiBlocks.elements().size() == nBlocks);
for (const auto& element : contentMultiBlocks.elements()) {
BOOST_TEST(element == contentBlock, boost::test_tools::per_element());
}
}
BOOST_AUTO_TEST_SUITE_END() // Respond
BOOST_AUTO_TEST_CASE(Reject)
{
BOOST_CHECK_NO_THROW(context.reject());
BOOST_REQUIRE_EQUAL(sendNackHistory.size(), 1);
BOOST_CHECK_EQUAL(sendNackHistory[0].getCode(), 400);
}
class AbnormalStateTestFixture
{
protected:
StatusDatasetContext context{Interest("/abnormal-state"), [] (auto&&...) {}, [] (auto&&...) {}};
};
BOOST_FIXTURE_TEST_SUITE(AbnormalState, AbnormalStateTestFixture)
BOOST_AUTO_TEST_CASE(AppendReject)
{
BOOST_CHECK_NO_THROW(context.append({0x82, 0x01, 0x02}));
BOOST_CHECK_EXCEPTION(context.reject(), std::logic_error, [] (const auto& e) {
return e.what() == "cannot call reject() after append/end"s;
});
}
BOOST_AUTO_TEST_CASE(AppendEndReject)
{
BOOST_CHECK_NO_THROW(context.append({0x82, 0x01, 0x02}));
BOOST_CHECK_NO_THROW(context.end());
BOOST_CHECK_EXCEPTION(context.reject(), std::logic_error, [] (const auto& e) {
return e.what() == "cannot call reject() after append/end"s;
});
}
BOOST_AUTO_TEST_CASE(EndAppend)
{
BOOST_CHECK_NO_THROW(context.end());
BOOST_CHECK_EXCEPTION(context.append({0x82, 0x01, 0x02}), std::logic_error, [] (const auto& e) {
return e.what() == "cannot call append() on a finalized context"s;
});
}
BOOST_AUTO_TEST_CASE(EndEnd)
{
BOOST_CHECK_NO_THROW(context.end());
BOOST_CHECK_EXCEPTION(context.end(), std::logic_error, [] (const auto& e) {
return e.what() == "cannot call end() on a finalized context"s;
});
}
BOOST_AUTO_TEST_CASE(EndReject)
{
BOOST_CHECK_NO_THROW(context.end());
BOOST_CHECK_EXCEPTION(context.reject(), std::logic_error, [] (const auto& e) {
return e.what() == "cannot call reject() after append/end"s;
});
}
BOOST_AUTO_TEST_CASE(RejectAppend)
{
BOOST_CHECK_NO_THROW(context.reject());
BOOST_CHECK_EXCEPTION(context.append({0x82, 0x01, 0x02}), std::logic_error, [] (const auto& e) {
return e.what() == "cannot call append() on a finalized context"s;
});
}
BOOST_AUTO_TEST_CASE(RejectEnd)
{
BOOST_CHECK_NO_THROW(context.reject());
BOOST_CHECK_EXCEPTION(context.end(), std::logic_error, [] (const auto& e) {
return e.what() == "cannot call end() on a finalized context"s;
});
}
BOOST_AUTO_TEST_SUITE_END() // AbnormalState
BOOST_AUTO_TEST_SUITE_END() // TestStatusDatasetContext
BOOST_AUTO_TEST_SUITE_END() // Mgmt
} // namespace ndn::tests