/* -*- 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 . **/ #include "PSync/full-producer.hpp" #include "PSync/consumer.hpp" #include "PSync/detail/state.hpp" #include "PSync/detail/util.hpp" #include "tests/boost-test.hpp" #include "tests/io-fixture.hpp" #include namespace psync { using namespace ndn; class FullSyncFixture : public tests::IoFixture { protected: void addNode(int id) { BOOST_ASSERT(id >= 0 && id <= 3); faces[id] = std::make_shared(m_io, util::DummyClientFace::Options{true, true}); userPrefixes[id] = Name("userPrefix" + to_string(id)); nodes[id] = make_shared(40, *faces[id], syncPrefix, userPrefixes[id], [] (const auto&) {}); } protected: const Name syncPrefix = "/psync"; shared_ptr faces[4]; Name userPrefixes[4]; shared_ptr 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; detail::State state; std::shared_ptr 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 = detail::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); detail::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