blob: 38e7bf27dd2d95232d4154fbdc80edd03a30df7a [file] [log] [blame]
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
* Copyright (c) 2014-2020, The University of Memphis
*
* This file is part of PSync.
* See AUTHORS.md for complete list of PSync authors and contributors.
*
* PSync 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.
*
* PSync 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 a copy of the GNU Lesser General Public License along with
* PSync, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
**/
#include "PSync/full-producer.hpp"
#include "PSync/consumer.hpp"
#include "PSync/detail/state.hpp"
#include "tests/boost-test.hpp"
#include "tests/unit-test-time-fixture.hpp"
#include <ndn-cxx/name.hpp>
#include <ndn-cxx/util/dummy-client-face.hpp>
namespace psync {
using namespace ndn;
class FullSyncFixture : public tests::UnitTestTimeFixture
{
protected:
void
addNode(int id)
{
BOOST_ASSERT(id >= 0 && id <= 3);
faces[id] = std::make_shared<util::DummyClientFace>(io, util::DummyClientFace::Options{true, true});
userPrefixes[id] = Name("userPrefix" + to_string(id));
nodes[id] = make_shared<FullProducer>(40, *faces[id], syncPrefix, userPrefixes[id],
[] (const auto&) {});
}
protected:
const Name syncPrefix = "/psync";
shared_ptr<util::DummyClientFace> faces[4];
Name userPrefixes[4];
shared_ptr<FullProducer> nodes[4];
};
BOOST_FIXTURE_TEST_SUITE(TestFullSync, FullSyncFixture)
BOOST_AUTO_TEST_CASE(TwoNodesSimple)
{
addNode(0);
addNode(1);
faces[0]->linkTo(*faces[1]);
advanceClocks(ndn::time::milliseconds(10));
nodes[0]->publishName(userPrefixes[0]);
advanceClocks(ndn::time::milliseconds(10), 100);
BOOST_CHECK_EQUAL(nodes[0]->getSeqNo(userPrefixes[0]).value_or(-1), 1);
BOOST_CHECK_EQUAL(nodes[1]->getSeqNo(userPrefixes[0]).value_or(-1), 1);
nodes[1]->publishName(userPrefixes[1]);
advanceClocks(ndn::time::milliseconds(10), 100);
BOOST_CHECK_EQUAL(nodes[0]->getSeqNo(userPrefixes[1]).value_or(-1), 1);
BOOST_CHECK_EQUAL(nodes[1]->getSeqNo(userPrefixes[1]).value_or(-1), 1);
nodes[1]->publishName(userPrefixes[1]);
advanceClocks(ndn::time::milliseconds(10), 100);
BOOST_CHECK_EQUAL(nodes[0]->getSeqNo(userPrefixes[1]).value_or(-1), 2);
BOOST_CHECK_EQUAL(nodes[1]->getSeqNo(userPrefixes[1]).value_or(-1), 2);
}
BOOST_AUTO_TEST_CASE(TwoNodesForceSeqNo)
{
addNode(0);
addNode(1);
faces[0]->linkTo(*faces[1]);
advanceClocks(ndn::time::milliseconds(10));
nodes[0]->publishName(userPrefixes[0], 3);
advanceClocks(ndn::time::milliseconds(10), 100);
BOOST_CHECK_EQUAL(nodes[0]->getSeqNo(userPrefixes[0]).value_or(-1), 3);
BOOST_CHECK_EQUAL(nodes[1]->getSeqNo(userPrefixes[0]).value_or(-1), 3);
}
BOOST_AUTO_TEST_CASE(TwoNodesWithMultipleUserNodes)
{
addNode(0);
addNode(1);
faces[0]->linkTo(*faces[1]);
advanceClocks(ndn::time::milliseconds(10));
Name nodeZeroExtraUser("userPrefix0-1");
Name nodeOneExtraUser("userPrefix1-1");
nodes[0]->addUserNode(nodeZeroExtraUser);
nodes[1]->addUserNode(nodeOneExtraUser);
nodes[0]->publishName(userPrefixes[0]);
advanceClocks(ndn::time::milliseconds(10), 100);
BOOST_CHECK_EQUAL(nodes[0]->getSeqNo(userPrefixes[0]).value_or(-1), 1);
BOOST_CHECK_EQUAL(nodes[1]->getSeqNo(userPrefixes[0]).value_or(-1), 1);
nodes[0]->publishName(nodeZeroExtraUser);
advanceClocks(ndn::time::milliseconds(10), 100);
BOOST_CHECK_EQUAL(nodes[0]->getSeqNo(nodeZeroExtraUser).value_or(-1), 1);
BOOST_CHECK_EQUAL(nodes[1]->getSeqNo(nodeZeroExtraUser).value_or(-1), 1);
nodes[1]->publishName(nodeOneExtraUser);
advanceClocks(ndn::time::milliseconds(10), 100);
BOOST_CHECK_EQUAL(nodes[0]->getSeqNo(nodeOneExtraUser).value_or(-1), 1);
BOOST_CHECK_EQUAL(nodes[1]->getSeqNo(nodeOneExtraUser).value_or(-1), 1);
}
BOOST_AUTO_TEST_CASE(MultipleNodes)
{
for (int i = 0; i < 4; i++) {
addNode(i);
}
for (int i = 0; i < 3; i++) {
faces[i]->linkTo(*faces[i + 1]);
}
nodes[0]->publishName(userPrefixes[0]);
advanceClocks(ndn::time::milliseconds(10), 100);
for (int i = 0; i < 4; i++) {
BOOST_CHECK_EQUAL(nodes[i]->getSeqNo(userPrefixes[0]).value_or(-1), 1);
}
nodes[1]->publishName(userPrefixes[1]);
advanceClocks(ndn::time::milliseconds(10), 100);
for (int i = 0; i < 4; i++) {
BOOST_CHECK_EQUAL(nodes[i]->getSeqNo(userPrefixes[1]).value_or(-1), 1);
}
nodes[1]->publishName(userPrefixes[1]);
advanceClocks(ndn::time::milliseconds(10), 100);
for (int i = 0; i < 4; i++) {
BOOST_CHECK_EQUAL(nodes[i]->getSeqNo(userPrefixes[1]).value_or(-1), 2);
}
}
BOOST_AUTO_TEST_CASE(MultipleNodesSimultaneousPublish)
{
for (int i = 0; i < 4; i++) {
addNode(i);
}
for (int i = 0; i < 3; i++) {
faces[i]->linkTo(*faces[i + 1]);
}
for (int i = 0; i < 4; i++) {
nodes[i]->publishName(userPrefixes[i]);
}
advanceClocks(ndn::time::milliseconds(10), 100);
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
BOOST_CHECK_EQUAL(nodes[i]->getSeqNo(userPrefixes[j]).value_or(-1), 1);
}
}
for (int i = 0; i < 4; i++) {
nodes[i]->publishName(userPrefixes[i], 4);
}
advanceClocks(ndn::time::milliseconds(10), 100);
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
BOOST_CHECK_EQUAL(nodes[i]->getSeqNo(userPrefixes[j]).value_or(-1), 4);
}
}
}
BOOST_AUTO_TEST_CASE(NetworkPartition)
{
for (int i = 0; i < 4; i++) {
addNode(i);
}
for (int i = 0; i < 3; i++) {
faces[i]->linkTo(*faces[i + 1]);
}
nodes[0]->publishName(userPrefixes[0]);
advanceClocks(ndn::time::milliseconds(10), 100);
for (int i = 0; i < 4; i++) {
BOOST_CHECK_EQUAL(nodes[i]->getSeqNo(userPrefixes[0]).value_or(-1), 1);
}
for (int i = 0; i < 3; i++) {
faces[i]->unlink();
}
faces[0]->linkTo(*faces[1]);
faces[2]->linkTo(*faces[3]);
nodes[0]->publishName(userPrefixes[0]);
advanceClocks(ndn::time::milliseconds(10), 100);
BOOST_CHECK_EQUAL(nodes[1]->getSeqNo(userPrefixes[0]).value_or(-1), 2);
BOOST_CHECK_EQUAL(nodes[2]->getSeqNo(userPrefixes[0]).value_or(-1), 1);
BOOST_CHECK_EQUAL(nodes[3]->getSeqNo(userPrefixes[0]).value_or(-1), 1);
nodes[1]->publishName(userPrefixes[1], 2);
advanceClocks(ndn::time::milliseconds(10), 100);
BOOST_CHECK_EQUAL(nodes[0]->getSeqNo(userPrefixes[1]).value_or(-1), 2);
nodes[2]->publishName(userPrefixes[2], 2);
advanceClocks(ndn::time::milliseconds(10), 100);
BOOST_CHECK_EQUAL(nodes[3]->getSeqNo(userPrefixes[2]).value_or(-1), 2);
nodes[3]->publishName(userPrefixes[3], 2);
advanceClocks(ndn::time::milliseconds(10), 100);
BOOST_CHECK_EQUAL(nodes[2]->getSeqNo(userPrefixes[3]).value_or(-1), 2);
BOOST_CHECK_EQUAL(nodes[0]->getSeqNo(userPrefixes[3]).value_or(-1), -1);
BOOST_CHECK_EQUAL(nodes[1]->getSeqNo(userPrefixes[3]).value_or(-1), -1);
for (int i = 0; i < 3; i++) {
faces[i]->unlink();
}
for (int i = 0; i < 3; i++) {
faces[i]->linkTo(*faces[i + 1]);
}
advanceClocks(ndn::time::milliseconds(10), 100);
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
BOOST_CHECK_EQUAL(nodes[i]->getSeqNo(userPrefixes[j]).value_or(-1), 2);
}
}
}
BOOST_AUTO_TEST_CASE(IBFOverflow)
{
addNode(0);
addNode(1);
faces[0]->linkTo(*faces[1]);
advanceClocks(ndn::time::milliseconds(10));
// 50 > 40 (expected number of entries in IBF)
for (int i = 0; i < 50; i++) {
nodes[0]->addUserNode(Name("userNode0-" + to_string(i)));
}
for (int i = 0; i < 20; i++) {
// Suppose all sync data were lost for these:
nodes[0]->updateSeqNo(Name("userNode0-" + to_string(i)), 1);
}
nodes[0]->publishName(Name("userNode0-" + to_string(20)));
advanceClocks(ndn::time::milliseconds(10), 100);
for (int i = 0; i <= 20; i++) {
Name userPrefix("userNode0-" + to_string(i));
BOOST_CHECK_EQUAL(nodes[1]->getSeqNo(userPrefix).value_or(-1), 1);
}
for (int i = 21; i < 49; i++) {
nodes[0]->updateSeqNo(Name("userNode0-" + to_string(i)), 1);
}
nodes[0]->publishName(Name("userNode0-49"));
advanceClocks(ndn::time::milliseconds(10), 100);
for (int i = 21; i < 49; i++) {
Name userPrefix("userNode0-" + to_string(i));
BOOST_CHECK_EQUAL(nodes[1]->getSeqNo(userPrefix).value_or(-1), 1);
}
}
BOOST_AUTO_TEST_CASE(DiffIBFDecodeFailureSimple)
{
addNode(0);
addNode(1);
faces[0]->linkTo(*faces[1]);
advanceClocks(ndn::time::milliseconds(10));
// Lowest number that triggers a decode failure for IBF size of 40
int totalUpdates = 47;
for (int i = 0; i <= totalUpdates; i++) {
nodes[0]->addUserNode(Name("userNode0-" + to_string(i)));
if (i != totalUpdates) {
nodes[0]->updateSeqNo(Name("userNode0-" + to_string(i)), 1);
}
}
nodes[0]->publishName(Name("userNode0-" + to_string(totalUpdates)));
advanceClocks(ndn::time::milliseconds(10), 100);
// No mechanism to recover yet
for (int i = 0; i <= totalUpdates; i++) {
Name userPrefix("userNode0-" + to_string(i));
BOOST_CHECK_EQUAL(nodes[1]->getSeqNo(userPrefix).value_or(-1), 1);
}
BOOST_CHECK_EQUAL(nodes[0]->getSeqNo(userPrefixes[1]).value_or(-1), -1);
BOOST_CHECK_EQUAL(nodes[1]->getSeqNo(userPrefixes[0]).value_or(-1), -1);
nodes[1]->publishName(userPrefixes[1]);
advanceClocks(ndn::time::milliseconds(10), 100);
BOOST_CHECK_EQUAL(nodes[0]->getSeqNo(userPrefixes[1]).value_or(-1), 1);
nodes[0]->publishName(userPrefixes[0]);
advanceClocks(ndn::time::milliseconds(10), 100);
BOOST_CHECK_EQUAL(nodes[1]->getSeqNo(userPrefixes[0]).value_or(-1), 1);
}
BOOST_AUTO_TEST_CASE(DiffIBFDecodeFailureSimpleSegmentedRecovery)
{
addNode(0);
addNode(1);
faces[0]->linkTo(*faces[1]);
advanceClocks(ndn::time::milliseconds(10));
// Lowest number that triggers a decode failure for IBF size of 40
int totalUpdates = 270;
for (int i = 0; i <= totalUpdates; i++) {
nodes[0]->addUserNode(Name("userNode0-" + to_string(i)));
if (i != totalUpdates) {
nodes[0]->updateSeqNo(Name("userNode0-" + to_string(i)), 1);
}
}
nodes[0]->publishName(Name("userNode0-" + to_string(totalUpdates)));
advanceClocks(ndn::time::milliseconds(10), 100);
// No mechanism to recover yet
for (int i = 0; i <= totalUpdates; i++) {
Name userPrefix("userNode0-" + to_string(i));
BOOST_CHECK_EQUAL(nodes[1]->getSeqNo(userPrefix).value_or(-1), 1);
}
BOOST_CHECK_EQUAL(nodes[0]->getSeqNo(userPrefixes[1]).value_or(-1), -1);
BOOST_CHECK_EQUAL(nodes[1]->getSeqNo(userPrefixes[0]).value_or(-1), -1);
nodes[1]->publishName(userPrefixes[1]);
advanceClocks(ndn::time::milliseconds(10), 100);
BOOST_CHECK_EQUAL(nodes[0]->getSeqNo(userPrefixes[1]).value_or(-1), 1);
nodes[0]->publishName(userPrefixes[0]);
advanceClocks(ndn::time::milliseconds(10), 100);
BOOST_CHECK_EQUAL(nodes[1]->getSeqNo(userPrefixes[0]).value_or(-1), 1);
}
BOOST_AUTO_TEST_CASE(DiffIBFDecodeFailureMultipleNodes)
{
for (int i = 0; i < 4; i++) {
addNode(i);
}
for (int i = 0; i < 3; i++) {
faces[i]->linkTo(*faces[i + 1]);
}
// Lowest number that triggers a decode failure for IBF size of 40
int totalUpdates = 47;
for (int i = 0; i <= totalUpdates; i++) {
nodes[0]->addUserNode(Name("userNode0-" + to_string(i)));
if (i != totalUpdates) {
nodes[0]->updateSeqNo(Name("userNode0-" + to_string(i)), 1);
}
}
nodes[0]->publishName(Name("userNode0-" + to_string(totalUpdates)));
advanceClocks(ndn::time::milliseconds(10), 100);
for (int i = 0; i <= totalUpdates; i++) {
Name userPrefix("userNode0-" + to_string(i));
for (int j = 0; j < 4; j++) {
BOOST_CHECK_EQUAL(nodes[j]->getSeqNo(userPrefix).value_or(-1), 1);
}
}
}
BOOST_AUTO_TEST_CASE(DelayedSecondSegment)
{
addNode(0);
int i = 0;
State state;
std::shared_ptr<Buffer> compressed;
do {
Name prefixToPublish("userNode0-" + to_string(i++));
nodes[0]->addUserNode(prefixToPublish);
nodes[0]->publishName(prefixToPublish);
state.addContent(Name(prefixToPublish).appendNumber(nodes[0]->m_prefixes[prefixToPublish]));
auto block = state.wireEncode();
compressed = compress(nodes[0]->m_contentCompression, block.wire(), block.size());
} while (compressed->size() < (MAX_NDN_PACKET_SIZE >> 1));
advanceClocks(ndn::time::milliseconds(10), 100);
Name syncInterestName(syncPrefix);
IBLT iblt(40, nodes[0]->m_ibltCompression);
iblt.appendToName(syncInterestName);
nodes[0]->onSyncInterest(syncPrefix, Interest(syncInterestName));
advanceClocks(ndn::time::milliseconds(10));
BOOST_CHECK_EQUAL(nodes[0]->m_segmentPublisher.m_ims.size(), 2);
// Expire contents from segmentPublisher
advanceClocks(ndn::time::milliseconds(10), 100);
BOOST_CHECK_EQUAL(nodes[0]->m_segmentPublisher.m_ims.size(), 0);
// Get data name from face and increase segment number to form next interest
BOOST_REQUIRE(!faces[0]->sentData.empty());
Name dataName = faces[0]->sentData.front().getName();
Name interestName = dataName.getSubName(0, dataName.size() - 2);
interestName.appendSegment(1);
faces[0]->sentData.clear();
nodes[0]->onSyncInterest(syncPrefix, Interest(interestName));
advanceClocks(ndn::time::milliseconds(10));
// Should have repopulated SegmentPublisher
BOOST_CHECK_EQUAL(nodes[0]->m_segmentPublisher.m_ims.size(), 2);
// Should have received the second data segment this time
BOOST_REQUIRE(!faces[0]->sentData.empty());
BOOST_CHECK_EQUAL(faces[0]->sentData.front().getName().at(-1).toSegment(), 1);
}
BOOST_AUTO_TEST_SUITE_END()
} // namespace psync