fw: abstract Nack processing out of BestRouteStrategy2
refs #3176
Change-Id: Ib220269ff52acc47c65f854a610941afd862ad41
diff --git a/tests/daemon/fw/best-route-strategy2.t.cpp b/tests/daemon/fw/best-route-strategy2.t.cpp
index fcd7b9f..69328ff 100644
--- a/tests/daemon/fw/best-route-strategy2.t.cpp
+++ b/tests/daemon/fw/best-route-strategy2.t.cpp
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
- * Copyright (c) 2014-2016, Regents of the University of California,
+ * Copyright (c) 2014-2017, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
* University Pierre & Marie Curie, Sorbonne University,
@@ -220,242 +220,6 @@
BOOST_AUTO_TEST_SUITE_END() // NoRouteNack
-BOOST_AUTO_TEST_SUITE(IncomingNack)
-
-BOOST_AUTO_TEST_CASE(OneUpstream) // one upstream, send Nack when Nack arrives
-{
- fib::Entry& fibEntry = *fib.insert(Name()).first;
- fibEntry.addNextHop(*face3, 10);
- fibEntry.addNextHop(*face4, 20);
- fibEntry.addNextHop(*face5, 30);
-
- shared_ptr<Interest> interest1 = makeInterest("/McQYjMbm", 992);
- shared_ptr<Interest> interest2 = makeInterest("/McQYjMbm", 114);
- shared_ptr<pit::Entry> pitEntry = pit.insert(*interest1).first;
- pitEntry->insertOrUpdateInRecord(*face1, *interest1);
- pitEntry->insertOrUpdateInRecord(*face2, *interest2);
- pitEntry->insertOrUpdateOutRecord(*face3, *interest1);
-
- lp::Nack nack3 = makeNack("/McQYjMbm", 992, lp::NackReason::CONGESTION);
- pitEntry->getOutRecord(*face3)->setIncomingNack(nack3);
- strategy.afterReceiveNack(*face3, nack3, pitEntry);
-
- BOOST_REQUIRE_EQUAL(strategy.sendNackHistory.size(), 2);
- BOOST_CHECK_EQUAL(strategy.sendNackHistory[0].pitInterest, pitEntry->getInterest());
- BOOST_CHECK_EQUAL(strategy.sendNackHistory[0].header.getReason(), lp::NackReason::CONGESTION);
- BOOST_CHECK_EQUAL(strategy.sendNackHistory[1].pitInterest, pitEntry->getInterest());
- BOOST_CHECK_EQUAL(strategy.sendNackHistory[1].header.getReason(), lp::NackReason::CONGESTION);
- std::unordered_set<FaceId> nackFaceIds{strategy.sendNackHistory[0].outFaceId,
- strategy.sendNackHistory[1].outFaceId};
- std::unordered_set<FaceId> expectedNackFaceIds{face1->getId(), face2->getId()};
- BOOST_CHECK_EQUAL_COLLECTIONS(nackFaceIds.begin(), nackFaceIds.end(),
- expectedNackFaceIds.begin(), expectedNackFaceIds.end());
-}
-
-BOOST_AUTO_TEST_CASE(TwoUpstreams) // two upstreams, send Nack when both Nacks arrive
-{
- fib::Entry& fibEntry = *fib.insert(Name()).first;
- fibEntry.addNextHop(*face3, 10);
- fibEntry.addNextHop(*face4, 20);
- fibEntry.addNextHop(*face5, 30);
-
- shared_ptr<Interest> interest1 = makeInterest("/aS9FAyUV19", 286);
- shared_ptr<pit::Entry> pitEntry = pit.insert(*interest1).first;
- pitEntry->insertOrUpdateInRecord(*face1, *interest1);
- pitEntry->insertOrUpdateOutRecord(*face3, *interest1);
- pitEntry->insertOrUpdateOutRecord(*face4, *interest1);
-
- lp::Nack nack3 = makeNack("/aS9FAyUV19", 286, lp::NackReason::CONGESTION);
- pitEntry->getOutRecord(*face3)->setIncomingNack(nack3);
- strategy.afterReceiveNack(*face3, nack3, pitEntry);
-
- BOOST_CHECK_EQUAL(strategy.sendNackHistory.size(), 0); // don't send Nack until all upstreams have Nacked
-
- lp::Nack nack4 = makeNack("/aS9FAyUV19", 286, lp::NackReason::CONGESTION);
- pitEntry->getOutRecord(*face4)->setIncomingNack(nack4);
- strategy.afterReceiveNack(*face4, nack4, pitEntry);
-
- BOOST_REQUIRE_EQUAL(strategy.sendNackHistory.size(), 1);
- BOOST_CHECK_EQUAL(strategy.sendNackHistory[0].pitInterest, pitEntry->getInterest());
- BOOST_CHECK_EQUAL(strategy.sendNackHistory[0].outFaceId, face1->getId());
- BOOST_CHECK_EQUAL(strategy.sendNackHistory[0].header.getReason(), lp::NackReason::CONGESTION);
-}
-
-BOOST_AUTO_TEST_CASE(Timeout) // two upstreams, one times out, don't send Nack
-{
- fib::Entry& fibEntry = *fib.insert(Name()).first;
- fibEntry.addNextHop(*face3, 10);
- fibEntry.addNextHop(*face4, 20);
- fibEntry.addNextHop(*face5, 30);
-
- shared_ptr<Interest> interest1 = makeInterest("/sIYw0TXWDj", 115);
- interest1->setInterestLifetime(time::milliseconds(400));
- shared_ptr<pit::Entry> pitEntry = pit.insert(*interest1).first;
- pitEntry->insertOrUpdateInRecord(*face1, *interest1);
- pitEntry->insertOrUpdateOutRecord(*face3, *interest1);
-
- this->advanceClocks(time::milliseconds(300));
- shared_ptr<Interest> interest2 = makeInterest("/sIYw0TXWDj", 223);
- pitEntry->insertOrUpdateInRecord(*face1, *interest2);
- pitEntry->insertOrUpdateOutRecord(*face4, *interest2);
-
- this->advanceClocks(time::milliseconds(200)); // face3 has timed out
-
- lp::Nack nack4 = makeNack("/sIYw0TXWDj", 223, lp::NackReason::CONGESTION);
- pitEntry->getOutRecord(*face4)->setIncomingNack(nack4);
- strategy.afterReceiveNack(*face4, nack4, pitEntry);
-
- BOOST_CHECK_EQUAL(strategy.sendNackHistory.size(), 0);
-}
-
-BOOST_FIXTURE_TEST_CASE(LiveDeadlock, UnitTestTimeFixture) // #3033 note-7
-{
- /*
- * /----------\
- * | producer |
- * \----------/
- * |
- * +---+
- * | P |
- * +---+
- * |
- * failed link
- * |
- * +---+
- * | R |
- * +---+
- * ^ ^
- * / \
- * / \
- * +---+ +---+
- * | B | <---> | C |
- * +---+ +---+
- * ^ ^
- * | |
- * | |
- * +---+ +---+
- * | A | | D |
- * +---+ +---+
- * ^ ^
- * | |
- * /----------\ /----------\
- * | consumer | | consumer |
- * \----------/ \----------/
- */
-
- TopologyTester topo;
- TopologyNode nodeP = topo.addForwarder("P"),
- nodeR = topo.addForwarder("R"),
- nodeA = topo.addForwarder("A"),
- nodeB = topo.addForwarder("B"),
- nodeC = topo.addForwarder("C"),
- nodeD = topo.addForwarder("D");
-
- for (TopologyNode node : {nodeP, nodeR, nodeA, nodeB, nodeC, nodeD}) {
- topo.setStrategy<BestRouteStrategy2>(node);
- }
-
- const time::milliseconds LINK_DELAY(10);
- shared_ptr<TopologyLink> linkPR = topo.addLink("PR", LINK_DELAY, {nodeP, nodeR}),
- linkRB = topo.addLink("RB", LINK_DELAY, {nodeR, nodeB}),
- linkRC = topo.addLink("RC", LINK_DELAY, {nodeR, nodeC}),
- linkBC = topo.addLink("BC", LINK_DELAY, {nodeB, nodeC}),
- linkBA = topo.addLink("BA", LINK_DELAY, {nodeB, nodeA}),
- linkCD = topo.addLink("CD", LINK_DELAY, {nodeC, nodeD});
-
- // TODO register the prefix on R->P but then set the face DOWN
- // topo.registerPrefix(nodeR, linkPR->getFace(nodeR), "ndn:/P", 10);
- topo.registerPrefix(nodeB, linkRB->getFace(nodeB), "ndn:/P", 20);
- topo.registerPrefix(nodeB, linkBC->getFace(nodeB), "ndn:/P", 30);
- topo.registerPrefix(nodeC, linkRC->getFace(nodeC), "ndn:/P", 20);
- topo.registerPrefix(nodeC, linkBC->getFace(nodeC), "ndn:/P", 30);
- topo.registerPrefix(nodeA, linkBA->getFace(nodeA), "ndn:/P", 30);
- topo.registerPrefix(nodeD, linkCD->getFace(nodeD), "ndn:/P", 30);
-
- ndn::Face& appA = topo.addAppFace("A", nodeA)->getClientFace();
- ndn::Face& appD = topo.addAppFace("D", nodeD)->getClientFace();
-
- int nNacksA = 0, nNacksD = 0;
- appA.expressInterest(Interest("/P/1"), bind([]{}), bind([&nNacksA]{ ++nNacksA; }), bind([]{}));
- appD.expressInterest(Interest("/P/1"), bind([]{}), bind([&nNacksD]{ ++nNacksD; }), bind([]{}));
- this->advanceClocks(time::milliseconds(1), time::milliseconds(5));
- appA.expressInterest(Interest("/P/1"), bind([]{}), bind([&nNacksA]{ ++nNacksA; }), bind([]{}));
- appD.expressInterest(Interest("/P/1"), bind([]{}), bind([&nNacksD]{ ++nNacksD; }), bind([]{}));
- this->advanceClocks(time::milliseconds(1), time::milliseconds(100));
-
- // As long as at least one Nack arrives at each client, strategy behavior is correct.
- // Whether both Interests are Nacked is a client face behavior, not strategy behavior.
- BOOST_CHECK_GT(nNacksA, 0);
- BOOST_CHECK_GT(nNacksD, 0);
-}
-
-template<lp::NackReason X, lp::NackReason Y, lp::NackReason R>
-struct NackReasonCombination
-{
- lp::NackReason
- getX() const
- {
- return X;
- }
-
- lp::NackReason
- getY() const
- {
- return Y;
- }
-
- lp::NackReason
- getExpectedResult() const
- {
- return R;
- }
-};
-
-typedef boost::mpl::vector<
- NackReasonCombination<lp::NackReason::CONGESTION, lp::NackReason::CONGESTION, lp::NackReason::CONGESTION>,
- NackReasonCombination<lp::NackReason::CONGESTION, lp::NackReason::DUPLICATE, lp::NackReason::CONGESTION>,
- NackReasonCombination<lp::NackReason::CONGESTION, lp::NackReason::NO_ROUTE, lp::NackReason::CONGESTION>,
- NackReasonCombination<lp::NackReason::DUPLICATE, lp::NackReason::CONGESTION, lp::NackReason::CONGESTION>,
- NackReasonCombination<lp::NackReason::DUPLICATE, lp::NackReason::DUPLICATE, lp::NackReason::DUPLICATE>,
- NackReasonCombination<lp::NackReason::DUPLICATE, lp::NackReason::NO_ROUTE, lp::NackReason::DUPLICATE>,
- NackReasonCombination<lp::NackReason::NO_ROUTE, lp::NackReason::CONGESTION, lp::NackReason::CONGESTION>,
- NackReasonCombination<lp::NackReason::NO_ROUTE, lp::NackReason::DUPLICATE, lp::NackReason::DUPLICATE>,
- NackReasonCombination<lp::NackReason::NO_ROUTE, lp::NackReason::NO_ROUTE, lp::NackReason::NO_ROUTE>
- > NackReasonCombinations;
-
-BOOST_AUTO_TEST_CASE_TEMPLATE(CombineReasons, Combination, NackReasonCombinations)
-{
- Combination combination;
-
- fib::Entry& fibEntry = *fib.insert(Name()).first;
- fibEntry.addNextHop(*face3, 10);
- fibEntry.addNextHop(*face4, 20);
- fibEntry.addNextHop(*face5, 30);
-
- shared_ptr<Interest> interest1 = makeInterest("/F6sEwB24I", 282);
- shared_ptr<pit::Entry> pitEntry = pit.insert(*interest1).first;
- pitEntry->insertOrUpdateInRecord(*face1, *interest1);
- pitEntry->insertOrUpdateOutRecord(*face3, *interest1);
- pitEntry->insertOrUpdateOutRecord(*face4, *interest1);
-
- lp::Nack nack3 = makeNack("/F6sEwB24I", 282, combination.getX());
- pitEntry->getOutRecord(*face3)->setIncomingNack(nack3);
- strategy.afterReceiveNack(*face3, nack3, pitEntry);
-
- BOOST_CHECK_EQUAL(strategy.sendNackHistory.size(), 0);
-
- lp::Nack nack4 = makeNack("/F6sEwB24I", 282, combination.getY());
- pitEntry->getOutRecord(*face4)->setIncomingNack(nack4);
- strategy.afterReceiveNack(*face4, nack4, pitEntry);
-
- BOOST_REQUIRE_EQUAL(strategy.sendNackHistory.size(), 1);
- BOOST_CHECK_EQUAL(strategy.sendNackHistory[0].pitInterest, pitEntry->getInterest());
- BOOST_CHECK_EQUAL(strategy.sendNackHistory[0].outFaceId, face1->getId());
- BOOST_CHECK_EQUAL(strategy.sendNackHistory[0].header.getReason(), combination.getExpectedResult());
-}
-
-BOOST_AUTO_TEST_SUITE_END() // IncomingNack
-
BOOST_AUTO_TEST_SUITE_END() // TestBestRouteStrategy2
BOOST_AUTO_TEST_SUITE_END() // Fw
diff --git a/tests/daemon/fw/strategy-nack-return.t.cpp b/tests/daemon/fw/strategy-nack-return.t.cpp
new file mode 100644
index 0000000..89902d3
--- /dev/null
+++ b/tests/daemon/fw/strategy-nack-return.t.cpp
@@ -0,0 +1,349 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014-2017, 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/>.
+ */
+
+/** \file
+ * This test suite tests incoming Nack processing in strategies.
+ */
+
+// Strategies implementing recommended Nack processing procedure, sorted alphabetically.
+#include "fw/best-route-strategy2.hpp"
+
+#include "tests/test-common.hpp"
+#include "tests/limited-io.hpp"
+#include "choose-strategy.hpp"
+#include "strategy-tester.hpp"
+#include "topology-tester.hpp"
+#include "tests/daemon/face/dummy-face.hpp"
+#include <boost/mpl/vector.hpp>
+
+namespace nfd {
+namespace fw {
+namespace tests {
+
+using namespace nfd::tests;
+
+BOOST_AUTO_TEST_SUITE(Fw)
+
+template<typename S>
+class StrategyNackReturnFixture : public UnitTestTimeFixture
+{
+public:
+ StrategyNackReturnFixture()
+ : limitedIo(this)
+ , strategy(choose<StrategyTester<S>>(forwarder))
+ , fib(forwarder.getFib())
+ , pit(forwarder.getPit())
+ , face1(make_shared<DummyFace>())
+ , face2(make_shared<DummyFace>())
+ , face3(make_shared<DummyFace>())
+ , face4(make_shared<DummyFace>())
+ , face5(make_shared<DummyFace>())
+ {
+ forwarder.addFace(face1);
+ forwarder.addFace(face2);
+ forwarder.addFace(face3);
+ forwarder.addFace(face4);
+ forwarder.addFace(face5);
+ }
+
+public:
+ LimitedIo limitedIo;
+
+ Forwarder forwarder;
+ StrategyTester<S>& strategy;
+ Fib& fib;
+ Pit& pit;
+
+ shared_ptr<Face> face1;
+ shared_ptr<Face> face2;
+ shared_ptr<Face> face3;
+ shared_ptr<Face> face4;
+ shared_ptr<Face> face5;
+};
+
+BOOST_AUTO_TEST_SUITE(TestStrategyNackReturn)
+
+using Strategies = boost::mpl::vector<
+ BestRouteStrategy2
+>;
+
+// one upstream, send Nack when Nack arrives
+BOOST_FIXTURE_TEST_CASE_TEMPLATE(OneUpstream,
+ S, Strategies, StrategyNackReturnFixture<S>)
+{
+ fib::Entry& fibEntry = *this->fib.insert(Name()).first;
+ fibEntry.addNextHop(*this->face3, 10);
+ fibEntry.addNextHop(*this->face4, 20);
+ fibEntry.addNextHop(*this->face5, 30);
+
+ shared_ptr<Interest> interest1 = makeInterest("/McQYjMbm", 992);
+ shared_ptr<Interest> interest2 = makeInterest("/McQYjMbm", 114);
+ shared_ptr<pit::Entry> pitEntry = this->pit.insert(*interest1).first;
+ pitEntry->insertOrUpdateInRecord(*this->face1, *interest1);
+ pitEntry->insertOrUpdateInRecord(*this->face2, *interest2);
+ pitEntry->insertOrUpdateOutRecord(*this->face3, *interest1);
+
+ lp::Nack nack3 = makeNack("/McQYjMbm", 992, lp::NackReason::CONGESTION);
+ pitEntry->getOutRecord(*this->face3)->setIncomingNack(nack3);
+
+ BOOST_REQUIRE(this->strategy.waitForAction(
+ [&] { this->strategy.afterReceiveNack(*this->face3, nack3, pitEntry); },
+ this->limitedIo, 2));
+
+ BOOST_REQUIRE_EQUAL(this->strategy.sendNackHistory.size(), 2);
+ BOOST_CHECK_EQUAL(this->strategy.sendNackHistory[0].pitInterest, pitEntry->getInterest());
+ BOOST_CHECK_EQUAL(this->strategy.sendNackHistory[0].header.getReason(), lp::NackReason::CONGESTION);
+ BOOST_CHECK_EQUAL(this->strategy.sendNackHistory[1].pitInterest, pitEntry->getInterest());
+ BOOST_CHECK_EQUAL(this->strategy.sendNackHistory[1].header.getReason(), lp::NackReason::CONGESTION);
+ std::set<FaceId> nackFaceIds{this->strategy.sendNackHistory[0].outFaceId,
+ this->strategy.sendNackHistory[1].outFaceId};
+ std::set<FaceId> expectedNackFaceIds{this->face1->getId(), this->face2->getId()};
+ BOOST_CHECK_EQUAL_COLLECTIONS(nackFaceIds.begin(), nackFaceIds.end(),
+ expectedNackFaceIds.begin(), expectedNackFaceIds.end());
+}
+
+// two upstreams, send Nack when both Nacks arrive
+BOOST_FIXTURE_TEST_CASE_TEMPLATE(TwoUpstreams,
+ S, Strategies, StrategyNackReturnFixture<S>)
+{
+ fib::Entry& fibEntry = *this->fib.insert(Name()).first;
+ fibEntry.addNextHop(*this->face3, 10);
+ fibEntry.addNextHop(*this->face4, 20);
+ fibEntry.addNextHop(*this->face5, 30);
+
+ shared_ptr<Interest> interest1 = makeInterest("/aS9FAyUV19", 286);
+ shared_ptr<pit::Entry> pitEntry = this->pit.insert(*interest1).first;
+ pitEntry->insertOrUpdateInRecord(*this->face1, *interest1);
+ pitEntry->insertOrUpdateOutRecord(*this->face3, *interest1);
+ pitEntry->insertOrUpdateOutRecord(*this->face4, *interest1);
+
+ lp::Nack nack3 = makeNack("/aS9FAyUV19", 286, lp::NackReason::CONGESTION);
+ pitEntry->getOutRecord(*this->face3)->setIncomingNack(nack3);
+ this->strategy.afterReceiveNack(*this->face3, nack3, pitEntry);
+
+ BOOST_CHECK_EQUAL(this->strategy.sendNackHistory.size(), 0); // don't send Nack until all upstreams have Nacked
+
+ lp::Nack nack4 = makeNack("/aS9FAyUV19", 286, lp::NackReason::CONGESTION);
+ pitEntry->getOutRecord(*this->face4)->setIncomingNack(nack4);
+ BOOST_REQUIRE(this->strategy.waitForAction(
+ [&] { this->strategy.afterReceiveNack(*this->face4, nack4, pitEntry); },
+ this->limitedIo));
+
+ BOOST_REQUIRE_EQUAL(this->strategy.sendNackHistory.size(), 1);
+ BOOST_CHECK_EQUAL(this->strategy.sendNackHistory[0].pitInterest, pitEntry->getInterest());
+ BOOST_CHECK_EQUAL(this->strategy.sendNackHistory[0].outFaceId, this->face1->getId());
+ BOOST_CHECK_EQUAL(this->strategy.sendNackHistory[0].header.getReason(), lp::NackReason::CONGESTION);
+}
+
+// two upstreams, one times out, don't send Nack
+BOOST_FIXTURE_TEST_CASE_TEMPLATE(Timeout,
+ S, Strategies, StrategyNackReturnFixture<S>)
+{
+ fib::Entry& fibEntry = *this->fib.insert(Name()).first;
+ fibEntry.addNextHop(*this->face3, 10);
+ fibEntry.addNextHop(*this->face4, 20);
+ fibEntry.addNextHop(*this->face5, 30);
+
+ shared_ptr<Interest> interest1 = makeInterest("/sIYw0TXWDj", 115);
+ interest1->setInterestLifetime(time::milliseconds(400));
+ shared_ptr<pit::Entry> pitEntry = this->pit.insert(*interest1).first;
+ pitEntry->insertOrUpdateInRecord(*this->face1, *interest1);
+ pitEntry->insertOrUpdateOutRecord(*this->face3, *interest1);
+
+ this->advanceClocks(time::milliseconds(300));
+ shared_ptr<Interest> interest2 = makeInterest("/sIYw0TXWDj", 223);
+ pitEntry->insertOrUpdateInRecord(*this->face1, *interest2);
+ pitEntry->insertOrUpdateOutRecord(*this->face4, *interest2);
+
+ this->advanceClocks(time::milliseconds(200)); // face3 has timed out
+
+ lp::Nack nack4 = makeNack("/sIYw0TXWDj", 223, lp::NackReason::CONGESTION);
+ pitEntry->getOutRecord(*this->face4)->setIncomingNack(nack4);
+ this->strategy.afterReceiveNack(*this->face4, nack4, pitEntry);
+
+ BOOST_CHECK_EQUAL(this->strategy.sendNackHistory.size(), 0);
+}
+
+// #3033 note-7
+BOOST_FIXTURE_TEST_CASE_TEMPLATE(LiveDeadlock,
+ S, Strategies, UnitTestTimeFixture)
+{
+ /*
+ * /----------\
+ * | producer |
+ * \----------/
+ * |
+ * +---+
+ * | P |
+ * +---+
+ * |
+ * failed link
+ * |
+ * +---+
+ * | R |
+ * +---+
+ * ^ ^
+ * / \
+ * / \
+ * +---+ +---+
+ * | B | <---> | C |
+ * +---+ +---+
+ * ^ ^
+ * | |
+ * | |
+ * +---+ +---+
+ * | A | | D |
+ * +---+ +---+
+ * ^ ^
+ * | |
+ * /----------\ /----------\
+ * | consumer | | consumer |
+ * \----------/ \----------/
+ */
+
+ TopologyTester topo;
+ TopologyNode nodeP = topo.addForwarder("P"),
+ nodeR = topo.addForwarder("R"),
+ nodeA = topo.addForwarder("A"),
+ nodeB = topo.addForwarder("B"),
+ nodeC = topo.addForwarder("C"),
+ nodeD = topo.addForwarder("D");
+
+ for (TopologyNode node : {nodeP, nodeR, nodeA, nodeB, nodeC, nodeD}) {
+ topo.setStrategy<S>(node);
+ }
+
+ const time::milliseconds LINK_DELAY(10);
+ shared_ptr<TopologyLink> linkPR = topo.addLink("PR", LINK_DELAY, {nodeP, nodeR}),
+ linkRB = topo.addLink("RB", LINK_DELAY, {nodeR, nodeB}),
+ linkRC = topo.addLink("RC", LINK_DELAY, {nodeR, nodeC}),
+ linkBC = topo.addLink("BC", LINK_DELAY, {nodeB, nodeC}),
+ linkBA = topo.addLink("BA", LINK_DELAY, {nodeB, nodeA}),
+ linkCD = topo.addLink("CD", LINK_DELAY, {nodeC, nodeD});
+
+ // TODO register the prefix on R->P but then set the face DOWN
+ // topo.registerPrefix(nodeR, linkPR->getFace(nodeR), "ndn:/P", 10);
+ topo.registerPrefix(nodeB, linkRB->getFace(nodeB), "ndn:/P", 20);
+ topo.registerPrefix(nodeB, linkBC->getFace(nodeB), "ndn:/P", 30);
+ topo.registerPrefix(nodeC, linkRC->getFace(nodeC), "ndn:/P", 20);
+ topo.registerPrefix(nodeC, linkBC->getFace(nodeC), "ndn:/P", 30);
+ topo.registerPrefix(nodeA, linkBA->getFace(nodeA), "ndn:/P", 30);
+ topo.registerPrefix(nodeD, linkCD->getFace(nodeD), "ndn:/P", 30);
+
+ ndn::Face& appA = topo.addAppFace("A", nodeA)->getClientFace();
+ ndn::Face& appD = topo.addAppFace("D", nodeD)->getClientFace();
+
+ int nNacksA = 0, nNacksD = 0;
+ appA.expressInterest(Interest("/P/1"), nullptr, bind([&nNacksA]{ ++nNacksA; }), nullptr);
+ appD.expressInterest(Interest("/P/1"), nullptr, bind([&nNacksD]{ ++nNacksD; }), nullptr);
+ this->advanceClocks(time::milliseconds(1), time::milliseconds(5));
+ appA.expressInterest(Interest("/P/1"), nullptr, bind([&nNacksA]{ ++nNacksA; }), nullptr);
+ appD.expressInterest(Interest("/P/1"), nullptr, bind([&nNacksD]{ ++nNacksD; }), nullptr);
+ this->advanceClocks(time::milliseconds(1), time::milliseconds(100));
+
+ // As long as at least one Nack arrives at each client, strategy behavior is correct.
+ // Whether both Interests are Nacked is a client face behavior, not strategy behavior.
+ BOOST_CHECK_GT(nNacksA, 0);
+ BOOST_CHECK_GT(nNacksD, 0);
+}
+
+template<lp::NackReason X, lp::NackReason Y, lp::NackReason R>
+struct NackReasonCombination
+{
+ static lp::NackReason
+ getX()
+ {
+ return X;
+ }
+
+ static lp::NackReason
+ getY()
+ {
+ return Y;
+ }
+
+ static lp::NackReason
+ getExpectedResult()
+ {
+ return R;
+ }
+};
+
+using NackReasonCombinations = boost::mpl::vector<
+ NackReasonCombination<lp::NackReason::CONGESTION, lp::NackReason::CONGESTION, lp::NackReason::CONGESTION>,
+ NackReasonCombination<lp::NackReason::CONGESTION, lp::NackReason::DUPLICATE, lp::NackReason::CONGESTION>,
+ NackReasonCombination<lp::NackReason::CONGESTION, lp::NackReason::NO_ROUTE, lp::NackReason::CONGESTION>,
+ NackReasonCombination<lp::NackReason::CONGESTION, lp::NackReason::NONE, lp::NackReason::CONGESTION>,
+ NackReasonCombination<lp::NackReason::DUPLICATE, lp::NackReason::CONGESTION, lp::NackReason::CONGESTION>,
+ NackReasonCombination<lp::NackReason::DUPLICATE, lp::NackReason::DUPLICATE, lp::NackReason::DUPLICATE>,
+ NackReasonCombination<lp::NackReason::DUPLICATE, lp::NackReason::NO_ROUTE, lp::NackReason::DUPLICATE>,
+ NackReasonCombination<lp::NackReason::DUPLICATE, lp::NackReason::NONE, lp::NackReason::DUPLICATE>,
+ NackReasonCombination<lp::NackReason::NO_ROUTE, lp::NackReason::CONGESTION, lp::NackReason::CONGESTION>,
+ NackReasonCombination<lp::NackReason::NO_ROUTE, lp::NackReason::DUPLICATE, lp::NackReason::DUPLICATE>,
+ NackReasonCombination<lp::NackReason::NO_ROUTE, lp::NackReason::NO_ROUTE, lp::NackReason::NO_ROUTE>,
+ NackReasonCombination<lp::NackReason::NO_ROUTE, lp::NackReason::NONE, lp::NackReason::NO_ROUTE>,
+ NackReasonCombination<lp::NackReason::NONE, lp::NackReason::CONGESTION, lp::NackReason::CONGESTION>,
+ NackReasonCombination<lp::NackReason::NONE, lp::NackReason::DUPLICATE, lp::NackReason::DUPLICATE>,
+ NackReasonCombination<lp::NackReason::NONE, lp::NackReason::NO_ROUTE, lp::NackReason::NO_ROUTE>,
+ NackReasonCombination<lp::NackReason::NONE, lp::NackReason::NONE, lp::NackReason::NONE>
+>;
+
+// use BestRouteStrategy2 as a representative to test Nack reason combination
+BOOST_FIXTURE_TEST_CASE_TEMPLATE(CombineReasons, Combination, NackReasonCombinations,
+ StrategyNackReturnFixture<BestRouteStrategy2>)
+{
+ fib::Entry& fibEntry = *fib.insert(Name()).first;
+ fibEntry.addNextHop(*face3, 10);
+ fibEntry.addNextHop(*face4, 20);
+ fibEntry.addNextHop(*face5, 30);
+
+ shared_ptr<Interest> interest1 = makeInterest("/F6sEwB24I", 282);
+ shared_ptr<pit::Entry> pitEntry = pit.insert(*interest1).first;
+ pitEntry->insertOrUpdateInRecord(*face1, *interest1);
+ pitEntry->insertOrUpdateOutRecord(*face3, *interest1);
+ pitEntry->insertOrUpdateOutRecord(*face4, *interest1);
+
+ lp::Nack nack3 = makeNack(*interest1, Combination::getX());
+ pitEntry->getOutRecord(*face3)->setIncomingNack(nack3);
+ strategy.afterReceiveNack(*face3, nack3, pitEntry);
+
+ BOOST_CHECK_EQUAL(strategy.sendNackHistory.size(), 0);
+
+ lp::Nack nack4 = makeNack(*interest1, Combination::getY());
+ pitEntry->getOutRecord(*face4)->setIncomingNack(nack4);
+ strategy.afterReceiveNack(*face4, nack4, pitEntry);
+
+ BOOST_REQUIRE_EQUAL(strategy.sendNackHistory.size(), 1);
+ BOOST_CHECK_EQUAL(strategy.sendNackHistory[0].pitInterest, pitEntry->getInterest());
+ BOOST_CHECK_EQUAL(strategy.sendNackHistory[0].outFaceId, face1->getId());
+ BOOST_CHECK_EQUAL(strategy.sendNackHistory[0].header.getReason(), Combination::getExpectedResult());
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestStrategyNackReturn
+BOOST_AUTO_TEST_SUITE_END() // Fw
+
+} // namespace tests
+} // namespace fw
+} // namespace nfd
diff --git a/tests/daemon/fw/strategy-scope-control.t.cpp b/tests/daemon/fw/strategy-scope-control.t.cpp
index d031bf9..8a9b93d 100644
--- a/tests/daemon/fw/strategy-scope-control.t.cpp
+++ b/tests/daemon/fw/strategy-scope-control.t.cpp
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
- * Copyright (c) 2014-2016, Regents of the University of California,
+ * Copyright (c) 2014-2017, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
* University Pierre & Marie Curie, Sorbonne University,
@@ -55,7 +55,6 @@
public:
StrategyScopeControlFixture()
: limitedIo(this)
- , nStrategyActions(0)
, strategy(choose<StrategyTester<S>>(forwarder))
, fib(forwarder.getFib())
, pit(forwarder.getPit())
@@ -64,37 +63,14 @@
, localFace3(make_shared<DummyFace>("dummy://3", "dummy://3", ndn::nfd::FACE_SCOPE_LOCAL))
, localFace4(make_shared<DummyFace>("dummy://4", "dummy://4", ndn::nfd::FACE_SCOPE_LOCAL))
{
- this->strategy.afterAction.connect([this] {
- limitedIo.afterOp();
- ++nStrategyActions;
- });
-
forwarder.addFace(nonLocalFace1);
forwarder.addFace(nonLocalFace2);
forwarder.addFace(localFace3);
forwarder.addFace(localFace4);
}
- /** \brief execute f and wait for a number of strategy actions
- * \note The actions may occur either during f() invocation or afterwards.
- */
- void
- waitForStrategyAction(const std::function<void()>& f, int nExpectedActions = 1)
- {
- nStrategyActions = 0;
- f();
- if (nStrategyActions < nExpectedActions) {
- BOOST_REQUIRE_EQUAL(limitedIo.run(nExpectedActions - nStrategyActions, LimitedIo::UNLIMITED_TIME),
- LimitedIo::EXCEED_OPS);
- }
- // A correctly implemented strategy is required to invoke reject pending Interest action
- // if it decides to not forward an Interest. If a test case is stuck in an endless loop within
- // this function, check that rejectPendingInterest is invoked under proper condition.
- }
-
public:
LimitedIo limitedIo;
- int nStrategyActions;
Forwarder forwarder;
StrategyTester<S>& strategy;
@@ -148,8 +124,9 @@
shared_ptr<pit::Entry> pitEntry = this->pit.insert(*interest).first;
pitEntry->insertOrUpdateInRecord(*this->localFace3, *interest);
- this->waitForStrategyAction(
- [&] { this->strategy.afterReceiveInterest(*this->localFace3, *interest, pitEntry); });
+ BOOST_REQUIRE(this->strategy.waitForAction(
+ [&] { this->strategy.afterReceiveInterest(*this->localFace3, *interest, pitEntry); },
+ this->limitedIo));
BOOST_CHECK_EQUAL(this->strategy.sendInterestHistory.size(), 1);
BOOST_CHECK_EQUAL(this->strategy.rejectPendingInterestHistory.size(), 0);
@@ -166,9 +143,9 @@
shared_ptr<pit::Entry> pitEntry = this->pit.insert(*interest).first;
pitEntry->insertOrUpdateInRecord(*this->localFace3, *interest);
- this->waitForStrategyAction(
+ BOOST_REQUIRE(this->strategy.waitForAction(
[&] { this->strategy.afterReceiveInterest(*this->localFace3, *interest, pitEntry); },
- 1 + T::willSendNackNoRoute());
+ this->limitedIo, 1 + T::willSendNackNoRoute()));
BOOST_CHECK_EQUAL(this->strategy.sendInterestHistory.size(), 0);
BOOST_CHECK_EQUAL(this->strategy.rejectPendingInterestHistory.size(), 1);
@@ -189,8 +166,9 @@
shared_ptr<pit::Entry> pitEntry = this->pit.insert(*interest).first;
pitEntry->insertOrUpdateInRecord(*this->localFace3, *interest);
- this->waitForStrategyAction(
- [&] { this->strategy.afterReceiveInterest(*this->localFace3, *interest, pitEntry); });
+ BOOST_REQUIRE(this->strategy.waitForAction(
+ [&] { this->strategy.afterReceiveInterest(*this->localFace3, *interest, pitEntry); },
+ this->limitedIo));
BOOST_REQUIRE_EQUAL(this->strategy.sendInterestHistory.size(), 1);
BOOST_CHECK_EQUAL(this->strategy.sendInterestHistory.back().outFaceId, this->localFace4->getId());
@@ -208,9 +186,9 @@
shared_ptr<pit::Entry> pitEntry = this->pit.insert(*interest).first;
pitEntry->insertOrUpdateInRecord(*this->nonLocalFace1, *interest);
- this->waitForStrategyAction(
+ BOOST_REQUIRE(this->strategy.waitForAction(
[&] { this->strategy.afterReceiveInterest(*this->nonLocalFace1, *interest, pitEntry); },
- 1 + T::willSendNackNoRoute());
+ this->limitedIo, 1 + T::willSendNackNoRoute()));
BOOST_CHECK_EQUAL(this->strategy.sendInterestHistory.size(), 0);
BOOST_CHECK_EQUAL(this->strategy.rejectPendingInterestHistory.size(), 1);
@@ -231,8 +209,9 @@
shared_ptr<pit::Entry> pitEntry = this->pit.insert(*interest).first;
pitEntry->insertOrUpdateInRecord(*this->nonLocalFace1, *interest);
- this->waitForStrategyAction(
- [&] { this->strategy.afterReceiveInterest(*this->nonLocalFace1, *interest, pitEntry); });
+ BOOST_REQUIRE(this->strategy.waitForAction(
+ [&] { this->strategy.afterReceiveInterest(*this->nonLocalFace1, *interest, pitEntry); },
+ this->limitedIo));
BOOST_REQUIRE_EQUAL(this->strategy.sendInterestHistory.size(), 1);
BOOST_CHECK_EQUAL(this->strategy.sendInterestHistory.back().outFaceId, this->localFace4->getId());
@@ -253,9 +232,9 @@
lp::Nack nack = makeNack("/localhost/A/1", 1460, lp::NackReason::NO_ROUTE);
pitEntry->insertOrUpdateOutRecord(*this->localFace4, *interest)->setIncomingNack(nack);
- this->waitForStrategyAction(
+ BOOST_REQUIRE(this->strategy.waitForAction(
[&] { this->strategy.afterReceiveNack(*this->localFace4, nack, pitEntry); },
- T::canProcessNack());
+ this->limitedIo, T::canProcessNack()));
BOOST_CHECK_EQUAL(this->strategy.sendInterestHistory.size(), 0);
BOOST_CHECK_EQUAL(this->strategy.rejectPendingInterestHistory.size(), 0);
@@ -278,9 +257,9 @@
lp::Nack nack = makeNack("/localhop/A/1", 1377, lp::NackReason::NO_ROUTE);
pitEntry->insertOrUpdateOutRecord(*this->localFace4, *interest)->setIncomingNack(nack);
- this->waitForStrategyAction(
+ BOOST_REQUIRE(this->strategy.waitForAction(
[&] { this->strategy.afterReceiveNack(*this->localFace4, nack, pitEntry); },
- T::canProcessNack());
+ this->limitedIo, T::canProcessNack()));
BOOST_CHECK_EQUAL(this->strategy.sendInterestHistory.size(), 0);
BOOST_CHECK_EQUAL(this->strategy.rejectPendingInterestHistory.size(), 0);
diff --git a/tests/daemon/fw/strategy-tester.hpp b/tests/daemon/fw/strategy-tester.hpp
index 9f3e18a..0d76d76 100644
--- a/tests/daemon/fw/strategy-tester.hpp
+++ b/tests/daemon/fw/strategy-tester.hpp
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
- * Copyright (c) 2014-2016, Regents of the University of California,
+ * Copyright (c) 2014-2017, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
* University Pierre & Marie Curie, Sorbonne University,
@@ -26,8 +26,8 @@
#ifndef NFD_TESTS_DAEMON_FW_STRATEGY_TESTER_HPP
#define NFD_TESTS_DAEMON_FW_STRATEGY_TESTER_HPP
-#include <boost/tuple/tuple_comparison.hpp>
#include "fw/strategy.hpp"
+#include "tests/limited-io.hpp"
namespace nfd {
namespace fw {
@@ -77,6 +77,33 @@
*/
signal::Signal<StrategyTester<S>> afterAction;
+ /** \brief execute f and wait for a number of strategy actions
+ * \note The actions may occur either during f() invocation or afterwards.
+ * \return whether expected number of actions have occurred
+ */
+ bool
+ waitForAction(const std::function<void()>& f,
+ nfd::tests::LimitedIo& limitedIo, int nExpectedActions = 1)
+ {
+ int nActions = 0;
+
+ signal::ScopedConnection conn = afterAction.connect([&] {
+ limitedIo.afterOp();
+ ++nActions;
+ });
+
+ f();
+
+ if (nActions < nExpectedActions) {
+ // A correctly implemented strategy is required to invoke reject pending Interest action if it
+ // decides to not forward an Interest. If a test case is stuck in the call below, check that
+ // rejectPendingInterest is invoked under proper condition.
+ return limitedIo.run(nExpectedActions - nActions, nfd::tests::LimitedIo::UNLIMITED_TIME) ==
+ nfd::tests::LimitedIo::EXCEED_OPS;
+ }
+ return nActions == nExpectedActions;
+ }
+
protected:
void
sendInterest(const shared_ptr<pit::Entry>& pitEntry, Face& outFace,
diff --git a/tests/test-common.cpp b/tests/test-common.cpp
index 0f4a77b..2dabe39 100644
--- a/tests/test-common.cpp
+++ b/tests/test-common.cpp
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
- * Copyright (c) 2014-2016, Regents of the University of California,
+ * Copyright (c) 2014-2017, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
* University Pierre & Marie Curie, Sorbonne University,
@@ -119,13 +119,19 @@
}
lp::Nack
+makeNack(Interest interest, lp::NackReason reason)
+{
+ lp::Nack nack(std::move(interest));
+ nack.setReason(reason);
+ return nack;
+}
+
+lp::Nack
makeNack(const Name& name, uint32_t nonce, lp::NackReason reason)
{
Interest interest(name);
interest.setNonce(nonce);
- lp::Nack nack(std::move(interest));
- nack.setReason(reason);
- return nack;
+ return makeNack(std::move(interest), reason);
}
} // namespace tests
diff --git a/tests/test-common.hpp b/tests/test-common.hpp
index 9ea3d3c..4d046d2 100644
--- a/tests/test-common.hpp
+++ b/tests/test-common.hpp
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
- * Copyright (c) 2014-2016, Regents of the University of California,
+ * Copyright (c) 2014-2017, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
* University Pierre & Marie Curie, Sorbonne University,
@@ -143,6 +143,13 @@
makeLink(const Name& name, std::initializer_list<std::pair<uint32_t, Name>> delegations);
/** \brief create a Nack
+ * \param interest Interest
+ * \param reason Nack reason
+ */
+lp::Nack
+makeNack(Interest interest, lp::NackReason reason);
+
+/** \brief create a Nack
* \param name Interest name
* \param nonce Interest nonce
* \param reason Nack reason