blob: a8492f1d54b016ca4ef2465690c8effa97ba0c0a [file] [log] [blame]
Alexander Afanasyevc169a812014-05-20 20:37:29 -04001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
Junxiao Shi909ffef2017-07-07 22:12:27 +00002/*
Alexander Afanasyev4c9a3d52017-01-03 17:45:19 -08003 * Copyright (c) 2013-2017 Regents of the University of California.
Alexander Afanasyevdfa52c42014-04-24 21:10:11 -07004 *
5 * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
Alexander Afanasyevdfa52c42014-04-24 21:10:11 -07006 *
Alexander Afanasyevc169a812014-05-20 20:37:29 -04007 * ndn-cxx library is free software: you can redistribute it and/or modify it under the
8 * terms of the GNU Lesser General Public License as published by the Free Software
9 * Foundation, either version 3 of the License, or (at your option) any later version.
10 *
11 * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
12 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
13 * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
14 *
15 * You should have received copies of the GNU General Public License and GNU Lesser
16 * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
17 * <http://www.gnu.org/licenses/>.
18 *
19 * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
Alexander Afanasyev20d2c582014-01-26 15:32:51 -080020 */
21
Alexander Afanasyeve6c65e22015-01-28 19:56:03 -080022#define BOOST_TEST_MAIN 1
23#define BOOST_TEST_DYN_LINK 1
24#define BOOST_TEST_MODULE ndn-cxx Integrated Tests (Face)
25
Alexander Afanasyev09c613f2014-01-29 00:23:58 -080026#include "face.hpp"
Junxiao Shi2bea5c42017-08-14 20:10:32 +000027#include "transport/tcp-transport.hpp"
28#include "transport/unix-transport.hpp"
Alexander Afanasyevbf9671d2014-02-11 13:44:13 -080029#include "util/scheduler.hpp"
Alexander Afanasyev20d2c582014-01-26 15:32:51 -080030
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -070031#include "boost-test.hpp"
Junxiao Shi2bea5c42017-08-14 20:10:32 +000032#include "identity-management-fixture.hpp"
33#include "make-interest-data.hpp"
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -070034
Junxiao Shicf9c6bb2016-07-27 02:18:19 +000035#include <stdio.h>
Alexander Afanasyeva54d5a62017-02-11 19:01:34 -080036#include <thread>
37#include <mutex>
38#include <condition_variable>
Junxiao Shi2bea5c42017-08-14 20:10:32 +000039#include <boost/mpl/vector.hpp>
Junxiao Shicf9c6bb2016-07-27 02:18:19 +000040
Alexander Afanasyev0abb2da2014-01-30 18:07:57 -080041namespace ndn {
Alexander Afanasyev5fc795f2014-10-20 23:06:56 -040042namespace tests {
Alexander Afanasyev20d2c582014-01-26 15:32:51 -080043
Junxiao Shi2bea5c42017-08-14 20:10:32 +000044static Name
45makeVeryLongName(Name prefix = Name())
Alexander Afanasyeve4f8c3b2016-06-23 16:03:48 -070046{
Eric Newberry06690612016-12-27 12:50:15 -070047 for (size_t i = 0; i <= MAX_NDN_PACKET_SIZE / 10; i++) {
Junxiao Shi2bea5c42017-08-14 20:10:32 +000048 prefix.append("0123456789");
49 }
50 return prefix;
51}
52
53static std::string
54executeCommand(const std::string& cmd)
55{
56 std::string output;
57 char buf[256];
58 FILE* pipe = popen(cmd.data(), "r");
59 BOOST_REQUIRE_MESSAGE(pipe != nullptr, "cannot execute '" << cmd << "'");
60 while (fgets(buf, sizeof(buf), pipe) != nullptr) {
61 output += buf;
62 }
63 pclose(pipe);
64 return output;
65}
66
67template<typename TransportType>
68class FaceFixture : public IdentityManagementFixture
69{
70protected:
71 FaceFixture()
72 : face(TransportType::create(""), m_keyChain)
73 , sched(face.getIoService())
74 {
Eric Newberry06690612016-12-27 12:50:15 -070075 }
Alexander Afanasyev49bb1fb2014-07-21 12:54:01 -070076
Junxiao Shi2bea5c42017-08-14 20:10:32 +000077 /** \brief Send an Interest from a secondary face
78 * \param delay scheduling delay before sending Interest
79 * \param interest the Interest
80 * \param[out] outcome the response, initially '?', 'D' for Data, 'N' for Nack, 'T' for timeout
81 * \return scheduled event id
82 */
83 EventId
84 sendInterest(time::nanoseconds delay, const Interest& interest, char& outcome)
Alexander Afanasyevee8bb1e2014-05-02 17:39:54 -070085 {
Junxiao Shi2bea5c42017-08-14 20:10:32 +000086 if (face2 == nullptr) {
87 face2 = make_unique<Face>(TransportType::create(""), face.getIoService(), m_keyChain);
88 }
Alexander Afanasyevee8bb1e2014-05-02 17:39:54 -070089
Junxiao Shi2bea5c42017-08-14 20:10:32 +000090 outcome = '?';
91 return sched.scheduleEvent(delay, [this, interest, &outcome] {
92 face2->expressInterest(interest,
93 [&] (const Interest&, const Data&) { outcome = 'D'; },
94 [&] (const Interest&, const lp::Nack&) { outcome = 'N'; },
95 [&] (const Interest&) { outcome = 'T'; });
96 });
97 }
98
99 EventId
100 sendInterest(time::nanoseconds delay, const Interest& interest)
101 {
102 static char ignoredOutcome;
103 return sendInterest(delay, interest, ignoredOutcome);
104 }
105
106 /** \brief Stop io_service after a delay
107 * \return scheduled event id
108 */
109 EventId
110 terminateAfter(time::nanoseconds delay)
111 {
112 return sched.scheduleEvent(delay, [this] { face.getIoService().stop(); });
Alexander Afanasyevee8bb1e2014-05-02 17:39:54 -0700113 }
Junxiao Shicf9c6bb2016-07-27 02:18:19 +0000114
115protected:
Junxiao Shi2bea5c42017-08-14 20:10:32 +0000116 Face face;
117 Scheduler sched;
118
119 unique_ptr<Face> face2;
Alexander Afanasyevee8bb1e2014-05-02 17:39:54 -0700120};
121
Junxiao Shi2bea5c42017-08-14 20:10:32 +0000122using Transports = boost::mpl::vector<UnixTransport, TcpTransport>;
123
124BOOST_FIXTURE_TEST_SUITE(TestFace, FaceFixture<UnixTransport>)
125
126BOOST_AUTO_TEST_SUITE(Consumer)
127
128BOOST_FIXTURE_TEST_CASE_TEMPLATE(ExpressInterestData, TransportType, Transports, FaceFixture<TransportType>)
Alexander Afanasyevee8bb1e2014-05-02 17:39:54 -0700129{
Junxiao Shi2bea5c42017-08-14 20:10:32 +0000130 int nData = 0;
131 this->face.expressInterest(Interest("/"),
132 [&] (const Interest&, const Data&) { ++nData; },
133 [] (const Interest&, const lp::Nack&) { BOOST_ERROR("unexpected Nack"); },
134 [] (const Interest&) { BOOST_ERROR("unexpected timeout"); });
Alexander Afanasyevee8bb1e2014-05-02 17:39:54 -0700135
Junxiao Shi2bea5c42017-08-14 20:10:32 +0000136 this->face.processEvents();
137 BOOST_CHECK_EQUAL(nData, 1);
Alexander Afanasyevee8bb1e2014-05-02 17:39:54 -0700138}
Alexander Afanasyev90164962014-03-06 08:29:59 +0000139
Junxiao Shi2bea5c42017-08-14 20:10:32 +0000140BOOST_FIXTURE_TEST_CASE_TEMPLATE(ExpressInterestNack, TransportType, Transports, FaceFixture<TransportType>)
Alexander Afanasyev984ad192014-05-02 19:11:15 -0700141{
Junxiao Shi2bea5c42017-08-14 20:10:32 +0000142 int nNacks = 0;
143 this->face.expressInterest(Interest("/localhost/non-existent-should-nack"),
144 [] (const Interest&, const Data&) { BOOST_ERROR("unexpected Data"); },
145 [&] (const Interest&, const lp::Nack&) { ++nNacks; },
146 [] (const Interest&) { BOOST_ERROR("unexpected timeout"); });
Alexander Afanasyev984ad192014-05-02 19:11:15 -0700147
Junxiao Shi2bea5c42017-08-14 20:10:32 +0000148 this->face.processEvents();
149 BOOST_CHECK_EQUAL(nNacks, 1);
Alexander Afanasyev984ad192014-05-02 19:11:15 -0700150}
151
Junxiao Shi2bea5c42017-08-14 20:10:32 +0000152BOOST_FIXTURE_TEST_CASE_TEMPLATE(ExpressInterestTimeout, TransportType, Transports, FaceFixture<TransportType>)
Alexander Afanasyev984ad192014-05-02 19:11:15 -0700153{
Junxiao Shi2bea5c42017-08-14 20:10:32 +0000154 // add route toward null face so Interest would timeout instead of getting Nacked
155 executeCommand("nfdc route add /localhost/non-existent-should-timeout null://");
156 std::this_thread::sleep_for(std::chrono::milliseconds(200)); // wait for FIB update to take effect
Alexander Afanasyev984ad192014-05-02 19:11:15 -0700157
Junxiao Shi2bea5c42017-08-14 20:10:32 +0000158 int nTimeouts = 0;
159 this->face.expressInterest(Interest("/localhost/non-existent-should-timeout", time::seconds(1)),
160 [] (const Interest&, const Data&) { BOOST_ERROR("unexpected Data"); },
161 [] (const Interest&, const lp::Nack&) { BOOST_ERROR("unexpected Nack"); },
162 [&] (const Interest&) { ++nTimeouts; });
Alexander Afanasyev984ad192014-05-02 19:11:15 -0700163
Junxiao Shi2bea5c42017-08-14 20:10:32 +0000164 this->face.processEvents();
165 BOOST_CHECK_EQUAL(nTimeouts, 1);
Alexander Afanasyev984ad192014-05-02 19:11:15 -0700166}
167
Junxiao Shi2bea5c42017-08-14 20:10:32 +0000168BOOST_FIXTURE_TEST_CASE_TEMPLATE(OversizedInterest, TransportType, Transports, FaceFixture<TransportType>)
Alexander Afanasyev984ad192014-05-02 19:11:15 -0700169{
Junxiao Shi2bea5c42017-08-14 20:10:32 +0000170 BOOST_CHECK_THROW(do {
171 this->face.expressInterest(Interest(makeVeryLongName()), nullptr, nullptr, nullptr);
172 this->face.processEvents();
173 } while (false), Face::OversizedPacketError);
Alexander Afanasyev984ad192014-05-02 19:11:15 -0700174}
Alexander Afanasyev5fc795f2014-10-20 23:06:56 -0400175
Junxiao Shi2bea5c42017-08-14 20:10:32 +0000176BOOST_AUTO_TEST_SUITE_END() // Consumer
177
178BOOST_AUTO_TEST_SUITE(Producer)
179
180BOOST_FIXTURE_TEST_CASE_TEMPLATE(RegisterUnregisterPrefix, TransportType, Transports, FaceFixture<TransportType>)
181{
182 this->terminateAfter(time::seconds(4));
183
184 int nRegSuccess = 0, nUnregSuccess = 0;
185 auto id = this->face.registerPrefix("/Hello/World",
186 [&] (const Name&) { ++nRegSuccess; },
187 [] (const Name&, const std::string& msg) { BOOST_ERROR("unexpected register prefix failure: " << msg); });
188
189 this->sched.scheduleEvent(time::seconds(1), [&nRegSuccess] {
190 BOOST_CHECK_EQUAL(nRegSuccess, 1);
191 std::string output = executeCommand("nfdc route list | grep /Hello/World");
192 BOOST_CHECK(!output.empty());
193 });
194
195 this->sched.scheduleEvent(time::seconds(2), [this, id, &nUnregSuccess] {
196 this->face.unregisterPrefix(id,
197 [&] { ++nUnregSuccess; },
198 [] (const std::string& msg) { BOOST_ERROR("unexpected unregister prefix failure: " << msg); });
199 });
200
201 this->sched.scheduleEvent(time::seconds(3), [&nUnregSuccess] {
202 BOOST_CHECK_EQUAL(nUnregSuccess, 1);
203
204 // Boost.Test would fail if a child process exits with non-zero. http://stackoverflow.com/q/5325202
205 std::string output = executeCommand("nfdc route list | grep /Hello/World || true");
206 BOOST_CHECK(output.empty());
207 });
208
209 this->face.processEvents();
210}
211
212BOOST_FIXTURE_TEST_CASE_TEMPLATE(RegularFilter, TransportType, Transports, FaceFixture<TransportType>)
213{
214 this->terminateAfter(time::seconds(2));
215
216 int nInterests1 = 0, nRegSuccess1 = 0, nRegSuccess2 = 0;
217 this->face.setInterestFilter("/Hello/World",
218 [&] (const InterestFilter&, const Interest&) { ++nInterests1; },
219 [&] (const Name&) { ++nRegSuccess1; },
220 [] (const Name&, const std::string& msg) { BOOST_ERROR("unexpected register prefix failure: " << msg); });
221 this->face.setInterestFilter("/Los/Angeles/Lakers",
222 [&] (const InterestFilter&, const Interest&) { BOOST_ERROR("unexpected Interest"); },
223 [&] (const Name&) { ++nRegSuccess2; },
224 [] (const Name&, const std::string& msg) { BOOST_ERROR("unexpected register prefix failure: " << msg); });
225
226 this->sched.scheduleEvent(time::milliseconds(500), [] {
227 std::string output = executeCommand("nfdc route list | grep /Hello/World");
228 BOOST_CHECK(!output.empty());
229 });
230
231 char interestOutcome;
232 this->sendInterest(time::seconds(1), Interest("/Hello/World/regular", time::milliseconds(50)), interestOutcome);
233
234 this->face.processEvents();
235 BOOST_CHECK_EQUAL(interestOutcome, 'T');
236 BOOST_CHECK_EQUAL(nInterests1, 1);
237 BOOST_CHECK_EQUAL(nRegSuccess1, 1);
238 BOOST_CHECK_EQUAL(nRegSuccess2, 1);
239}
240
241BOOST_FIXTURE_TEST_CASE_TEMPLATE(RegexFilter, TransportType, Transports, FaceFixture<TransportType>)
242{
243 this->terminateAfter(time::seconds(2));
244
245 int nRegSuccess = 0;
246 std::set<Name> receivedInterests;
247 this->face.setInterestFilter(InterestFilter("/Hello/World", "<><b><c>?"),
248 [&] (const InterestFilter&, const Interest& interest) { receivedInterests.insert(interest.getName()); },
249 [&] (const Name&) { ++nRegSuccess; },
250 [] (const Name&, const std::string& msg) { BOOST_ERROR("unexpected register prefix failure: " << msg); });
251
252 this->sched.scheduleEvent(time::milliseconds(700), [] {
253 std::string output = executeCommand("nfdc route list | grep /Hello/World");
254 BOOST_CHECK(!output.empty());
255 });
256
257 this->sendInterest(time::milliseconds(200), Interest("/Hello/World/a", time::milliseconds(50)));
258 this->sendInterest(time::milliseconds(300), Interest("/Hello/World/a/b", time::milliseconds(50)));
259 this->sendInterest(time::milliseconds(400), Interest("/Hello/World/a/b/c", time::milliseconds(50)));
260 this->sendInterest(time::milliseconds(500), Interest("/Hello/World/a/b/d", time::milliseconds(50)));
261
262 this->face.processEvents();
263 BOOST_CHECK_EQUAL(nRegSuccess, 1);
264 std::set<Name> expectedInterests{"/Hello/World/a/b", "/Hello/World/a/b/c"};
265 BOOST_CHECK_EQUAL_COLLECTIONS(receivedInterests.begin(), receivedInterests.end(),
266 expectedInterests.begin(), expectedInterests.end());
267}
268
269BOOST_FIXTURE_TEST_CASE_TEMPLATE(RegexFilterNoRegister, TransportType, Transports, FaceFixture<TransportType>)
270{
271 this->terminateAfter(time::seconds(2));
272
273 // no Interest shall arrive because prefix isn't registered in forwarder
274 this->face.setInterestFilter(InterestFilter("/Hello/World", "<><b><c>?"),
275 [&] (const InterestFilter&, const Interest& interest) { BOOST_ERROR("unexpected Interest"); });
276
277 this->sched.scheduleEvent(time::milliseconds(700), [] {
278 // Boost.Test would fail if a child process exits with non-zero. http://stackoverflow.com/q/5325202
279 std::string output = executeCommand("nfdc route list | grep /Hello/World || true");
280 BOOST_CHECK(output.empty());
281 });
282
283 this->sendInterest(time::milliseconds(200), Interest("/Hello/World/a", time::milliseconds(50)));
284 this->sendInterest(time::milliseconds(300), Interest("/Hello/World/a/b", time::milliseconds(50)));
285 this->sendInterest(time::milliseconds(400), Interest("/Hello/World/a/b/c", time::milliseconds(50)));
286 this->sendInterest(time::milliseconds(500), Interest("/Hello/World/a/b/d", time::milliseconds(50)));
287
288 this->face.processEvents();
289}
290
291BOOST_FIXTURE_TEST_CASE_TEMPLATE(PutDataNack, TransportType, Transports, FaceFixture<TransportType>)
292{
293 this->terminateAfter(time::seconds(2));
294
295 this->face.setInterestFilter("/Hello/World",
296 [&] (const InterestFilter&, const Interest& interest) {
297 if (interest.getName().at(2) == name::Component("nack")) {
298 this->face.put(makeNack(interest, lp::NackReason::NO_ROUTE));
299 }
300 else {
301 this->face.put(*makeData(interest.getName()));
302 }
303 },
304 nullptr,
305 [] (const Name&, const std::string& msg) { BOOST_ERROR("unexpected register prefix failure: " << msg); });
306
307 char outcome1, outcome2;
308 this->sendInterest(time::milliseconds(700), Interest("/Hello/World/data", time::milliseconds(50)), outcome1);
309 this->sendInterest(time::milliseconds(800), Interest("/Hello/World/nack", time::milliseconds(50)), outcome2);
310
311 this->face.processEvents();
312 BOOST_CHECK_EQUAL(outcome1, 'D');
313 BOOST_CHECK_EQUAL(outcome2, 'N');
314}
315
316BOOST_FIXTURE_TEST_CASE_TEMPLATE(OversizedData, TransportType, Transports, FaceFixture<TransportType>)
317{
318 this->terminateAfter(time::seconds(2));
319
320 this->face.setInterestFilter("/Hello/World",
321 [&] (const InterestFilter&, const Interest& interest) {
322 this->face.put(*makeData(makeVeryLongName(interest.getName())));
323 },
324 nullptr,
325 [] (const Name&, const std::string& msg) { BOOST_ERROR("unexpected register prefix failure: " << msg); });
326
327 this->sendInterest(time::seconds(1), Interest("/Hello/World/oversized", time::milliseconds(50)));
328
329 BOOST_CHECK_THROW(this->face.processEvents(), Face::OversizedPacketError);
330}
331
332BOOST_AUTO_TEST_SUITE_END() // Producer
333
334BOOST_AUTO_TEST_SUITE(IoRoutine)
335
Alexander Afanasyevfba1ac62015-08-26 15:19:13 -0700336BOOST_AUTO_TEST_CASE(ShutdownWhileSendInProgress) // Bug #3136
337{
Junxiao Shi2bea5c42017-08-14 20:10:32 +0000338 this->face.expressInterest(Interest("/Hello/World"), nullptr, nullptr, nullptr);
339 this->face.processEvents(time::seconds(1));
Alexander Afanasyevfba1ac62015-08-26 15:19:13 -0700340
Junxiao Shi2bea5c42017-08-14 20:10:32 +0000341 this->face.expressInterest(Interest("/Bye/World/1"), nullptr, nullptr, nullptr);
342 this->face.expressInterest(Interest("/Bye/World/2"), nullptr, nullptr, nullptr);
343 this->face.expressInterest(Interest("/Bye/World/3"), nullptr, nullptr, nullptr);
344 this->face.shutdown();
Alexander Afanasyevfba1ac62015-08-26 15:19:13 -0700345
Junxiao Shi2bea5c42017-08-14 20:10:32 +0000346 this->face.processEvents(time::seconds(1)); // should not segfault
347 BOOST_CHECK(true);
Alexander Afanasyevfba1ac62015-08-26 15:19:13 -0700348}
349
Alexander Afanasyeve508f142015-09-01 15:14:45 -0700350BOOST_AUTO_TEST_CASE(LargeDelayBetweenFaceConstructorAndProcessEvents) // Bug #2742
351{
Junxiao Shi2bea5c42017-08-14 20:10:32 +0000352 std::this_thread::sleep_for(std::chrono::seconds(5)); // simulate setup workload
353 this->face.processEvents(time::seconds(1)); // should not throw
354 BOOST_CHECK(true);
Alexander Afanasyeve508f142015-09-01 15:14:45 -0700355}
356
Alexander Afanasyeva54d5a62017-02-11 19:01:34 -0800357BOOST_AUTO_TEST_CASE(ProcessEventsBlocksForeverWhenNothingScheduled) // Bug #3957
358{
Alexander Afanasyeva54d5a62017-02-11 19:01:34 -0800359 std::mutex m;
360 std::condition_variable cv;
361 bool processEventsFinished = false;
362
363 std::thread faceThread([&] {
Junxiao Shi2bea5c42017-08-14 20:10:32 +0000364 this->face.processEvents();
Alexander Afanasyeva54d5a62017-02-11 19:01:34 -0800365
Junxiao Shi2bea5c42017-08-14 20:10:32 +0000366 processEventsFinished = true;
367 std::lock_guard<std::mutex> lk(m);
368 cv.notify_one();
369 });
Alexander Afanasyeva54d5a62017-02-11 19:01:34 -0800370
371 {
372 std::unique_lock<std::mutex> lk(m);
373 cv.wait_for(lk, std::chrono::seconds(5), [&] { return processEventsFinished; });
374 }
375
376 BOOST_CHECK_EQUAL(processEventsFinished, true);
377 if (!processEventsFinished) {
Junxiao Shi2bea5c42017-08-14 20:10:32 +0000378 this->face.shutdown();
Alexander Afanasyeva54d5a62017-02-11 19:01:34 -0800379 }
380 faceThread.join();
381}
382
Junxiao Shi2bea5c42017-08-14 20:10:32 +0000383BOOST_AUTO_TEST_SUITE_END() // IoRoutine
384
385BOOST_AUTO_TEST_SUITE_END() // TestFace
Alexander Afanasyev0abb2da2014-01-30 18:07:57 -0800386
Alexander Afanasyeve4f8c3b2016-06-23 16:03:48 -0700387} // namespace tests
Alexander Afanasyev0abb2da2014-01-30 18:07:57 -0800388} // namespace ndn