blob: 3d0d5e2e9cc34840417368c264325be89bdd7911 [file] [log] [blame]
Weiwei Liu245d7912016-07-28 00:04:25 -07001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/**
3 * Copyright (c) 2016, Regents of the University of California,
4 * Colorado State University,
5 * University Pierre & Marie Curie, Sorbonne University.
6 *
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));
95 BOOST_REQUIRE_EQUAL(face.sentInterests.size(), 1);
96
97 for (uint64_t i = 1; i < nDataSegments - 1; ++i) {
98 face.receive(*makeDataWithSegment(i));
99 advanceClocks(io, time::nanoseconds(1));
100 BOOST_REQUIRE_CLOSE(aimdPipeline->m_cwnd - preCwnd, 1, 0.1);
101 preCwnd = aimdPipeline->m_cwnd;
102 }
103}
104
105BOOST_AUTO_TEST_CASE(CongestionAvoidance)
106{
107 nDataSegments = 8;
108 aimdPipeline->m_ssthresh = 4.0;
109 BOOST_REQUIRE_CLOSE(aimdPipeline->m_cwnd, 1, 0.1);
110
111 double preCwnd = aimdPipeline->m_cwnd;
112 runWithData(*makeDataWithSegment(0));
113 advanceClocks(io, time::nanoseconds(1));
114 BOOST_REQUIRE_EQUAL(face.sentInterests.size(), 1);
115
116 for (uint64_t i = 1; i < aimdPipeline->m_ssthresh; ++i) { // slow start
117 face.receive(*makeDataWithSegment(i));
118 advanceClocks(io, time::nanoseconds(1));
119 preCwnd = aimdPipeline->m_cwnd;
120 }
121
122 BOOST_REQUIRE_CLOSE(preCwnd, aimdPipeline->m_ssthresh, 0.1);
123
124 for (uint64_t i = aimdPipeline->m_ssthresh; i < nDataSegments - 1; ++i) { // congestion avoidance
125 face.receive(*makeDataWithSegment(i));
126 advanceClocks(io, time::nanoseconds(1));
127 BOOST_REQUIRE_CLOSE(aimdPipeline->m_cwnd - preCwnd, opt.aiStep / floor(aimdPipeline->m_cwnd), 0.1);
128 preCwnd = aimdPipeline->m_cwnd;
129 }
130}
131
132BOOST_AUTO_TEST_CASE(Timeout)
133{
134 nDataSegments = 8;
135 aimdPipeline->m_ssthresh = 4.0;
136 BOOST_REQUIRE_CLOSE(aimdPipeline->m_cwnd, 1, 0.1);
137
138 runWithData(*makeDataWithSegment(0));
139 advanceClocks(io, time::nanoseconds(1));
140 BOOST_REQUIRE_EQUAL(face.sentInterests.size(), 1);
141
142 // receive segment 1 and segment 2
143 for (uint64_t i = 1; i < 3; ++i) {
144 face.receive(*makeDataWithSegment(i));
145 advanceClocks(io, time::nanoseconds(1));
146 }
147
148 BOOST_REQUIRE_CLOSE(aimdPipeline->m_cwnd, 3, 0.1);
149 BOOST_REQUIRE_EQUAL(face.sentInterests.size(), 5); // request for segment 5 has been sent
150
151 advanceClocks(io, time::milliseconds(100));
152
153 // receive segment 4
154 face.receive(*makeDataWithSegment(4));
155 advanceClocks(io, time::nanoseconds(1));
156
157 // receive segment 5
158 face.receive(*makeDataWithSegment(5));
159 advanceClocks(io, time::nanoseconds(1));
160
161 BOOST_REQUIRE_CLOSE(aimdPipeline->m_cwnd, 4.25, 0.1);
162 BOOST_REQUIRE_EQUAL(face.sentInterests.size(), 7); // all the segment requests have been sent
163
164 // timeout segment 3
165 advanceClocks(io, time::milliseconds(150));
166
167 BOOST_REQUIRE_CLOSE(aimdPipeline->m_cwnd, 2.125, 0.1); // window size drop to 1/2 of previous size
168 BOOST_CHECK_EQUAL(aimdPipeline->m_retxQueue.size(), 1);
169
170 // receive segment 6, retransmit 3
171 face.receive(*makeDataWithSegment(6));
172 advanceClocks(io, time::nanoseconds(1));
173
174 BOOST_REQUIRE_CLOSE(aimdPipeline->m_cwnd, 2.625, 0.1); // congestion avoidance
175 BOOST_CHECK_EQUAL(aimdPipeline->m_retxQueue.size(), 0);
176 BOOST_CHECK_EQUAL(aimdPipeline->m_retxCount[3], 1);
177}
178
179BOOST_AUTO_TEST_CASE(Nack)
180{
181 nDataSegments = 5;
182 aimdPipeline->m_cwnd = 10.0;
183 runWithData(*makeDataWithSegment(0));
184 advanceClocks(io, time::nanoseconds(1));
185
186 face.receive(*makeDataWithSegment(1));
187 advanceClocks(io, time::nanoseconds(1));
188 BOOST_REQUIRE_EQUAL(face.sentInterests.size(), 4);
189
190 // receive a nack with NackReason::DUPLICATE for segment 2
191 auto nack1 = makeNack(face.sentInterests[1], lp::NackReason::DUPLICATE);
192 face.receive(nack1);
193 advanceClocks(io, time::nanoseconds(1));
194
195 // nack1 is ignored
196 BOOST_CHECK_EQUAL(hasFailed, false);
197 BOOST_CHECK_EQUAL(aimdPipeline->m_retxQueue.size(), 0);
198
199 // receive a nack with NackReason::CONGESTION for segment 3
200 auto nack2 = makeNack(face.sentInterests[2], lp::NackReason::CONGESTION);
201 face.receive(nack2);
202 advanceClocks(io, time::nanoseconds(1));
203
204 // segment 3 is retransmitted
205 BOOST_CHECK_EQUAL(aimdPipeline->m_retxCount[3], 1);
206
207 // receive a nack with NackReason::NONE for segment 4
208 auto nack3 = makeNack(face.sentInterests[3], lp::NackReason::NONE);
209 face.receive(nack3);
210 advanceClocks(io, time::nanoseconds(1));
211
212 // Other types of Nack will trigger a failure
213 BOOST_CHECK_EQUAL(hasFailed, true);
214}
215
216BOOST_AUTO_TEST_CASE(FinalBlockIdNotSetAtBeginning)
217{
218 nDataSegments = 4;
219 aimdPipeline->m_cwnd = 4;
220 runWithData(*makeDataWithSegment(0, false));
221 advanceClocks(io, time::nanoseconds(1));
222
223 // receive segment 1 without FinalBlockId
224 face.receive(*makeDataWithSegment(1, false));
225 advanceClocks(io, time::nanoseconds(1));
226
227 // interests for segment 1 - 6 have been sent
228 BOOST_CHECK_EQUAL(face.sentInterests.size(), 6);
229 BOOST_CHECK_EQUAL(aimdPipeline->m_hasFinalBlockId, false);
230 // pending interests: segment 2, 3, 4, 5, 6
231 BOOST_CHECK_EQUAL(face.getNPendingInterests(), 5);
232
233 // receive segment 2 with FinalBlockId
234 face.receive(*makeDataWithSegment(2));
235 advanceClocks(io, time::nanoseconds(1));
236 BOOST_CHECK_EQUAL(aimdPipeline->m_hasFinalBlockId, true);
237
238 // pending interests for segment 2, 4, 5, 6 haven been removed
239 BOOST_CHECK_EQUAL(face.getNPendingInterests(), 1);
240}
241
242BOOST_AUTO_TEST_CASE(FailureBeforeFinalBlockIdReceived)
243{
244 // failed to retrieve segNo while the FinalBlockId has not yet been
245 // set, and later received a FinalBlockId >= segNo, i.e. segNo is
246 // part of the content.
247
248 nDataSegments = 4;
249 aimdPipeline->m_cwnd = 4;
250 runWithData(*makeDataWithSegment(0, false));
251 advanceClocks(io, time::nanoseconds(1));
252
253 // receive segment 1 without FinalBlockId
254 face.receive(*makeDataWithSegment(1, false));
255 advanceClocks(io, time::nanoseconds(1));
256 // interests for segment 1 - 6 have been sent
257 BOOST_REQUIRE_EQUAL(face.sentInterests.size(), 6);
258
259 // receive nack with NackReason::NONE for segment 3
260 auto nack = makeNack(face.sentInterests[2], lp::NackReason::NONE);
261 face.receive(nack);
262 advanceClocks(io, time::nanoseconds(1));
263
264 // error not triggered
265 // pending interests for segment > 3 haven been removed
266 BOOST_CHECK_EQUAL(hasFailed, false);
267 BOOST_CHECK_EQUAL(face.getNPendingInterests(), 1);
268
269 // receive segment 2 with FinalBlockId
270 face.receive(*makeDataWithSegment(2));
271 advanceClocks(io, time::nanoseconds(1));
272
273 // error triggered since segment 3 is part of the content
274 BOOST_CHECK_EQUAL(hasFailed, true);
275}
276
277BOOST_AUTO_TEST_CASE(SpuriousFailureBeforeFinalBlockIdReceived)
278{
279 // failed to retrieve segNo while the FinalBlockId has not yet been
280 // set, and later received a FinalBlockId < segNo, i.e. segNo is
281 // not part of the content, and it was actually a spurious failure
282
283 nDataSegments = 4;
284 aimdPipeline->m_cwnd = 4;
285 runWithData(*makeDataWithSegment(0, false));
286 advanceClocks(io, time::nanoseconds(1));
287
288 // receive segment 1 without FinalBlockId
289 face.receive(*makeDataWithSegment(1, false));
290 advanceClocks(io, time::nanoseconds(1));
291 // interests for segment 1 - 6 have been sent
292 BOOST_REQUIRE_EQUAL(face.sentInterests.size(), 6);
293
294 // receive nack with NackReason::NONE for segment 4
295 auto nack= makeNack(face.sentInterests[3], lp::NackReason::NONE);
296 face.receive(nack);
297 advanceClocks(io, time::nanoseconds(1));
298
299 // error not triggered
300 // pending interests for segment > 4 haven been removed
301 BOOST_CHECK_EQUAL(hasFailed, false);
302 BOOST_CHECK_EQUAL(face.getNPendingInterests(), 2);
303
304 // receive segment 2 with FinalBlockId
305 face.receive(*makeDataWithSegment(2));
306 advanceClocks(io, time::nanoseconds(1));
307
308 // timeout segment 3
309 advanceClocks(io, time::milliseconds(250));
310
311 // segment 3 is retransmitted
312 BOOST_CHECK_EQUAL(aimdPipeline->m_retxCount[3], 1);
313
314 // receive segment 3
315 face.receive(*makeDataWithSegment(3));
316 advanceClocks(io, time::nanoseconds(1));
317
318 BOOST_CHECK_EQUAL(hasFailed, false);
319}
320
321BOOST_AUTO_TEST_SUITE_END() // TestPipelineInterestsAimd
322BOOST_AUTO_TEST_SUITE_END() // Chunks
323
324} // namespace tests
325} // namespace aimd
326} // namespace chunks
327} // namespace ndn