blob: 65b128055e3405f0ba6ec82dad921a60b44ac76c [file] [log] [blame]
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
* Copyright (c) 2014-2023, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
* University Pierre & Marie Curie, Sorbonne University,
* Washington University in St. Louis,
* Beijing Institute of Technology,
* The University of Memphis.
*
* This file is part of NFD (Named Data Networking Forwarding Daemon).
* See AUTHORS.md for complete list of NFD authors and contributors.
*
* NFD 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.
*
* NFD 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
* NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
*/
#include "table/fib.hpp"
#include "table/pit.hpp"
#include "table/measurements.hpp"
#include "tests/test-common.hpp"
#include "tests/daemon/global-io-fixture.hpp"
#include "tests/daemon/face/dummy-face.hpp"
#include <ndn-cxx/util/concepts.hpp>
namespace nfd::tests {
using namespace nfd::fib;
NDN_CXX_ASSERT_FORWARD_ITERATOR(Fib::const_iterator);
BOOST_AUTO_TEST_SUITE(Table)
BOOST_FIXTURE_TEST_SUITE(TestFib, GlobalIoFixture)
BOOST_AUTO_TEST_CASE(FibEntry)
{
NameTree nameTree;
Fib fib(nameTree);
Name prefix("/pxWhfFza");
shared_ptr<Face> face1 = make_shared<DummyFace>();
shared_ptr<Face> face2 = make_shared<DummyFace>();
Face* expectedFace = nullptr;
uint64_t expectedCost = 0;
size_t nNewNextHopSignals = 0;
fib.afterNewNextHop.connect(
[&] (const Name& prefix1, const NextHop& nextHop) {
++nNewNextHopSignals;
BOOST_CHECK_EQUAL(prefix1, prefix);
BOOST_CHECK_EQUAL(&nextHop.getFace(), expectedFace);
BOOST_CHECK_EQUAL(nextHop.getCost(), expectedCost);
});
Entry& entry = *fib.insert(prefix).first;
BOOST_CHECK_EQUAL(entry.getPrefix(), prefix);
// []
BOOST_CHECK_EQUAL(entry.getNextHops().size(), 0);
expectedFace = face1.get();
expectedCost = 20;
fib.addOrUpdateNextHop(entry, *face1, 20);
// [(face1,20)]
BOOST_CHECK_EQUAL(entry.getNextHops().size(), 1);
BOOST_CHECK_EQUAL(&entry.getNextHops().begin()->getFace(), face1.get());
BOOST_CHECK_EQUAL(entry.getNextHops().begin()->getCost(), 20);
BOOST_CHECK_EQUAL(nNewNextHopSignals, 1);
fib.addOrUpdateNextHop(entry, *face1, 30);
// [(face1,30)]
BOOST_CHECK_EQUAL(entry.getNextHops().size(), 1);
BOOST_CHECK_EQUAL(&entry.getNextHops().begin()->getFace(), face1.get());
BOOST_CHECK_EQUAL(entry.getNextHops().begin()->getCost(), 30);
BOOST_CHECK_EQUAL(nNewNextHopSignals, 1);
expectedFace = face2.get();
expectedCost = 40;
fib.addOrUpdateNextHop(entry, *face2, 40);
// [(face1,30), (face2,40)]
BOOST_CHECK_EQUAL(entry.getNextHops().size(), 2);
{
auto it = entry.getNextHops().begin();
BOOST_REQUIRE(it != entry.getNextHops().end());
BOOST_CHECK_EQUAL(&it->getFace(), face1.get());
BOOST_CHECK_EQUAL(it->getCost(), 30);
++it;
BOOST_REQUIRE(it != entry.getNextHops().end());
BOOST_CHECK_EQUAL(&it->getFace(), face2.get());
BOOST_CHECK_EQUAL(it->getCost(), 40);
++it;
BOOST_CHECK(it == entry.getNextHops().end());
BOOST_CHECK_EQUAL(nNewNextHopSignals, 2);
}
fib.addOrUpdateNextHop(entry, *face2, 10);
// [(face2,10), (face1,30)]
BOOST_CHECK_EQUAL(entry.getNextHops().size(), 2);
{
auto it = entry.getNextHops().begin();
BOOST_REQUIRE(it != entry.getNextHops().end());
BOOST_CHECK_EQUAL(&it->getFace(), face2.get());
BOOST_CHECK_EQUAL(it->getCost(), 10);
++it;
BOOST_REQUIRE(it != entry.getNextHops().end());
BOOST_CHECK_EQUAL(&it->getFace(), face1.get());
BOOST_CHECK_EQUAL(it->getCost(), 30);
++it;
BOOST_CHECK(it == entry.getNextHops().end());
BOOST_CHECK_EQUAL(nNewNextHopSignals, 2);
}
Fib::RemoveNextHopResult status = fib.removeNextHop(entry, *face1);
// [(face2,10)]
BOOST_CHECK(status == Fib::RemoveNextHopResult::NEXTHOP_REMOVED);
BOOST_CHECK_EQUAL(entry.getNextHops().size(), 1);
BOOST_CHECK_EQUAL(entry.getNextHops().begin()->getFace().getId(), face2->getId());
BOOST_CHECK_EQUAL(entry.getNextHops().begin()->getCost(), 10);
status = fib.removeNextHop(entry, *face1);
// [(face2,10)]
BOOST_CHECK(status == Fib::RemoveNextHopResult::NO_SUCH_NEXTHOP);
BOOST_CHECK_EQUAL(entry.getNextHops().size(), 1);
BOOST_CHECK_EQUAL(entry.getNextHops().begin()->getFace().getId(), face2->getId());
BOOST_CHECK_EQUAL(entry.getNextHops().begin()->getCost(), 10);
status = fib.removeNextHop(entry, *face2);
// []
BOOST_CHECK(status == Fib::RemoveNextHopResult::FIB_ENTRY_REMOVED);
BOOST_CHECK(fib.findExactMatch(prefix) == nullptr);
}
BOOST_AUTO_TEST_CASE(Insert_LongestPrefixMatch)
{
NameTree nameTree;
Fib fib(nameTree);
// []
BOOST_CHECK_EQUAL(fib.size(), 0);
BOOST_CHECK_EQUAL(fib.findLongestPrefixMatch("/A").getPrefix(), "/"); // the empty entry
std::pair<Entry*, bool> insertRes = fib.insert("/");
BOOST_CHECK_EQUAL(insertRes.second, true);
BOOST_REQUIRE(insertRes.first != nullptr);
BOOST_CHECK_EQUAL(insertRes.first->getPrefix(), "/");
// ['/']
BOOST_CHECK_EQUAL(fib.size(), 1);
BOOST_CHECK_EQUAL(fib.findLongestPrefixMatch("/A").getPrefix(), "/");
insertRes = fib.insert("/A");
BOOST_CHECK_EQUAL(insertRes.second, true);
BOOST_REQUIRE(insertRes.first != nullptr);
BOOST_CHECK_EQUAL(insertRes.first->getPrefix(), "/A");
// ['/', '/A']
BOOST_CHECK_EQUAL(fib.size(), 2);
insertRes = fib.insert("/A");
BOOST_CHECK_EQUAL(insertRes.second, false);
BOOST_REQUIRE(insertRes.first != nullptr);
BOOST_CHECK_EQUAL(insertRes.first->getPrefix(), "/A");
// ['/', '/A']
BOOST_CHECK_EQUAL(fib.size(), 2);
BOOST_CHECK_EQUAL(fib.findLongestPrefixMatch("/A").getPrefix(), "/A");
BOOST_CHECK_EQUAL(fib.findLongestPrefixMatch("/A/B/C/D").getPrefix(), "/A");
insertRes = fib.insert("/A/B/C");
BOOST_CHECK_EQUAL(insertRes.second, true);
BOOST_REQUIRE(insertRes.first != nullptr);
BOOST_CHECK_EQUAL(insertRes.first->getPrefix(), "/A/B/C");
// ['/', '/A', '/A/B/C']
BOOST_CHECK_EQUAL(fib.size(), 3);
BOOST_CHECK_EQUAL(fib.findLongestPrefixMatch("/A").getPrefix(), "/A");
BOOST_CHECK_EQUAL(fib.findLongestPrefixMatch("/A/B").getPrefix(), "/A");
BOOST_CHECK_EQUAL(fib.findLongestPrefixMatch("/A/B/C/D").getPrefix(), "/A/B/C");
BOOST_CHECK_EQUAL(fib.findLongestPrefixMatch("/E").getPrefix(), "/");
}
BOOST_AUTO_TEST_CASE(LongestPrefixMatchWithPitEntry)
{
NameTree nameTree;
Fib fib(nameTree);
shared_ptr<Data> dataABC = makeData("/A/B/C");
Name fullNameABC = dataABC->getFullName();
shared_ptr<Data> dataADE = makeData("/A/D/E");
Name fullNameADE = dataADE->getFullName();
fib.insert("/A");
fib.insert(fullNameABC);
Pit pit(nameTree);
shared_ptr<Interest> interestAB = makeInterest("/A/B");
shared_ptr<pit::Entry> pitAB = pit.insert(*interestAB).first;
shared_ptr<Interest> interestABC = makeInterest(fullNameABC);
shared_ptr<pit::Entry> pitABC = pit.insert(*interestABC).first;
shared_ptr<Interest> interestADE = makeInterest(fullNameADE);
shared_ptr<pit::Entry> pitADE = pit.insert(*interestADE).first;
size_t nNameTreeEntriesBefore = nameTree.size();
BOOST_CHECK_EQUAL(fib.findLongestPrefixMatch(*pitAB).getPrefix(), "/A");
BOOST_CHECK_EQUAL(fib.findLongestPrefixMatch(*pitABC).getPrefix(), fullNameABC);
BOOST_CHECK_EQUAL(fib.findLongestPrefixMatch(*pitADE).getPrefix(), "/A");
BOOST_CHECK_EQUAL(nameTree.size(), nNameTreeEntriesBefore);
}
BOOST_AUTO_TEST_CASE(LongestPrefixMatchWithMeasurementsEntry)
{
NameTree nameTree;
Fib fib(nameTree);
fib.insert("/A");
fib.insert("/A/B/C");
Measurements measurements(nameTree);
measurements::Entry& mAB = measurements.get("/A/B");
measurements::Entry& mABCD = measurements.get("/A/B/C/D");
BOOST_CHECK_EQUAL(fib.findLongestPrefixMatch(mAB).getPrefix(), "/A");
BOOST_CHECK_EQUAL(fib.findLongestPrefixMatch(mABCD).getPrefix(), "/A/B/C");
}
void
validateFindExactMatch(Fib& fib, const Name& target)
{
const Entry* entry = fib.findExactMatch(target);
BOOST_REQUIRE_MESSAGE(entry != nullptr, "No entry found for " << target);
BOOST_CHECK_EQUAL(entry->getPrefix(), target);
}
void
validateNoExactMatch(Fib& fib, const Name& target)
{
const Entry* entry = fib.findExactMatch(target);
BOOST_CHECK_MESSAGE(entry == nullptr,
"Found unexpected entry for " << target);
}
BOOST_AUTO_TEST_CASE(ExactMatch)
{
NameTree nameTree;
Fib fib(nameTree);
fib.insert("/A");
fib.insert("/A/B");
fib.insert("/A/B/C");
validateFindExactMatch(fib, "/A");
validateFindExactMatch(fib, "/A/B");
validateFindExactMatch(fib, "/A/B/C");
validateNoExactMatch(fib, "/");
validateNoExactMatch(fib, "/does/not/exist");
}
BOOST_AUTO_TEST_CASE(ExactMatchGap)
{
NameTree nameTree;
Fib fib(nameTree);
fib.insert("/X");
fib.insert("/X/Y/Z");
validateNoExactMatch(fib, "/X/Y");
}
BOOST_AUTO_TEST_CASE(ExactMatchEmpty)
{
NameTree nameTree;
Fib fib(nameTree);
validateNoExactMatch(fib, "/");
validateNoExactMatch(fib, "/nothing/here");
}
void
validateErase(Fib& fib, const Name& target)
{
fib.erase(target);
const Entry* entry = fib.findExactMatch(target);
BOOST_CHECK_MESSAGE(entry == nullptr, "Found \"removed\" entry for " << target);
}
BOOST_AUTO_TEST_CASE(Erase)
{
NameTree nameTree;
Fib fib(nameTree);
fib.insert("/");
fib.insert("/A");
fib.insert("/A/B");
fib.insert("/A/B/C");
// check if we remove the right thing and leave
// everything else alone
validateErase(fib, "/A/B");
validateFindExactMatch(fib, "/A");
validateFindExactMatch(fib, "/A/B/C");
validateFindExactMatch(fib, "/");
validateErase(fib, "/A/B/C");
validateFindExactMatch(fib, "/A");
validateFindExactMatch(fib, "/");
validateErase(fib, "/A");
validateFindExactMatch(fib, "/");
validateErase(fib, "/");
validateNoExactMatch(fib, "/");
}
BOOST_AUTO_TEST_CASE(EraseGap)
{
NameTree nameTree;
Fib fib(nameTree);
fib.insert("/X");
fib.insert("/X/Y/Z");
fib.erase("/X/Y"); //should do nothing
validateFindExactMatch(fib, "/X");
validateFindExactMatch(fib, "/X/Y/Z");
}
BOOST_AUTO_TEST_CASE(EraseEmpty)
{
NameTree nameTree;
Fib fib(nameTree);
BOOST_CHECK_NO_THROW(fib.erase("/does/not/exist"));
validateErase(fib, "/");
BOOST_CHECK_NO_THROW(fib.erase("/still/does/not/exist"));
}
BOOST_AUTO_TEST_CASE(EraseNameTreeEntry)
{
NameTree nameTree;
Fib fib(nameTree);
size_t nNameTreeEntriesBefore = nameTree.size();
fib.insert("/A/B/C");
fib.erase("/A/B/C");
BOOST_CHECK_EQUAL(nameTree.size(), nNameTreeEntriesBefore);
}
BOOST_AUTO_TEST_CASE(Iterator)
{
NameTree nameTree;
Fib fib(nameTree);
Name nameA("/A");
Name nameAB("/A/B");
Name nameABC("/A/B/C");
Name nameRoot("/");
fib.insert(nameA);
fib.insert(nameAB);
fib.insert(nameABC);
fib.insert(nameRoot);
std::set<Name> expected{nameA, nameAB, nameABC, nameRoot};
for (Fib::const_iterator it = fib.begin(); it != fib.end(); it++) {
bool isInSet = expected.find(it->getPrefix()) != expected.end();
BOOST_CHECK(isInSet);
expected.erase(it->getPrefix());
}
BOOST_CHECK_EQUAL(expected.size(), 0);
}
BOOST_AUTO_TEST_SUITE_END() // TestFib
BOOST_AUTO_TEST_SUITE_END() // Table
} // namespace nfd::tests