fw: NccStrategy
refs #1242
Change-Id: I21f870728b7b361547adbde9d64357c93aeb1ed9
diff --git a/tests/core/limited-io.cpp b/tests/core/limited-io.cpp
new file mode 100644
index 0000000..5752685
--- /dev/null
+++ b/tests/core/limited-io.cpp
@@ -0,0 +1,58 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (C) 2014 Named Data Networking Project
+ * See COPYING for copyright and distribution information.
+ */
+
+#include "limited-io.hpp"
+
+namespace nfd {
+
+const int LimitedIo::UNLIMITED_OPS = std::numeric_limits<int>::max();
+const time::Duration LimitedIo::UNLIMITED_TIME = time::nanoseconds(-1);
+
+LimitedIo::LimitedIo()
+ : m_isRunning(false)
+ , m_nOpsRemaining(0)
+{
+ resetGlobalIoService();
+}
+
+LimitedIo::StopReason
+LimitedIo::run(int nOpsLimit, time::Duration nTimeLimit)
+{
+ BOOST_ASSERT(!m_isRunning);
+ m_isRunning = true;
+
+ m_reason = NO_WORK;
+ m_nOpsRemaining = nOpsLimit;
+ if (nTimeLimit != UNLIMITED_TIME) {
+ m_timeout = scheduler::schedule(nTimeLimit, bind(&LimitedIo::afterTimeout, this));
+ }
+
+ getGlobalIoService().run();
+
+ getGlobalIoService().reset();
+ scheduler::cancel(m_timeout);
+ m_isRunning = false;
+ return m_reason;
+}
+
+void
+LimitedIo::afterOp()
+{
+ --m_nOpsRemaining;
+ if (m_nOpsRemaining <= 0) {
+ m_reason = EXCEED_OPS;
+ getGlobalIoService().stop();
+ }
+}
+
+void
+LimitedIo::afterTimeout()
+{
+ m_reason = EXCEED_TIME;
+ getGlobalIoService().stop();
+}
+
+} // namespace nfd
diff --git a/tests/core/limited-io.hpp b/tests/core/limited-io.hpp
new file mode 100644
index 0000000..14dadd7
--- /dev/null
+++ b/tests/core/limited-io.hpp
@@ -0,0 +1,49 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (C) 2014 Named Data Networking Project
+ * See COPYING for copyright and distribution information.
+ */
+
+#ifndef NFD_TEST_CORE_LIMITED_IO_HPP
+#define NFD_TEST_CORE_LIMITED_IO_HPP
+
+#include "core/scheduler.hpp"
+
+namespace nfd {
+
+class LimitedIo
+{
+public:
+ LimitedIo();
+
+ enum StopReason
+ {
+ NO_WORK,
+ EXCEED_OPS,
+ EXCEED_TIME
+ };
+
+ StopReason
+ run(int nOpsLimit, time::Duration nTimeLimit);
+
+ void
+ afterOp();
+
+private:
+ void
+ afterTimeout();
+
+public:
+ static const int UNLIMITED_OPS;
+ static const time::Duration UNLIMITED_TIME;
+
+private:
+ bool m_isRunning;
+ int m_nOpsRemaining;
+ EventId m_timeout;
+ StopReason m_reason;
+};
+
+} // namespace nfd
+
+#endif // NFD_TEST_CORE_LIMITED_IO_HPP
diff --git a/tests/fw/ncc-strategy.cpp b/tests/fw/ncc-strategy.cpp
new file mode 100644
index 0000000..a258b83
--- /dev/null
+++ b/tests/fw/ncc-strategy.cpp
@@ -0,0 +1,87 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (C) 2014 Named Data Networking Project
+ * See COPYING for copyright and distribution information.
+ */
+
+#include "fw/ncc-strategy.hpp"
+#include "strategy-tester.hpp"
+#include "../face/dummy-face.hpp"
+#include "../core/limited-io.hpp"
+
+#include <boost/test/unit_test.hpp>
+
+namespace nfd {
+
+BOOST_AUTO_TEST_SUITE(FwNccStrategy)
+
+// NccStrategy is fairly complex.
+// The most important property is:
+// it remembers which upstream is the fastest to return Data,
+// and favors this upstream in subsequent Interests.
+BOOST_AUTO_TEST_CASE(FavorRespondingUpstream)
+{
+ LimitedIo limitedIo;
+ Forwarder forwarder;
+ typedef StrategyTester<fw::NccStrategy> NccStrategyTester;
+ shared_ptr<NccStrategyTester> strategy = make_shared<NccStrategyTester>(boost::ref(forwarder));
+ strategy->onAction += bind(&LimitedIo::afterOp, &limitedIo);
+
+ shared_ptr<DummyFace> face1 = make_shared<DummyFace>();
+ shared_ptr<DummyFace> face2 = make_shared<DummyFace>();
+ shared_ptr<DummyFace> face3 = make_shared<DummyFace>();
+ forwarder.addFace(face1);
+ forwarder.addFace(face2);
+ forwarder.addFace(face3);
+
+ Fib& fib = forwarder.getFib();
+ shared_ptr<fib::Entry> fibEntry = fib.insert(Name()).first;
+ fibEntry->setStrategy(strategy);
+ fibEntry->addNextHop(face1, 10);
+ fibEntry->addNextHop(face2, 20);
+
+ Pit& pit = forwarder.getPit();
+
+ // first Interest: strategy knows nothing and follows routing
+ shared_ptr<Interest> interest1p = make_shared<Interest>("ndn:/0Jm1ajrW/%00");
+ Interest& interest1 = *interest1p;
+ interest1.setInterestLifetime(8000);
+ shared_ptr<pit::Entry> pitEntry1 = pit.insert(interest1).first;
+
+ pitEntry1->insertOrUpdateInRecord(face3, interest1);
+ strategy->afterReceiveInterest(*face3, interest1, fibEntry, pitEntry1);
+
+ // forwards to face1 because routing says it's best
+ limitedIo.run(LimitedIo::UNLIMITED_OPS, time::milliseconds(1));
+ BOOST_REQUIRE_EQUAL(strategy->m_sendInterestHistory.size(), 1);
+ BOOST_CHECK_EQUAL(strategy->m_sendInterestHistory[0].get<1>(), face1);
+
+ // forwards to face2 because face1 doesn't respond
+ limitedIo.run(LimitedIo::UNLIMITED_OPS, time::milliseconds(500));
+ BOOST_REQUIRE_EQUAL(strategy->m_sendInterestHistory.size(), 2);
+ BOOST_CHECK_EQUAL(strategy->m_sendInterestHistory[1].get<1>(), face2);
+
+ // face2 responds
+ shared_ptr<Data> data1p = make_shared<Data>("ndn:/0Jm1ajrW/%00");
+ Data& data1 = *data1p;
+ strategy->beforeSatisfyPendingInterest(pitEntry1, *face2, data1);
+ limitedIo.run(LimitedIo::UNLIMITED_OPS, time::milliseconds(500));
+
+ // second Interest: strategy knows face2 is best
+ shared_ptr<Interest> interest2p = make_shared<Interest>("ndn:/0Jm1ajrW/%00%01");
+ Interest& interest2 = *interest2p;
+ interest2.setInterestLifetime(8000);
+ shared_ptr<pit::Entry> pitEntry2 = pit.insert(interest2).first;
+
+ pitEntry2->insertOrUpdateInRecord(face3, interest2);
+ strategy->afterReceiveInterest(*face3, interest2, fibEntry, pitEntry2);
+
+ // forwards to face2 because it responds previously
+ limitedIo.run(LimitedIo::UNLIMITED_OPS, time::milliseconds(1));
+ BOOST_REQUIRE_GE(strategy->m_sendInterestHistory.size(), 3);
+ BOOST_CHECK_EQUAL(strategy->m_sendInterestHistory[2].get<1>(), face2);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace nfd
diff --git a/tests/fw/strategy-tester.hpp b/tests/fw/strategy-tester.hpp
index 0ec4770..f8e0848 100644
--- a/tests/fw/strategy-tester.hpp
+++ b/tests/fw/strategy-tester.hpp
@@ -26,6 +26,9 @@
: S(forwarder)
{
}
+
+ /// fires after each Action
+ EventEmitter<> onAction;
protected:
virtual void
@@ -49,6 +52,8 @@
shared_ptr<Face> outFace)
{
m_sendInterestHistory.push_back(SendInterestArgs(pitEntry, outFace));
+ pitEntry->insertOrUpdateOutRecord(outFace, pitEntry->getInterest());
+ onAction();
}
template<typename S>
@@ -56,6 +61,7 @@
StrategyTester<S>::rejectPendingInterest(shared_ptr<pit::Entry> pitEntry)
{
m_rejectPendingInterestHistory.push_back(RejectPendingInterestArgs(pitEntry));
+ onAction();
}