/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ /** * Copyright (c) 2014-2016, 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 . */ #include "table/name-tree.hpp" #include "tests/test-common.hpp" namespace nfd { namespace name_tree { namespace tests { using namespace nfd::tests; BOOST_AUTO_TEST_SUITE(Table) BOOST_FIXTURE_TEST_SUITE(TestNameTree, BaseFixture) BOOST_AUTO_TEST_CASE(ComputeHash) { Name root("/"); root.wireEncode(); HashValue hashValue = computeHash(root); BOOST_CHECK_EQUAL(hashValue, 0); Name prefix("/nohello/world/ndn/research"); prefix.wireEncode(); HashSequence hashes = computeHashes(prefix); BOOST_CHECK_EQUAL(hashes.size(), prefix.size() + 1); } BOOST_AUTO_TEST_SUITE(Hashtable) using name_tree::Hashtable; BOOST_AUTO_TEST_CASE(Modifiers) { Hashtable ht(HashtableOptions(16)); Name name("/A/B/C/D"); HashSequence hashes = computeHashes(name); BOOST_CHECK_EQUAL(ht.size(), 0); BOOST_CHECK(ht.find(name, 2) == nullptr); const Node* node = nullptr; bool isNew = false; std::tie(node, isNew) = ht.insert(name, 2, hashes); BOOST_CHECK_EQUAL(isNew, true); BOOST_CHECK(node != nullptr); BOOST_CHECK_EQUAL(ht.size(), 1); BOOST_CHECK_EQUAL(ht.find(name, 2), node); BOOST_CHECK_EQUAL(ht.find(name, 2, hashes), node); BOOST_CHECK(ht.find(name, 0) == nullptr); BOOST_CHECK(ht.find(name, 1) == nullptr); BOOST_CHECK(ht.find(name, 3) == nullptr); BOOST_CHECK(ht.find(name, 4) == nullptr); const Node* node2 = nullptr; std::tie(node2, isNew) = ht.insert(name, 2, hashes); BOOST_CHECK_EQUAL(isNew, false); BOOST_CHECK_EQUAL(node2, node); BOOST_CHECK_EQUAL(ht.size(), 1); std::tie(node2, isNew) = ht.insert(name, 4, hashes); BOOST_CHECK_EQUAL(isNew, true); BOOST_CHECK(node2 != nullptr); BOOST_CHECK_NE(node2, node); BOOST_CHECK_EQUAL(ht.size(), 2); ht.erase(const_cast(node2)); BOOST_CHECK_EQUAL(ht.size(), 1); BOOST_CHECK(ht.find(name, 4) == nullptr); BOOST_CHECK_EQUAL(ht.find(name, 2), node); ht.erase(const_cast(node)); BOOST_CHECK_EQUAL(ht.size(), 0); BOOST_CHECK(ht.find(name, 2) == nullptr); BOOST_CHECK(ht.find(name, 4) == nullptr); } BOOST_AUTO_TEST_CASE(Resize) { HashtableOptions options(9); BOOST_CHECK_EQUAL(options.initialSize, 9); BOOST_CHECK_EQUAL(options.minSize, 9); options.minSize = 6; options.expandLoadFactor = 0.80; options.expandFactor = 5.0; options.shrinkLoadFactor = 0.12; options.shrinkFactor = 0.3; Hashtable ht(options); auto addNodes = [&ht] (int min, int max) { for (int i = min; i <= max; ++i) { Name name; name.appendNumber(i); HashSequence hashes = computeHashes(name); ht.insert(name, name.size(), hashes); } }; auto removeNodes = [&ht] (int min, int max) { for (int i = min; i <= max; ++i) { Name name; name.appendNumber(i); const Node* node = ht.find(name, name.size()); BOOST_REQUIRE(node != nullptr); ht.erase(const_cast(node)); } }; BOOST_CHECK_EQUAL(ht.size(), 0); BOOST_CHECK_EQUAL(ht.getNBuckets(), 9); addNodes(1, 1); BOOST_CHECK_EQUAL(ht.size(), 1); BOOST_CHECK_EQUAL(ht.getNBuckets(), 9); removeNodes(1, 1); BOOST_CHECK_EQUAL(ht.size(), 0); BOOST_CHECK_EQUAL(ht.getNBuckets(), 6); addNodes(1, 4); BOOST_CHECK_EQUAL(ht.size(), 4); BOOST_CHECK_EQUAL(ht.getNBuckets(), 6); addNodes(5, 5); BOOST_CHECK_EQUAL(ht.size(), 5); BOOST_CHECK_EQUAL(ht.getNBuckets(), 30); addNodes(6, 23); BOOST_CHECK_EQUAL(ht.size(), 23); BOOST_CHECK_EQUAL(ht.getNBuckets(), 30); addNodes(24, 25); BOOST_CHECK_EQUAL(ht.size(), 25); BOOST_CHECK_EQUAL(ht.getNBuckets(), 150); removeNodes(19, 25); BOOST_CHECK_EQUAL(ht.size(), 18); BOOST_CHECK_EQUAL(ht.getNBuckets(), 150); removeNodes(17, 18); BOOST_CHECK_EQUAL(ht.size(), 16); BOOST_CHECK_EQUAL(ht.getNBuckets(), 45); removeNodes(7, 16); BOOST_CHECK_EQUAL(ht.size(), 6); BOOST_CHECK_EQUAL(ht.getNBuckets(), 45); removeNodes(5, 6); BOOST_CHECK_EQUAL(ht.size(), 4); BOOST_CHECK_EQUAL(ht.getNBuckets(), 13); removeNodes(1, 4); BOOST_CHECK_EQUAL(ht.size(), 0); BOOST_CHECK_EQUAL(ht.getNBuckets(), 6); } BOOST_AUTO_TEST_SUITE_END() // Hashtable BOOST_AUTO_TEST_SUITE(TestEntry) BOOST_AUTO_TEST_CASE(TreeRelation) { Name name("ndn:/named-data/research/abc/def/ghi"); auto node = make_unique(0, name); Entry& npe = node->entry; BOOST_CHECK(npe.getParent() == nullptr); Name parentName = name.getPrefix(-1); auto parentNode = make_unique(1, parentName); Entry& parent = parentNode->entry; BOOST_CHECK_EQUAL(parent.hasChildren(), false); BOOST_CHECK_EQUAL(parent.isEmpty(), true); npe.setParent(parentNode->entry); BOOST_CHECK_EQUAL(npe.getParent(), &parent); BOOST_CHECK_EQUAL(parent.hasChildren(), true); BOOST_CHECK_EQUAL(parent.isEmpty(), false); BOOST_REQUIRE_EQUAL(parent.getChildren().size(), 1); BOOST_CHECK_EQUAL(parent.getChildren().front(), &npe); npe.unsetParent(); BOOST_CHECK(npe.getParent() == nullptr); BOOST_CHECK_EQUAL(parent.hasChildren(), false); BOOST_CHECK_EQUAL(parent.isEmpty(), true); } BOOST_AUTO_TEST_CASE(TableEntries) { Name name("ndn:/named-data/research/abc/def/ghi"); Node node(0, name); Entry& npe = node.entry; BOOST_CHECK_EQUAL(npe.getName(), name); BOOST_CHECK_EQUAL(npe.hasTableEntries(), false); BOOST_CHECK_EQUAL(npe.isEmpty(), true); BOOST_CHECK(npe.getFibEntry() == nullptr); BOOST_CHECK_EQUAL(npe.hasPitEntries(), false); BOOST_CHECK_EQUAL(npe.getPitEntries().empty(), true); BOOST_CHECK(npe.getMeasurementsEntry() == nullptr); BOOST_CHECK(npe.getStrategyChoiceEntry() == nullptr); npe.setFibEntry(make_unique(name)); BOOST_REQUIRE(npe.getFibEntry() != nullptr); BOOST_CHECK_EQUAL(npe.getFibEntry()->getPrefix(), name); BOOST_CHECK_EQUAL(npe.hasTableEntries(), true); BOOST_CHECK_EQUAL(npe.isEmpty(), false); npe.setFibEntry(nullptr); BOOST_CHECK(npe.getFibEntry() == nullptr); BOOST_CHECK_EQUAL(npe.hasTableEntries(), false); BOOST_CHECK_EQUAL(npe.isEmpty(), true); auto pit1 = make_shared(*makeInterest(name)); shared_ptr interest2 = makeInterest(name); interest2->setMinSuffixComponents(2); auto pit2 = make_shared(*interest2); npe.insertPitEntry(pit1); BOOST_CHECK_EQUAL(npe.hasPitEntries(), true); BOOST_CHECK_EQUAL(npe.getPitEntries().size(), 1); BOOST_CHECK_EQUAL(npe.hasTableEntries(), true); BOOST_CHECK_EQUAL(npe.isEmpty(), false); npe.insertPitEntry(pit2); BOOST_CHECK_EQUAL(npe.getPitEntries().size(), 2); pit::Entry* pit1ptr = pit1.get(); weak_ptr pit1weak(pit1); pit1.reset(); BOOST_CHECK_EQUAL(pit1weak.use_count(), 1); // npe is the sole owner of pit1 npe.erasePitEntry(pit1ptr); BOOST_REQUIRE_EQUAL(npe.getPitEntries().size(), 1); BOOST_CHECK_EQUAL(npe.getPitEntries().front()->getInterest(), *interest2); npe.erasePitEntry(pit2.get()); BOOST_CHECK_EQUAL(npe.hasPitEntries(), false); BOOST_CHECK_EQUAL(npe.getPitEntries().size(), 0); BOOST_CHECK_EQUAL(npe.hasTableEntries(), false); BOOST_CHECK_EQUAL(npe.isEmpty(), true); npe.setMeasurementsEntry(make_unique(name)); BOOST_REQUIRE(npe.getMeasurementsEntry() != nullptr); BOOST_CHECK_EQUAL(npe.getMeasurementsEntry()->getName(), name); BOOST_CHECK_EQUAL(npe.hasTableEntries(), true); BOOST_CHECK_EQUAL(npe.isEmpty(), false); npe.setMeasurementsEntry(nullptr); BOOST_CHECK(npe.getMeasurementsEntry() == nullptr); BOOST_CHECK_EQUAL(npe.hasTableEntries(), false); BOOST_CHECK_EQUAL(npe.isEmpty(), true); npe.setStrategyChoiceEntry(make_unique(name)); BOOST_REQUIRE(npe.getStrategyChoiceEntry() != nullptr); BOOST_CHECK_EQUAL(npe.getStrategyChoiceEntry()->getPrefix(), name); BOOST_CHECK_EQUAL(npe.hasTableEntries(), true); BOOST_CHECK_EQUAL(npe.isEmpty(), false); npe.setStrategyChoiceEntry(nullptr); BOOST_CHECK(npe.getStrategyChoiceEntry() == nullptr); BOOST_CHECK_EQUAL(npe.hasTableEntries(), false); BOOST_CHECK_EQUAL(npe.isEmpty(), true); } BOOST_AUTO_TEST_SUITE_END() // TestEntry BOOST_AUTO_TEST_CASE(Basic) { size_t nBuckets = 16; NameTree nt(nBuckets); BOOST_CHECK_EQUAL(nt.size(), 0); BOOST_CHECK_EQUAL(nt.getNBuckets(), nBuckets); // lookup Name nameABC("ndn:/a/b/c"); Entry& npeABC = nt.lookup(nameABC); BOOST_CHECK_EQUAL(nt.size(), 4); Name nameABD("/a/b/d"); Entry& npeABD = nt.lookup(nameABD); BOOST_CHECK_EQUAL(nt.size(), 5); Name nameAE("/a/e/"); Entry& npeAE = nt.lookup(nameAE); BOOST_CHECK_EQUAL(nt.size(), 6); Name nameF("/f"); Entry& npeF = nt.lookup(nameF); BOOST_CHECK_EQUAL(nt.size(), 7); // getParent and findExactMatch Name nameAB("/a/b"); BOOST_CHECK_EQUAL(npeABC.getParent(), nt.findExactMatch(nameAB)); BOOST_CHECK_EQUAL(npeABD.getParent(), nt.findExactMatch(nameAB)); Name nameA("/a"); BOOST_CHECK_EQUAL(npeAE.getParent(), nt.findExactMatch(nameA)); Name nameRoot("/"); BOOST_CHECK_EQUAL(npeF.getParent(), nt.findExactMatch(nameRoot)); BOOST_CHECK_EQUAL(nt.size(), 7); Name name0("/does/not/exist"); Entry* npe0 = nt.findExactMatch(name0); BOOST_CHECK(npe0 == nullptr); // findLongestPrefixMatch Entry* temp = nullptr; Name nameABCLPM("/a/b/c/def/asdf/nlf"); temp = nt.findLongestPrefixMatch(nameABCLPM); BOOST_CHECK_EQUAL(temp, nt.findExactMatch(nameABC)); Name nameABDLPM("/a/b/d/def/asdf/nlf"); temp = nt.findLongestPrefixMatch(nameABDLPM); BOOST_CHECK_EQUAL(temp, nt.findExactMatch(nameABD)); Name nameABLPM("/a/b/hello/world"); temp = nt.findLongestPrefixMatch(nameABLPM); BOOST_CHECK_EQUAL(temp, nt.findExactMatch(nameAB)); Name nameAELPM("/a/e/hello/world"); temp = nt.findLongestPrefixMatch(nameAELPM); BOOST_CHECK_EQUAL(temp, nt.findExactMatch(nameAE)); Name nameALPM("/a/hello/world"); temp = nt.findLongestPrefixMatch(nameALPM); BOOST_CHECK_EQUAL(temp, nt.findExactMatch(nameA)); Name nameFLPM("/f/hello/world"); temp = nt.findLongestPrefixMatch(nameFLPM); BOOST_CHECK_EQUAL(temp, nt.findExactMatch(nameF)); Name nameRootLPM("/does_not_exist"); temp = nt.findLongestPrefixMatch(nameRootLPM); BOOST_CHECK_EQUAL(temp, nt.findExactMatch(nameRoot)); bool eraseResult = false; temp = nt.findExactMatch(nameABC); if (temp != nullptr) eraseResult = nt.eraseIfEmpty(temp); BOOST_CHECK_EQUAL(nt.size(), 6); BOOST_CHECK(nt.findExactMatch(nameABC) == nullptr); BOOST_CHECK_EQUAL(eraseResult, true); eraseResult = false; temp = nt.findExactMatch(nameABCLPM); if (temp != nullptr) eraseResult = nt.eraseIfEmpty(temp); BOOST_CHECK(temp == nullptr); BOOST_CHECK_EQUAL(nt.size(), 6); BOOST_CHECK_EQUAL(eraseResult, false); nt.lookup(nameABC); BOOST_CHECK_EQUAL(nt.size(), 7); eraseResult = false; temp = nt.findExactMatch(nameABC); if (temp != nullptr) eraseResult = nt.eraseIfEmpty(temp); BOOST_CHECK_EQUAL(nt.size(), 6); BOOST_CHECK_EQUAL(eraseResult, true); BOOST_CHECK(nt.findExactMatch(nameABC) == nullptr); BOOST_CHECK_EQUAL(nt.getNBuckets(), 16); // should resize now Name nameABCD("a/b/c/d"); nt.lookup(nameABCD); Name nameABCDE("a/b/c/d/e"); nt.lookup(nameABCDE); BOOST_CHECK_EQUAL(nt.size(), 9); BOOST_CHECK_EQUAL(nt.getNBuckets(), 32); // try to erase /a/b/c, should return false temp = nt.findExactMatch(nameABC); BOOST_CHECK_EQUAL(temp->getName(), nameABC); eraseResult = nt.eraseIfEmpty(temp); BOOST_CHECK_EQUAL(eraseResult, false); temp = nt.findExactMatch(nameABC); BOOST_CHECK_EQUAL(temp->getName(), nameABC); temp = nt.findExactMatch(nameABD); if (temp != nullptr) nt.eraseIfEmpty(temp); BOOST_CHECK_EQUAL(nt.size(), 8); } /** \brief verify a NameTree enumeration contains expected entries * * Example: * \code * auto& enumerable = ...; * EnumerationVerifier(enumerable) * .expect("/A") * .expect("/B") * .end(); * // enumerable must have /A /B and nothing else to pass the test. * \endcode */ class EnumerationVerifier : noncopyable { public: template EnumerationVerifier(Enumerable&& enumerable) { for (const Entry& entry : enumerable) { const Name& name = entry.getName(); BOOST_CHECK_MESSAGE(m_names.insert(name).second, "duplicate Name " << name); } } EnumerationVerifier& expect(const Name& name) { BOOST_CHECK_MESSAGE(m_names.erase(name) == 1, "missing Name " << name); return *this; } void end() { BOOST_CHECK(m_names.empty()); } private: std::unordered_set m_names; }; class EnumerationFixture : public BaseFixture { protected: EnumerationFixture() : nt(N_BUCKETS) { BOOST_CHECK_EQUAL(nt.size(), 0); BOOST_CHECK_EQUAL(nt.getNBuckets(), N_BUCKETS); } void insertAbAc() { nt.lookup("/a/b"); nt.lookup("/a/c"); BOOST_CHECK_EQUAL(nt.size(), 4); // /, /a, /a/b, /a/c } void insertAb1Ab2Ac1Ac2() { nt.lookup("/a/b/1"); nt.lookup("/a/b/2"); nt.lookup("/a/c/1"); nt.lookup("/a/c/2"); BOOST_CHECK_EQUAL(nt.size(), 8); // /, /a, /a/b, /a/b/1, /a/b/2, /a/c, /a/c/1, /a/c/2 } protected: static const size_t N_BUCKETS = 16; NameTree nt; }; const size_t EnumerationFixture::N_BUCKETS; BOOST_FIXTURE_TEST_CASE(IteratorFullEnumerate, EnumerationFixture) { nt.lookup("/a/b/c"); BOOST_CHECK_EQUAL(nt.size(), 4); nt.lookup("/a/b/d"); BOOST_CHECK_EQUAL(nt.size(), 5); nt.lookup("/a/e"); BOOST_CHECK_EQUAL(nt.size(), 6); nt.lookup("/f"); BOOST_CHECK_EQUAL(nt.size(), 7); nt.lookup("/"); BOOST_CHECK_EQUAL(nt.size(), 7); auto&& enumerable = nt.fullEnumerate(); EnumerationVerifier(enumerable) .expect("/") .expect("/a") .expect("/a/b") .expect("/a/b/c") .expect("/a/b/d") .expect("/a/e") .expect("/f") .end(); } BOOST_FIXTURE_TEST_SUITE(IteratorPartialEnumerate, EnumerationFixture) BOOST_AUTO_TEST_CASE(Empty) { auto&& enumerable = nt.partialEnumerate("/a"); EnumerationVerifier(enumerable) .end(); } BOOST_AUTO_TEST_CASE(NotIn) { this->insertAbAc(); // Enumerate on some name that is not in nameTree Name name0("/0"); auto&& enumerable = nt.partialEnumerate("/0"); EnumerationVerifier(enumerable) .end(); } BOOST_AUTO_TEST_CASE(OnlyA) { this->insertAbAc(); // Accept "root" nameA only auto&& enumerable = nt.partialEnumerate("/a", [] (const Entry& entry) { return std::make_pair(entry.getName() == "/a", true); }); EnumerationVerifier(enumerable) .expect("/a") .end(); } BOOST_AUTO_TEST_CASE(ExceptA) { this->insertAbAc(); // Accept anything except "root" nameA auto&& enumerable = nt.partialEnumerate("/a", [] (const Entry& entry) { return std::make_pair(entry.getName() != "/a", true); }); EnumerationVerifier(enumerable) .expect("/a/b") .expect("/a/c") .end(); } BOOST_AUTO_TEST_CASE(NoNameANoSubTreeAB) { this->insertAb1Ab2Ac1Ac2(); // No NameA // No SubTree from NameAB auto&& enumerable = nt.partialEnumerate("/a", [] (const Entry& entry) { return std::make_pair(entry.getName() != "/a", entry.getName() != "/a/b"); }); EnumerationVerifier(enumerable) .expect("/a/b") .expect("/a/c") .expect("/a/c/1") .expect("/a/c/2") .end(); } BOOST_AUTO_TEST_CASE(NoNameANoSubTreeAC) { this->insertAb1Ab2Ac1Ac2(); // No NameA // No SubTree from NameAC auto&& enumerable = nt.partialEnumerate("/a", [] (const Entry& entry) { return std::make_pair(entry.getName() != "/a", entry.getName() != "/a/c"); }); EnumerationVerifier(enumerable) .expect("/a/b") .expect("/a/b/1") .expect("/a/b/2") .expect("/a/c") .end(); } BOOST_AUTO_TEST_CASE(NoSubTreeA) { this->insertAb1Ab2Ac1Ac2(); // No Subtree from NameA auto&& enumerable = nt.partialEnumerate("/a", [] (const Entry& entry) { return std::make_pair(true, entry.getName() != "/a"); }); EnumerationVerifier(enumerable) .expect("/a") .end(); } BOOST_AUTO_TEST_CASE(Example) { // Example // / // /A // /A/B x // /A/B/C // /A/D x // /E // /F nt.lookup("/A"); nt.lookup("/A/B"); nt.lookup("/A/B/C"); nt.lookup("/A/D"); nt.lookup("/E"); nt.lookup("/F"); auto&& enumerable = nt.partialEnumerate("/A", [] (const Entry& entry) { bool visitEntry = false; bool visitChildren = false; Name name = entry.getName(); if (name == "/" || name == "/A/B" || name == "/A/B/C" || name == "/A/D") { visitEntry = true; } if (name == "/" || name == "/A" || name == "/F") { visitChildren = true; } return std::make_pair(visitEntry, visitChildren); }); EnumerationVerifier(enumerable) .expect("/A/B") .expect("/A/D") .end(); } BOOST_AUTO_TEST_SUITE_END() BOOST_FIXTURE_TEST_CASE(IteratorFindAllMatches, EnumerationFixture) { nt.lookup("/a/b/c/d/e/f"); nt.lookup("/a/a/c"); nt.lookup("/a/a/d/1"); nt.lookup("/a/a/d/2"); BOOST_CHECK_EQUAL(nt.size(), 12); auto&& allMatches = nt.findAllMatches("/a/b/c/d/e"); EnumerationVerifier(allMatches) .expect("/") .expect("/a") .expect("/a/b") .expect("/a/b/c") .expect("/a/b/c/d") .expect("/a/b/c/d/e") .end(); } BOOST_AUTO_TEST_CASE(HashTableResizeShrink) { size_t nBuckets = 16; NameTree nameTree(nBuckets); Name prefix("/a/b/c/d/e/f/g/h"); // requires 9 buckets Entry& entry = nameTree.lookup(prefix); BOOST_CHECK_EQUAL(nameTree.size(), 9); BOOST_CHECK_EQUAL(nameTree.getNBuckets(), 32); nameTree.eraseIfEmpty(&entry); BOOST_CHECK_EQUAL(nameTree.size(), 0); BOOST_CHECK_EQUAL(nameTree.getNBuckets(), 16); } // .lookup should not invalidate iterator BOOST_AUTO_TEST_CASE(SurvivedIteratorAfterLookup) { NameTree nt; nt.lookup("/A/B/C"); nt.lookup("/E"); Name nameB("/A/B"); std::set seenNames; for (NameTree::const_iterator it = nt.begin(); it != nt.end(); ++it) { BOOST_CHECK(seenNames.insert(it->getName()).second); if (it->getName() == nameB) { nt.lookup("/D"); } } BOOST_CHECK_EQUAL(seenNames.count("/"), 1); BOOST_CHECK_EQUAL(seenNames.count("/A"), 1); BOOST_CHECK_EQUAL(seenNames.count("/A/B"), 1); BOOST_CHECK_EQUAL(seenNames.count("/A/B/C"), 1); BOOST_CHECK_EQUAL(seenNames.count("/E"), 1); seenNames.erase("/D"); // /D may or may not appear BOOST_CHECK(seenNames.size() == 5); } // .eraseIfEmpty should not invalidate iterator BOOST_AUTO_TEST_CASE(SurvivedIteratorAfterErase) { NameTree nt; nt.lookup("/A/B/C"); nt.lookup("/A/D/E"); nt.lookup("/A/F/G"); nt.lookup("/H"); Name nameD("/A/D"); std::set seenNames; for (NameTree::const_iterator it = nt.begin(); it != nt.end(); ++it) { BOOST_CHECK(seenNames.insert(it->getName()).second); if (it->getName() == nameD) { nt.eraseIfEmpty(nt.findExactMatch("/A/F/G")); // /A/F/G and /A/F are erased } } BOOST_CHECK_EQUAL(seenNames.count("/"), 1); BOOST_CHECK_EQUAL(seenNames.count("/A"), 1); BOOST_CHECK_EQUAL(seenNames.count("/A/B"), 1); BOOST_CHECK_EQUAL(seenNames.count("/A/B/C"), 1); BOOST_CHECK_EQUAL(seenNames.count("/A/D"), 1); BOOST_CHECK_EQUAL(seenNames.count("/A/D/E"), 1); BOOST_CHECK_EQUAL(seenNames.count("/H"), 1); seenNames.erase("/A/F"); // /A/F may or may not appear seenNames.erase("/A/F/G"); // /A/F/G may or may not appear BOOST_CHECK(seenNames.size() == 7); } BOOST_AUTO_TEST_SUITE_END() // TestNameTree BOOST_AUTO_TEST_SUITE_END() // Table } // namespace tests } // namespace name_tree } // namespace nfd