blob: f1a9110ad2ad8bd768eba8a85e772cc79042db81 [file] [log] [blame]
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
* Copyright (c) 2014-2023, Regents of the University of California.
*
* This file is part of NDN repo-ng (Next generation of NDN repository).
* See AUTHORS.md for complete list of repo-ng authors and contributors.
*
* repo-ng is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Foundation,
* either version 3 of the License, or (at your option) any later version.
*
* repo-ng 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with
* repo-ng, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
*/
#include "handles/delete-handle.hpp"
#include "handles/write-handle.hpp"
#include "storage/repo-storage.hpp"
#include "storage/sqlite-storage.hpp"
#include "command-fixture.hpp"
#include "../repo-storage-fixture.hpp"
#include "../dataset-fixtures.hpp"
#include <ndn-cxx/security/interest-signer.hpp>
#include <ndn-cxx/security/signing-helpers.hpp>
#include <ndn-cxx/util/random.hpp>
#include <ndn-cxx/util/time.hpp>
#include <boost/test/unit_test.hpp>
namespace repo::tests {
using ndn::time::milliseconds;
// All the test cases in this test suite should be run at once.
BOOST_AUTO_TEST_SUITE(TestBasicCommandInsertDelete)
const uint8_t CONTENT[] = {3, 1, 4, 1, 5, 9, 2, 6};
template<class Dataset>
class Fixture : public CommandFixture, public RepoStorageFixture, public Dataset
{
public:
Fixture()
: writeHandle(repoFace, *handle, dispatcher, scheduler, validator)
, deleteHandle(repoFace, *handle, dispatcher, scheduler, validator)
, insertFace(repoFace.getIoContext())
, deleteFace(repoFace.getIoContext())
, signer(m_keyChain)
{
Name cmdPrefix("/repo/command");
repoFace.registerPrefix(cmdPrefix, nullptr,
[] (const Name&, const std::string& reason) {
BOOST_FAIL("Command prefix registration error: " << reason);
});
}
void
scheduleInsertEvent();
void
scheduleDeleteEvent();
void
onInsertInterest(const Interest& interest);
void
onRegisterFailed(const std::string& reason);
void
delayedInterest();
void
onInsertData(const Interest& interest, const Data& data);
void
onDeleteData(const Interest& interest, const Data& data);
void
onInsertTimeout(const Interest& interest);
void
onDeleteTimeout(const Interest& interest);
void
sendInsertInterest(const Interest& interest);
void
sendDeleteInterest(const Interest& deleteInterest);
void
checkInsertOk(const Interest& interest);
void
checkDeleteOk(const Interest& interest);
public:
WriteHandle writeHandle;
DeleteHandle deleteHandle;
Face insertFace;
Face deleteFace;
std::map<Name, ndn::scheduler::EventId> insertEvents;
std::map<Name, Name> deleteNamePairs;
ndn::security::InterestSigner signer;
};
template<class T>
void
Fixture<T>::onInsertInterest(const Interest& interest)
{
Data data(interest.getName());
data.setContent(CONTENT);
m_keyChain.sign(data);
insertFace.put(data);
auto eventIt = insertEvents.find(interest.getName());
if (eventIt != insertEvents.end()) {
eventIt->second.cancel();
insertEvents.erase(eventIt);
}
// schedule an event to check whether insert is ok
scheduler.schedule(500_ms, [=] { this->checkInsertOk(interest); });
}
template<class T>
void
Fixture<T>::onRegisterFailed(const std::string& reason)
{
BOOST_ERROR("ERROR: Failed to register prefix in local hub's daemon" + reason);
}
template<class T>
void
Fixture<T>::delayedInterest()
{
BOOST_ERROR("Fetching interest does not come. It may be satisfied in CS or something is wrong");
}
template<class T>
void
Fixture<T>::onInsertData(const Interest&, const Data& data)
{
RepoCommandResponse response;
response.wireDecode(data.getContent().blockFromValue());
BOOST_CHECK_EQUAL(response.getCode(), 100);
}
template<class T>
void
Fixture<T>::onDeleteData(const Interest& interest, const Data& data)
{
RepoCommandResponse response;
response.wireDecode(data.getContent().blockFromValue());
BOOST_CHECK_EQUAL(response.getCode(), 200);
// schedule an event to check whether delete is ok
scheduler.schedule(100_ms, [=] { this->checkDeleteOk(interest); });
}
template<class T>
void
Fixture<T>::onInsertTimeout(const Interest&)
{
BOOST_ERROR("Insert command timeout");
}
template<class T>
void
Fixture<T>::onDeleteTimeout(const Interest&)
{
BOOST_ERROR("Delete command timeout");
}
template<class T>
void
Fixture<T>::sendInsertInterest(const Interest& insertInterest)
{
insertFace.expressInterest(insertInterest,
std::bind(&Fixture<T>::onInsertData, this, _1, _2),
std::bind(&Fixture<T>::onInsertTimeout, this, _1), // Nack
std::bind(&Fixture<T>::onInsertTimeout, this, _1));
}
template<class T>
void
Fixture<T>::sendDeleteInterest(const Interest& deleteInterest)
{
deleteFace.expressInterest(deleteInterest,
std::bind(&Fixture<T>::onDeleteData, this, _1, _2),
std::bind(&Fixture<T>::onDeleteTimeout, this, _1), // Nack
std::bind(&Fixture<T>::onDeleteTimeout, this, _1));
}
template<class T>
void
Fixture<T>::checkInsertOk(const Interest& interest)
{
BOOST_TEST_CONTEXT("Interest " << interest) {
auto data = handle->readData(interest);
if (data) {
BOOST_TEST(data->getContent().value_bytes() == CONTENT, boost::test_tools::per_element());
}
else {
BOOST_ERROR("Insert check failed");
}
}
}
template<class T>
void
Fixture<T>::checkDeleteOk(const Interest& interest)
{
auto nameIt = deleteNamePairs.find(interest.getName());
BOOST_CHECK_MESSAGE(nameIt != deleteNamePairs.end(), "Delete name not found: " << interest.getName());
Interest dataInterest(nameIt->second);
auto data = handle->readData(dataInterest);
BOOST_CHECK(!data);
}
template<class T>
void
Fixture<T>::scheduleInsertEvent()
{
int timeCount = 1;
for (auto i = this->data.begin(); i != this->data.end(); ++i) {
Name insertCommandName("/repo/command/insert");
RepoCommandParameter insertParameter;
insertParameter.setName(Name((*i)->getName())
.appendNumber(ndn::random::generateWord64()));
insertCommandName.append(tlv::GenericNameComponent, insertParameter.wireEncode());
Interest insertInterest = signer.makeCommandInterest(insertCommandName);
// schedule a job to express insertInterest every 50ms
scheduler.schedule(milliseconds(timeCount * 50 + 1000),
std::bind(&Fixture<T>::sendInsertInterest, this, insertInterest));
// schedule what to do when interest timeout
auto delayEventId = scheduler.schedule(milliseconds(5000 + timeCount * 50),
std::bind(&Fixture<T>::delayedInterest, this));
insertEvents[insertParameter.getName()] = delayEventId;
// The delayEvent will be canceled in onInsertInterest
insertFace.setInterestFilter(insertParameter.getName(),
std::bind(&Fixture<T>::onInsertInterest, this, _2),
ndn::RegisterPrefixSuccessCallback(),
std::bind(&Fixture<T>::onRegisterFailed, this, _2));
timeCount++;
}
}
template<class T>
void
Fixture<T>::scheduleDeleteEvent()
{
int timeCount = 1;
for (auto i = this->data.begin(); i != this->data.end(); ++i) {
Name deleteCommandName("/repo/command/delete");
RepoCommandParameter deleteParameter;
deleteParameter.setProcessId(ndn::random::generateWord64());
deleteParameter.setName((*i)->getName());
deleteCommandName.append(tlv::GenericNameComponent, deleteParameter.wireEncode());
Interest deleteInterest = signer.makeCommandInterest(deleteCommandName);
deleteNamePairs[deleteInterest.getName()] = (*i)->getName();
scheduler.schedule(milliseconds(4000 + timeCount * 50),
std::bind(&Fixture<T>::sendDeleteInterest, this, deleteInterest));
timeCount++;
}
}
using Datasets = boost::mp11::mp_list<BasicDataset, FetchByPrefixDataset, SamePrefixDataset<10>>;
BOOST_FIXTURE_TEST_CASE_TEMPLATE(InsertDelete, T, Datasets, Fixture<T>)
{
// schedule events
this->scheduler.schedule(0_s, [this] { this->scheduleInsertEvent(); });
this->scheduler.schedule(10_s, [this] { this->scheduleDeleteEvent(); });
this->repoFace.processEvents(30_s);
}
BOOST_AUTO_TEST_SUITE_END()
} // namespace repo::tests