blob: e7856c468fda5e2b664bbe00d65ae7c321b1fd91 [file] [log] [blame]
Weiwei Liu245d7912016-07-28 00:04:25 -07001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
Davide Pesaventoe9c69852017-11-04 18:08:37 -04002/*
Davide Pesavento958896e2017-01-19 00:52:04 -05003 * Copyright (c) 2016-2017, Regents of the University of California,
4 * Colorado State University,
5 * University Pierre & Marie Curie, Sorbonne University.
Weiwei Liu245d7912016-07-28 00:04:25 -07006 *
7 * This file is part of ndn-tools (Named Data Networking Essential Tools).
8 * See AUTHORS.md for complete list of ndn-tools authors and contributors.
9 *
10 * ndn-tools is free software: you can redistribute it and/or modify it under the terms
11 * of the GNU General Public License as published by the Free Software Foundation,
12 * either version 3 of the License, or (at your option) any later version.
13 *
14 * ndn-tools is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
15 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
16 * PURPOSE. See the GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License along with
19 * ndn-tools, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
20 *
21 * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
22 *
23 * @author Weiwei Liu
24 */
25
26#include "tools/chunks/catchunks/pipeline-interests-aimd.hpp"
27#include "tools/chunks/catchunks/options.hpp"
28
29#include "pipeline-interests-fixture.hpp"
30
31namespace ndn {
32namespace chunks {
33namespace aimd {
34namespace tests {
35
36using namespace ndn::tests;
37
38class PipelineInterestAimdFixture : public ndn::chunks::tests::PipelineInterestsFixture
39{
40public:
41 PipelineInterestAimdFixture()
42 : PipelineInterestsFixture()
43 , opt(makePipelineOptions())
44 , rttEstimator(makeRttEstimatorOptions())
45 {
46 auto pline = make_unique<PipelineInterestsAimd>(face, rttEstimator, opt);
47 aimdPipeline = pline.get();
48 setPipeline(std::move(pline));
49 }
50
51private:
52 static PipelineInterestsAimdOptions
53 makePipelineOptions()
54 {
55 PipelineInterestsAimdOptions pipelineOptions;
56 pipelineOptions.disableCwa = false;
57 pipelineOptions.resetCwndToInit = false;
58 pipelineOptions.initCwnd = 1.0;
59 pipelineOptions.aiStep = 1.0;
60 pipelineOptions.mdCoef = 0.5;
61 pipelineOptions.initSsthresh = std::numeric_limits<int>::max();
62 return pipelineOptions;
63 }
64
65 static RttEstimator::Options
66 makeRttEstimatorOptions()
67 {
68 RttEstimator::Options rttOptions;
69 rttOptions.alpha = 0.125;
70 rttOptions.beta = 0.25;
71 rttOptions.k = 4;
72 rttOptions.minRto = Milliseconds(200);
73 rttOptions.maxRto = Milliseconds(4000);
74 return rttOptions;
75 }
76
77protected:
78 PipelineInterestsAimdOptions opt;
79 RttEstimator rttEstimator;
80 PipelineInterestsAimd* aimdPipeline;
81};
82
83BOOST_AUTO_TEST_SUITE(Chunks)
84BOOST_FIXTURE_TEST_SUITE(TestPipelineInterestsAimd, PipelineInterestAimdFixture)
85
86BOOST_AUTO_TEST_CASE(SlowStart)
87{
88 nDataSegments = 4;
89 aimdPipeline->m_ssthresh = 8.0;
90 BOOST_REQUIRE_CLOSE(aimdPipeline->m_cwnd, 1, 0.1);
91
92 double preCwnd = aimdPipeline->m_cwnd;
93 runWithData(*makeDataWithSegment(0));
94 advanceClocks(io, time::nanoseconds(1));
Davide Pesaventoe9c69852017-11-04 18:08:37 -040095 BOOST_CHECK_EQUAL(face.sentInterests.size(), 1);
Weiwei Liu245d7912016-07-28 00:04:25 -070096
97 for (uint64_t i = 1; i < nDataSegments - 1; ++i) {
98 face.receive(*makeDataWithSegment(i));
99 advanceClocks(io, time::nanoseconds(1));
Davide Pesaventoe9c69852017-11-04 18:08:37 -0400100 BOOST_CHECK_CLOSE(aimdPipeline->m_cwnd - preCwnd, 1, 0.1);
Weiwei Liu245d7912016-07-28 00:04:25 -0700101 preCwnd = aimdPipeline->m_cwnd;
102 }
Davide Pesaventoe9c69852017-11-04 18:08:37 -0400103
104 BOOST_CHECK_EQUAL(pipeline->m_nReceived, nDataSegments - 1);
Weiwei Liu245d7912016-07-28 00:04:25 -0700105}
106
107BOOST_AUTO_TEST_CASE(CongestionAvoidance)
108{
109 nDataSegments = 8;
110 aimdPipeline->m_ssthresh = 4.0;
111 BOOST_REQUIRE_CLOSE(aimdPipeline->m_cwnd, 1, 0.1);
112
113 double preCwnd = aimdPipeline->m_cwnd;
114 runWithData(*makeDataWithSegment(0));
115 advanceClocks(io, time::nanoseconds(1));
Davide Pesaventoe9c69852017-11-04 18:08:37 -0400116 BOOST_CHECK_EQUAL(face.sentInterests.size(), 1);
Weiwei Liu245d7912016-07-28 00:04:25 -0700117
118 for (uint64_t i = 1; i < aimdPipeline->m_ssthresh; ++i) { // slow start
119 face.receive(*makeDataWithSegment(i));
120 advanceClocks(io, time::nanoseconds(1));
121 preCwnd = aimdPipeline->m_cwnd;
122 }
123
Davide Pesaventoe9c69852017-11-04 18:08:37 -0400124 BOOST_CHECK_CLOSE(preCwnd, aimdPipeline->m_ssthresh, 0.1);
Weiwei Liu245d7912016-07-28 00:04:25 -0700125
126 for (uint64_t i = aimdPipeline->m_ssthresh; i < nDataSegments - 1; ++i) { // congestion avoidance
127 face.receive(*makeDataWithSegment(i));
128 advanceClocks(io, time::nanoseconds(1));
Davide Pesaventoe9c69852017-11-04 18:08:37 -0400129 BOOST_CHECK_CLOSE(aimdPipeline->m_cwnd - preCwnd, opt.aiStep / floor(aimdPipeline->m_cwnd), 0.1);
Weiwei Liu245d7912016-07-28 00:04:25 -0700130 preCwnd = aimdPipeline->m_cwnd;
131 }
Davide Pesaventoe9c69852017-11-04 18:08:37 -0400132
133 BOOST_CHECK_EQUAL(pipeline->m_nReceived, nDataSegments - 1);
Weiwei Liu245d7912016-07-28 00:04:25 -0700134}
135
136BOOST_AUTO_TEST_CASE(Timeout)
137{
138 nDataSegments = 8;
139 aimdPipeline->m_ssthresh = 4.0;
140 BOOST_REQUIRE_CLOSE(aimdPipeline->m_cwnd, 1, 0.1);
141
142 runWithData(*makeDataWithSegment(0));
143 advanceClocks(io, time::nanoseconds(1));
Davide Pesaventoe9c69852017-11-04 18:08:37 -0400144 BOOST_CHECK_EQUAL(face.sentInterests.size(), 1);
Weiwei Liu245d7912016-07-28 00:04:25 -0700145
146 // receive segment 1 and segment 2
147 for (uint64_t i = 1; i < 3; ++i) {
148 face.receive(*makeDataWithSegment(i));
149 advanceClocks(io, time::nanoseconds(1));
150 }
151
Davide Pesaventoe9c69852017-11-04 18:08:37 -0400152 BOOST_CHECK_EQUAL(pipeline->m_nReceived, 3);
153 BOOST_CHECK_CLOSE(aimdPipeline->m_cwnd, 3, 0.1);
154 BOOST_CHECK_EQUAL(face.sentInterests.size(), 5); // request for segment 5 has been sent
Weiwei Liu245d7912016-07-28 00:04:25 -0700155
156 advanceClocks(io, time::milliseconds(100));
157
158 // receive segment 4
159 face.receive(*makeDataWithSegment(4));
160 advanceClocks(io, time::nanoseconds(1));
161
162 // receive segment 5
163 face.receive(*makeDataWithSegment(5));
164 advanceClocks(io, time::nanoseconds(1));
165
Davide Pesaventoe9c69852017-11-04 18:08:37 -0400166 BOOST_CHECK_EQUAL(pipeline->m_nReceived, 5);
167 BOOST_CHECK_CLOSE(aimdPipeline->m_cwnd, 4.25, 0.1);
168 BOOST_CHECK_EQUAL(face.sentInterests.size(), 7); // all the segment requests have been sent
Weiwei Liu245d7912016-07-28 00:04:25 -0700169
170 // timeout segment 3
171 advanceClocks(io, time::milliseconds(150));
172
Davide Pesaventoe9c69852017-11-04 18:08:37 -0400173 BOOST_CHECK_EQUAL(pipeline->m_nReceived, 5);
174 BOOST_CHECK_CLOSE(aimdPipeline->m_cwnd, 2.125, 0.1); // window size drop to 1/2 of previous size
Weiwei Liu245d7912016-07-28 00:04:25 -0700175 BOOST_CHECK_EQUAL(aimdPipeline->m_retxQueue.size(), 1);
176
177 // receive segment 6, retransmit 3
178 face.receive(*makeDataWithSegment(6));
179 advanceClocks(io, time::nanoseconds(1));
180
Davide Pesaventoe9c69852017-11-04 18:08:37 -0400181 BOOST_CHECK_EQUAL(pipeline->m_nReceived, 6);
182 BOOST_CHECK_CLOSE(aimdPipeline->m_cwnd, 2.625, 0.1); // congestion avoidance
Weiwei Liu245d7912016-07-28 00:04:25 -0700183 BOOST_CHECK_EQUAL(aimdPipeline->m_retxQueue.size(), 0);
184 BOOST_CHECK_EQUAL(aimdPipeline->m_retxCount[3], 1);
185}
186
187BOOST_AUTO_TEST_CASE(Nack)
188{
189 nDataSegments = 5;
190 aimdPipeline->m_cwnd = 10.0;
191 runWithData(*makeDataWithSegment(0));
192 advanceClocks(io, time::nanoseconds(1));
193
194 face.receive(*makeDataWithSegment(1));
195 advanceClocks(io, time::nanoseconds(1));
Davide Pesaventoe9c69852017-11-04 18:08:37 -0400196
197 BOOST_CHECK_EQUAL(pipeline->m_nReceived, 2);
Weiwei Liu245d7912016-07-28 00:04:25 -0700198 BOOST_REQUIRE_EQUAL(face.sentInterests.size(), 4);
199
200 // receive a nack with NackReason::DUPLICATE for segment 2
201 auto nack1 = makeNack(face.sentInterests[1], lp::NackReason::DUPLICATE);
202 face.receive(nack1);
203 advanceClocks(io, time::nanoseconds(1));
204
205 // nack1 is ignored
206 BOOST_CHECK_EQUAL(hasFailed, false);
Davide Pesaventoe9c69852017-11-04 18:08:37 -0400207 BOOST_CHECK_EQUAL(pipeline->m_nReceived, 2);
Weiwei Liu245d7912016-07-28 00:04:25 -0700208 BOOST_CHECK_EQUAL(aimdPipeline->m_retxQueue.size(), 0);
209
210 // receive a nack with NackReason::CONGESTION for segment 3
211 auto nack2 = makeNack(face.sentInterests[2], lp::NackReason::CONGESTION);
212 face.receive(nack2);
213 advanceClocks(io, time::nanoseconds(1));
214
215 // segment 3 is retransmitted
216 BOOST_CHECK_EQUAL(aimdPipeline->m_retxCount[3], 1);
217
218 // receive a nack with NackReason::NONE for segment 4
219 auto nack3 = makeNack(face.sentInterests[3], lp::NackReason::NONE);
220 face.receive(nack3);
221 advanceClocks(io, time::nanoseconds(1));
222
223 // Other types of Nack will trigger a failure
224 BOOST_CHECK_EQUAL(hasFailed, true);
Davide Pesaventoe9c69852017-11-04 18:08:37 -0400225 BOOST_CHECK_EQUAL(pipeline->m_nReceived, 2);
Weiwei Liu245d7912016-07-28 00:04:25 -0700226}
227
228BOOST_AUTO_TEST_CASE(FinalBlockIdNotSetAtBeginning)
229{
230 nDataSegments = 4;
231 aimdPipeline->m_cwnd = 4;
232 runWithData(*makeDataWithSegment(0, false));
233 advanceClocks(io, time::nanoseconds(1));
234
235 // receive segment 1 without FinalBlockId
236 face.receive(*makeDataWithSegment(1, false));
237 advanceClocks(io, time::nanoseconds(1));
238
239 // interests for segment 1 - 6 have been sent
240 BOOST_CHECK_EQUAL(face.sentInterests.size(), 6);
Davide Pesaventoe9c69852017-11-04 18:08:37 -0400241 BOOST_CHECK_EQUAL(pipeline->m_nReceived, 2);
Weiwei Liu245d7912016-07-28 00:04:25 -0700242 BOOST_CHECK_EQUAL(aimdPipeline->m_hasFinalBlockId, false);
243 // pending interests: segment 2, 3, 4, 5, 6
244 BOOST_CHECK_EQUAL(face.getNPendingInterests(), 5);
245
246 // receive segment 2 with FinalBlockId
247 face.receive(*makeDataWithSegment(2));
248 advanceClocks(io, time::nanoseconds(1));
Davide Pesaventoe9c69852017-11-04 18:08:37 -0400249 BOOST_CHECK_EQUAL(pipeline->m_nReceived, 3);
Weiwei Liu245d7912016-07-28 00:04:25 -0700250 BOOST_CHECK_EQUAL(aimdPipeline->m_hasFinalBlockId, true);
251
252 // pending interests for segment 2, 4, 5, 6 haven been removed
253 BOOST_CHECK_EQUAL(face.getNPendingInterests(), 1);
254}
255
256BOOST_AUTO_TEST_CASE(FailureBeforeFinalBlockIdReceived)
257{
258 // failed to retrieve segNo while the FinalBlockId has not yet been
259 // set, and later received a FinalBlockId >= segNo, i.e. segNo is
260 // part of the content.
261
262 nDataSegments = 4;
263 aimdPipeline->m_cwnd = 4;
264 runWithData(*makeDataWithSegment(0, false));
265 advanceClocks(io, time::nanoseconds(1));
266
267 // receive segment 1 without FinalBlockId
268 face.receive(*makeDataWithSegment(1, false));
269 advanceClocks(io, time::nanoseconds(1));
270 // interests for segment 1 - 6 have been sent
271 BOOST_REQUIRE_EQUAL(face.sentInterests.size(), 6);
272
273 // receive nack with NackReason::NONE for segment 3
274 auto nack = makeNack(face.sentInterests[2], lp::NackReason::NONE);
275 face.receive(nack);
276 advanceClocks(io, time::nanoseconds(1));
277
278 // error not triggered
279 // pending interests for segment > 3 haven been removed
280 BOOST_CHECK_EQUAL(hasFailed, false);
281 BOOST_CHECK_EQUAL(face.getNPendingInterests(), 1);
282
283 // receive segment 2 with FinalBlockId
284 face.receive(*makeDataWithSegment(2));
285 advanceClocks(io, time::nanoseconds(1));
286
287 // error triggered since segment 3 is part of the content
288 BOOST_CHECK_EQUAL(hasFailed, true);
289}
290
291BOOST_AUTO_TEST_CASE(SpuriousFailureBeforeFinalBlockIdReceived)
292{
293 // failed to retrieve segNo while the FinalBlockId has not yet been
294 // set, and later received a FinalBlockId < segNo, i.e. segNo is
295 // not part of the content, and it was actually a spurious failure
296
297 nDataSegments = 4;
298 aimdPipeline->m_cwnd = 4;
299 runWithData(*makeDataWithSegment(0, false));
300 advanceClocks(io, time::nanoseconds(1));
301
302 // receive segment 1 without FinalBlockId
303 face.receive(*makeDataWithSegment(1, false));
304 advanceClocks(io, time::nanoseconds(1));
305 // interests for segment 1 - 6 have been sent
306 BOOST_REQUIRE_EQUAL(face.sentInterests.size(), 6);
307
308 // receive nack with NackReason::NONE for segment 4
Davide Pesavento958896e2017-01-19 00:52:04 -0500309 auto nack = makeNack(face.sentInterests[3], lp::NackReason::NONE);
Weiwei Liu245d7912016-07-28 00:04:25 -0700310 face.receive(nack);
311 advanceClocks(io, time::nanoseconds(1));
312
313 // error not triggered
Davide Pesavento958896e2017-01-19 00:52:04 -0500314 // pending interests for segment > 4 have been removed
Weiwei Liu245d7912016-07-28 00:04:25 -0700315 BOOST_CHECK_EQUAL(hasFailed, false);
316 BOOST_CHECK_EQUAL(face.getNPendingInterests(), 2);
317
318 // receive segment 2 with FinalBlockId
319 face.receive(*makeDataWithSegment(2));
320 advanceClocks(io, time::nanoseconds(1));
321
322 // timeout segment 3
Davide Pesavento958896e2017-01-19 00:52:04 -0500323 advanceClocks(io, time::seconds(1));
Weiwei Liu245d7912016-07-28 00:04:25 -0700324
325 // segment 3 is retransmitted
326 BOOST_CHECK_EQUAL(aimdPipeline->m_retxCount[3], 1);
327
328 // receive segment 3
329 face.receive(*makeDataWithSegment(3));
330 advanceClocks(io, time::nanoseconds(1));
331
332 BOOST_CHECK_EQUAL(hasFailed, false);
333}
334
335BOOST_AUTO_TEST_SUITE_END() // TestPipelineInterestsAimd
336BOOST_AUTO_TEST_SUITE_END() // Chunks
337
338} // namespace tests
339} // namespace aimd
340} // namespace chunks
341} // namespace ndn