blob: 934dfcd2306c5f7ee08000bc754860875c15ed01 [file] [log] [blame]
Junxiao Shi80ee7cb2014-12-14 10:53:05 -07001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/**
3 * Copyright (c) 2014-2015, Regents of the University of California,
4 * Arizona Board of Regents,
5 * Colorado State University,
6 * University Pierre & Marie Curie, Sorbonne University,
7 * Washington University in St. Louis,
8 * Beijing Institute of Technology,
9 * The University of Memphis.
10 *
11 * This file is part of NFD (Named Data Networking Forwarding Daemon).
12 * See AUTHORS.md for complete list of NFD authors and contributors.
13 *
14 * NFD is free software: you can redistribute it and/or modify it under the terms
15 * of the GNU General Public License as published by the Free Software Foundation,
16 * either version 3 of the License, or (at your option) any later version.
17 *
18 * NFD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
19 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
20 * PURPOSE. See the GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License along with
23 * NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
24 */
25
26#include "fw/access-strategy.hpp"
27
28#include "tests/test-common.hpp"
29#include "topology-tester.hpp"
30
31namespace nfd {
32namespace tests {
33
34// This test suite tests AccessStrategy's behavior as a black box,
35// without accessing its internals.
36//
37// Many test assertions are qualitative rather than quantitative.
38// They capture the design highlights of the strategy without requiring a definite value,
39// so that the test suite is not fragile to minor changes in the strategy implementation.
40//
41// Topology graphes in this test suite are shown in ASCII art,
42// in a style similar to ns-3 and ndnSIM examples.
43// They are enclosed in multi-line comments, which is an intentional violation of
44// code style rule 3.25. This is necessary because some lines ends with '\' which
45// would cause "multi-line comment" compiler warning if '//' comments are used.
46
47BOOST_FIXTURE_TEST_SUITE(FwAccessStrategy, UnitTestTimeFixture)
48
49class TwoLaptopsFixture : public UnitTestTimeFixture
50{
51protected:
52 TwoLaptopsFixture()
53 {
54 /*
55 * +--------+
56 * +----->| router |<------+
57 * | +--------+ |
58 * 10ms | | 20ms
59 * v v
60 * +---------+ +---------+
61 * | laptopA | | laptopB |
62 * +---------+ +---------+
63 */
64
65 router = topo.addForwarder();
66 laptopA = topo.addForwarder();
67 laptopB = topo.addForwarder();
68
69 topo.setStrategy<fw::AccessStrategy>(router);
70
71 linkA = topo.addLink(time::milliseconds(10), {router, laptopA});
72 linkB = topo.addLink(time::milliseconds(20), {router, laptopB});
73 }
74
75protected:
76 TopologyTester topo;
77
78 TopologyNode router;
79 TopologyNode laptopA;
80 TopologyNode laptopB;
81 shared_ptr<TopologyLink> linkA;
82 shared_ptr<TopologyLink> linkB;
83};
84
85BOOST_FIXTURE_TEST_CASE(OneProducer, TwoLaptopsFixture)
86{
87 /*
88 * /------------------\
89 * | intervalConsumer |
90 * \------------------/
91 * ^ v
92 * | v /laptops/A
93 * |
94 * v
95 * /laptops << +--------+ >> /laptops
96 * +----->| router |<------+
97 * | +--------+ |
98 * 10ms | | 20ms
99 * v v
100 * +---------+ +---------+
101 * | laptopA | | laptopB |
102 * +---------+ +---------+
103 * ^ v
104 * | v /laptops/A
105 * v
106 * /--------------\
107 * | echoProducer |
108 * \--------------/
109 */
110
111 // two laptops have same prefix in router FIB
112 topo.registerPrefix(router, linkA->getFace(router), "ndn:/laptops");
113 topo.registerPrefix(router, linkB->getFace(router), "ndn:/laptops");
114
115 shared_ptr<TopologyAppLink> producer = topo.addAppFace(laptopA, "ndn:/laptops/A");
116 topo.addEchoProducer(*producer->getClientFace());
117
118 shared_ptr<TopologyAppLink> consumer = topo.addAppFace(router);
119 topo.addIntervalConsumer(*consumer->getClientFace(), "ndn:/laptops/A",
120 time::milliseconds(100), 100);
121
122 this->advanceClocks(time::milliseconds(5), time::seconds(12));
123
124 // most Interests should be satisfied, and few Interests can go to wrong laptop
125 BOOST_CHECK_GE(consumer->getForwarderFace()->m_sentDatas.size(), 97);
126 BOOST_CHECK_GE(linkA->getFace(router)->m_sentInterests.size(), 97);
127 BOOST_CHECK_LE(linkB->getFace(router)->m_sentInterests.size(), 5);
128}
129
130BOOST_FIXTURE_TEST_CASE(FastSlowProducer, TwoLaptopsFixture)
131{
132 /*
133 * /------------------\
134 * | intervalConsumer |
135 * \------------------/
136 * ^ v
137 * | v /laptops/BOTH
138 * |
139 * v
140 * /laptops << +--------+ >> /laptops
141 * +----->| router |<------+
142 * | +--------+ |
143 * 10ms | | 20ms
144 * v v
145 * +---------+ +---------+
146 * | laptopA | | laptopB |
147 * +---------+ +---------+
148 * ^ v ^ v
149 * | v /laptops/BOTH | v /laptops/BOTH
150 * v v
151 * /--------------\ /--------------\
152 * | echoProducer | | echoProducer |
153 * \--------------/ \--------------/
154 */
155
156 // two laptops have same prefix in router FIB
157 topo.registerPrefix(router, linkA->getFace(router), "ndn:/laptops");
158 topo.registerPrefix(router, linkB->getFace(router), "ndn:/laptops");
159
160 shared_ptr<TopologyAppLink> producerA = topo.addAppFace(laptopA, "ndn:/laptops/BOTH");
161 topo.addEchoProducer(*producerA->getClientFace());
162 shared_ptr<TopologyAppLink> producerB = topo.addAppFace(laptopB, "ndn:/laptops/BOTH");
163 topo.addEchoProducer(*producerB->getClientFace());
164
165 shared_ptr<TopologyAppLink> consumer = topo.addAppFace(router);
166 topo.addIntervalConsumer(*consumer->getClientFace(), "ndn:/laptops/BOTH",
167 time::milliseconds(100), 100);
168
169 this->advanceClocks(time::milliseconds(5), time::seconds(12));
170
171 // most Interests should be satisfied, and few Interests can go to slower laptopB
172 BOOST_CHECK_GE(consumer->getForwarderFace()->m_sentDatas.size(), 97);
173 BOOST_CHECK_GE(linkA->getFace(router)->m_sentInterests.size(), 90);
174 BOOST_CHECK_LE(linkB->getFace(router)->m_sentInterests.size(), 15);
175}
176
177BOOST_FIXTURE_TEST_CASE(ProducerMobility, TwoLaptopsFixture)
178{
179 /*
180 * /------------------\ /------------------\
181 * | intervalConsumer | | intervalConsumer |
182 * \------------------/ A \------------------/
183 * ^ v f ^ v
184 * | v /laptops/M t | v /laptops/M
185 * | e |
186 * v r v
187 * /laptops << +--------+ >> /laptops /laptops << +--------+ >> /laptops
188 * +----->| router |<------+ 6 +----->| router |<------+
189 * | +--------+ | | +--------+ |
190 * 10ms | | 20ms === s ==> 10ms | | 20ms
191 * v v e v v
192 * +---------+ +---------+ c +---------+ +---------+
193 * | laptopA | | laptopB | o | laptopA | | laptopB |
194 * +---------+ +---------+ n +---------+ +---------+
195 * ^ v d v ^
196 * | v /laptops/M s /laptops/M v |
197 * v v
198 * /--------------\ /--------------\
199 * | echoProducer | | echoProducer |
200 * \--------------/ \--------------/
201 */
202
203 // two laptops have same prefix in router FIB
204 topo.registerPrefix(router, linkA->getFace(router), "ndn:/laptops");
205 topo.registerPrefix(router, linkB->getFace(router), "ndn:/laptops");
206
207 shared_ptr<TopologyAppLink> producerA = topo.addAppFace(laptopA, "ndn:/laptops/M");
208 topo.addEchoProducer(*producerA->getClientFace());
209 shared_ptr<TopologyAppLink> producerB = topo.addAppFace(laptopB, "ndn:/laptops/M");
210 topo.addEchoProducer(*producerB->getClientFace());
211
212 shared_ptr<TopologyAppLink> consumer = topo.addAppFace(router);
213 topo.addIntervalConsumer(*consumer->getClientFace(), "ndn:/laptops/M",
214 time::milliseconds(100), 100);
215
216 // producer is initially on laptopA
217 producerB->fail();
218 this->advanceClocks(time::milliseconds(5), time::seconds(6));
219
220 // few Interests can go to laptopB
221 BOOST_CHECK_LE(linkB->getFace(router)->m_sentInterests.size(), 5);
222
223 // producer moves to laptopB
224 producerA->fail();
225 producerB->recover();
226 linkA->getFace(router)->m_sentInterests.clear();
227 this->advanceClocks(time::milliseconds(5), time::seconds(6));
228
229 // few additional Interests can go to laptopA
230 BOOST_CHECK_LE(linkA->getFace(router)->m_sentInterests.size(), 5);
231
232 // most Interests should be satisfied
233 BOOST_CHECK_GE(consumer->getForwarderFace()->m_sentDatas.size(), 97);
234}
235
236BOOST_FIXTURE_TEST_CASE(Bidirectional, TwoLaptopsFixture)
237{
238 /*
239 * /laptops << +--------+ >> /laptops
240 * +----->| router |<------+
241 * | +--------+ |
242 * ^ 10ms | | 20ms ^
243 * / ^ v v ^ /
244 * +---------+ +---------+
245 * +----->| laptopA | | laptopB |<------------+
246 * | +---------+ +---------+ |
247 * | ^ v /laptops/A ^ v /laptops/B |
248 * ^ | | v | v | ^
249 * /laptops/B ^ v v v v ^ /laptops/A
250 * /------------------\ /--------------\ /--------------\ /------------------\
251 * | intervalConsumer | | echoProducer | | echoProducer | | intervalConsumer |
252 * \------------------/ \--------------/ \--------------/ \------------------/
253 */
254
255 // laptops have default routes toward the router
256 topo.registerPrefix(laptopA, linkA->getFace(laptopA), "ndn:/");
257 topo.registerPrefix(laptopB, linkB->getFace(laptopB), "ndn:/");
258
259 // two laptops have same prefix in router FIB
260 topo.registerPrefix(router, linkA->getFace(router), "ndn:/laptops");
261 topo.registerPrefix(router, linkB->getFace(router), "ndn:/laptops");
262
263 shared_ptr<TopologyAppLink> producerA = topo.addAppFace(laptopA, "ndn:/laptops/A");
264 topo.addEchoProducer(*producerA->getClientFace());
265 shared_ptr<TopologyAppLink> producerB = topo.addAppFace(laptopB, "ndn:/laptops/B");
266 topo.addEchoProducer(*producerB->getClientFace());
267
268 shared_ptr<TopologyAppLink> consumerAB = topo.addAppFace(laptopA);
269 topo.addIntervalConsumer(*consumerAB->getClientFace(), "ndn:/laptops/B",
270 time::milliseconds(100), 100);
271 shared_ptr<TopologyAppLink> consumerBA = topo.addAppFace(laptopB);
272 topo.addIntervalConsumer(*consumerBA->getClientFace(), "ndn:/laptops/A",
273 time::milliseconds(100), 100);
274
275 this->advanceClocks(time::milliseconds(5), time::seconds(12));
276
277 // most Interests should be satisfied
278 BOOST_CHECK_GE(consumerAB->getForwarderFace()->m_sentDatas.size(), 97);
279 BOOST_CHECK_GE(consumerBA->getForwarderFace()->m_sentDatas.size(), 97);
280}
281
282BOOST_FIXTURE_TEST_CASE(PacketLoss, TwoLaptopsFixture)
283{
284 /*
285 * test case Interests
286 * |
287 * v
288 * +--------+
289 * | router |
290 * +--------+
291 * | v
292 * 10ms | v /laptops
293 * v
294 * +---------+
295 * | laptopA |
296 * +---------+
297 * ^ v
298 * | v /laptops/A
299 * v
300 * /--------------\
301 * | echoProducer |
302 * \--------------/
303 */
304
305 // laptopA has prefix in router FIB; laptopB is unused in this test case
306 topo.registerPrefix(router, linkA->getFace(router), "ndn:/laptops");
307
308 shared_ptr<TopologyAppLink> producerA = topo.addAppFace(laptopA, "ndn:/laptops/A");
309 topo.addEchoProducer(*producerA->getClientFace());
310
311 shared_ptr<TopologyAppLink> consumer = topo.addAppFace(router);
312
313 // Interest 1 completes normally
314 shared_ptr<Interest> interest1 = makeInterest("ndn:/laptops/A/1");
315 bool hasData1 = false;
316 consumer->getClientFace()->expressInterest(*interest1,
317 bind([&hasData1] { hasData1 = true; }));
318 this->advanceClocks(time::milliseconds(5), time::seconds(1));
319 BOOST_CHECK_EQUAL(hasData1, true);
320
321 // Interest 2 experiences a packet loss on initial transmission
322 shared_ptr<Interest> interest2a = makeInterest("ndn:/laptops/A/2");
323 bool hasData2a = false, hasTimeout2a = false;
324 consumer->getClientFace()->expressInterest(*interest2a,
325 bind([&hasData2a] { hasData2a = true; }),
326 bind([&hasTimeout2a] { hasTimeout2a = true; }));
327 producerA->fail();
328 this->advanceClocks(time::milliseconds(5), time::milliseconds(60));
329 BOOST_CHECK_EQUAL(hasData2a, false);
330 BOOST_CHECK_EQUAL(hasTimeout2a, false);
331
332 // Interest 2 retransmission is suppressed
333 shared_ptr<Interest> interest2b = makeInterest("ndn:/laptops/A/2");
334 bool hasData2b = false;
335 consumer->getClientFace()->expressInterest(*interest2b,
336 bind([&hasData2b] { hasData2b = true; }));
337 producerA->recover();
338 this->advanceClocks(time::milliseconds(5), time::seconds(1));
339 BOOST_CHECK_EQUAL(hasData2b, false);
340
341 // Interest 2 retransmission gets through, and is answered
342 shared_ptr<Interest> interest2c = makeInterest("ndn:/laptops/A/2");
343 bool hasData2c = false;
344 consumer->getClientFace()->expressInterest(*interest2c,
345 bind([&hasData2c] { hasData2c = true; }));
346 this->advanceClocks(time::milliseconds(5), time::seconds(1));
347 BOOST_CHECK_EQUAL(hasData2c, true);
348}
349
350BOOST_AUTO_TEST_SUITE_END()
351
352} // namespace tests
353} // namespace nfd