blob: 89384830ad7584516405924cf919d29ee69b43f2 [file] [log] [blame]
Ashlesh Gawande0b2897e2018-06-20 14:40:47 -05001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/*
Ashlesh Gawande0cf4b602019-01-18 15:58:17 -06003 * Copyright (c) 2014-2019, The University of Memphis
Ashlesh Gawande0b2897e2018-06-20 14:40:47 -05004 *
5 * This file is part of PSync.
6 * See AUTHORS.md for complete list of PSync authors and contributors.
7 *
8 * PSync is free software: you can redistribute it and/or modify it under the terms
Ashlesh Gawande0cf4b602019-01-18 15:58:17 -06009 * of the GNU Lesser General Public License as published by the Free Software Foundation,
Ashlesh Gawande0b2897e2018-06-20 14:40:47 -050010 * either version 3 of the License, or (at your option) any later version.
11 *
12 * PSync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
13 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
Ashlesh Gawande0cf4b602019-01-18 15:58:17 -060014 * PURPOSE. See the GNU Lesser General Public License for more details.
Ashlesh Gawande0b2897e2018-06-20 14:40:47 -050015 *
Ashlesh Gawande0cf4b602019-01-18 15:58:17 -060016 * You should have received a copy of the GNU Lesser General Public License along with
Ashlesh Gawande0b2897e2018-06-20 14:40:47 -050017 * PSync, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
18 **/
19
Ashlesh Gawande78b94ad2018-12-13 15:29:19 -060020#include "PSync/partial-producer.hpp"
21#include "PSync/consumer.hpp"
Ashlesh Gawande0b2897e2018-06-20 14:40:47 -050022#include "unit-test-time-fixture.hpp"
23
24#include <boost/test/unit_test.hpp>
25#include <ndn-cxx/name.hpp>
26#include <ndn-cxx/util/dummy-client-face.hpp>
27
Ashlesh Gawande0b2897e2018-06-20 14:40:47 -050028namespace psync {
29
30using namespace ndn;
31using namespace std;
32
33class PartialSyncFixture : public tests::UnitTestTimeFixture
34{
35public:
36 PartialSyncFixture()
37 : face(io, {true, true})
38 , syncPrefix("psync")
39 , userPrefix("testUser-0")
40 , numHelloDataRcvd(0)
41 , numSyncDataRcvd(0)
42 {
43 producer = make_shared<PartialProducer>(40, face, syncPrefix, userPrefix);
44 addUserNodes("testUser", 10);
45 }
46
Ashlesh Gawandeec43b362018-08-01 15:15:01 -050047 ~PartialSyncFixture()
48 {
49 for (auto consumer : consumers) {
50 if (consumer) {
51 consumer->stop();
52 }
53 }
54 }
55
Ashlesh Gawande0b2897e2018-06-20 14:40:47 -050056 void
Ashlesh Gawandeec43b362018-08-01 15:15:01 -050057 addConsumer(int id, const vector<string>& subscribeTo, bool linkToProducer = true)
Ashlesh Gawande0b2897e2018-06-20 14:40:47 -050058 {
59 consumerFaces[id] = make_shared<util::DummyClientFace>(io, util::DummyClientFace::Options{true, true});
60
Ashlesh Gawandeec43b362018-08-01 15:15:01 -050061 if (linkToProducer) {
62 face.linkTo(*consumerFaces[id]);
63 }
Ashlesh Gawande0b2897e2018-06-20 14:40:47 -050064
65 consumers[id] = make_shared<Consumer>(syncPrefix, *consumerFaces[id],
66 [&, id] (const vector<Name>& availableSubs)
67 {
68 numHelloDataRcvd++;
Ashlesh Gawandedeb73f82018-08-09 11:08:02 -050069 BOOST_CHECK(checkSubList(availableSubs));
Ashlesh Gawande0b2897e2018-06-20 14:40:47 -050070
71 checkIBFUpdated(id);
72
73 for (const auto& sub : subscribeTo) {
74 consumers[id]->addSubscription(sub);
75 }
76 consumers[id]->sendSyncInterest();
77 },
78 [&, id] (const std::vector<MissingDataInfo>& updates) {
79 numSyncDataRcvd++;
80
81 checkIBFUpdated(id);
82
83 for (const auto& update : updates) {
84 BOOST_CHECK(consumers[id]->isSubscribed(update.prefix));
85 BOOST_CHECK_EQUAL(oldSeqMap.at(update.prefix) + 1, update.lowSeq);
86 BOOST_CHECK_EQUAL(producer->m_prefixes.at(update.prefix), update.highSeq);
87 BOOST_CHECK_EQUAL(consumers[id]->getSeqNo(update.prefix).value(), update.highSeq);
88 }
89 }, 40, 0.001);
90
91 advanceClocks(ndn::time::milliseconds(10));
92 }
93
94 void
95 checkIBFUpdated(int id)
96 {
97 Name emptyName;
98 producer->m_iblt.appendToName(emptyName);
99 BOOST_CHECK_EQUAL(consumers[id]->m_iblt, emptyName);
100 }
101
102 bool
103 checkSubList(const vector<Name>& availableSubs)
104 {
105 for (const auto& prefix : producer->m_prefixes ) {
Ashlesh Gawandedeb73f82018-08-09 11:08:02 -0500106 if (std::find(availableSubs.begin(), availableSubs.end(), prefix.first) == availableSubs.end()) {
107 return false;
Ashlesh Gawande0b2897e2018-06-20 14:40:47 -0500108 }
109 }
110 return true;
111 }
112
113 void
114 addUserNodes(const std::string& prefix, int numOfUserNodes)
115 {
116 // zeroth is added through constructor
117 for (int i = 1; i < numOfUserNodes; i++) {
118 producer->addUserNode(prefix + "-" + to_string(i));
119 }
120 }
121
122 void
123 publishUpdateFor(const std::string& prefix)
124 {
125 oldSeqMap = producer->m_prefixes;
126 producer->publishName(prefix);
127 advanceClocks(ndn::time::milliseconds(10));
128 }
129
130 void
131 updateSeqFor(const std::string& prefix, uint64_t seq)
132 {
133 oldSeqMap = producer->m_prefixes;
134 producer->updateSeqNo(prefix, seq);
135 }
136
137 util::DummyClientFace face;
138 Name syncPrefix;
139 Name userPrefix;
140
141 shared_ptr<PartialProducer> producer;
142 std::map <ndn::Name, uint64_t> oldSeqMap;
143
144 shared_ptr<Consumer> consumers[3];
145 shared_ptr<util::DummyClientFace> consumerFaces[3];
146 int numHelloDataRcvd;
147 int numSyncDataRcvd;
148};
149
150BOOST_FIXTURE_TEST_SUITE(PartialSync, PartialSyncFixture)
151
152BOOST_AUTO_TEST_CASE(Simple)
153{
154 vector<string> subscribeTo{"testUser-2", "testUser-4", "testUser-6"};
155 addConsumer(0, subscribeTo);
156
157 consumers[0]->sendHelloInterest();
158 advanceClocks(ndn::time::milliseconds(10));
159 BOOST_CHECK_EQUAL(numHelloDataRcvd, 1);
160
161 publishUpdateFor("testUser-2");
162 BOOST_CHECK_EQUAL(numSyncDataRcvd, 1);
163 publishUpdateFor("testUser-3");
164 BOOST_CHECK_EQUAL(numSyncDataRcvd, 1);
165 publishUpdateFor("testUser-2");
166 BOOST_CHECK_EQUAL(numSyncDataRcvd, 2);
167}
168
169BOOST_AUTO_TEST_CASE(MissedUpdate)
170{
171 vector<string> subscribeTo{"testUser-2", "testUser-4", "testUser-6"};
172 addConsumer(0, subscribeTo);
173
174 consumers[0]->sendHelloInterest();
175 advanceClocks(ndn::time::milliseconds(10));
176 BOOST_CHECK_EQUAL(numHelloDataRcvd, 1);
177
178 updateSeqFor("testUser-2", 3);
179 BOOST_CHECK_EQUAL(numSyncDataRcvd, 0);
180
181 // The sync interest sent after hello will timeout
182 advanceClocks(ndn::time::milliseconds(1000));
183 BOOST_CHECK_EQUAL(numSyncDataRcvd, 0);
184
185 // Next sync interest will bring back the sync data
Ashlesh Gawandeec43b362018-08-01 15:15:01 -0500186 advanceClocks(ndn::time::milliseconds(1500));
Ashlesh Gawande0b2897e2018-06-20 14:40:47 -0500187 BOOST_CHECK_EQUAL(numSyncDataRcvd, 1);
188}
189
190BOOST_AUTO_TEST_CASE(LateSubscription)
191{
192 vector<string> subscribeTo{"testUser-2", "testUser-4", "testUser-6"};
193 addConsumer(0, subscribeTo);
194
195 consumers[0]->sendHelloInterest();
196 advanceClocks(ndn::time::milliseconds(10));
197
198 BOOST_CHECK_EQUAL(numHelloDataRcvd, 1);
199 publishUpdateFor("testUser-2");
200 BOOST_CHECK_EQUAL(numSyncDataRcvd, 1);
201
202 consumers[0]->addSubscription("testUser-3");
203 consumers[0]->sendSyncInterest();
204 publishUpdateFor("testUser-3");
205 BOOST_CHECK_EQUAL(numSyncDataRcvd, 2);
206}
207
208BOOST_AUTO_TEST_CASE(ConsumerSyncTimeout)
209{
210 vector<string> subscribeTo{"testUser-2", "testUser-4", "testUser-6"};
211 addConsumer(0, subscribeTo);
212
213 consumers[0]->sendHelloInterest();
214 BOOST_CHECK_EQUAL(producer->m_pendingEntries.size(), 0);
215 advanceClocks(ndn::time::milliseconds(10));
216 BOOST_CHECK_EQUAL(producer->m_pendingEntries.size(), 1);
217 advanceClocks(ndn::time::milliseconds(10), 100);
218 BOOST_CHECK_EQUAL(producer->m_pendingEntries.size(), 0);
219 advanceClocks(ndn::time::milliseconds(10), 100);
220
221 int numSyncInterests = 0;
222 for (const auto& interest : consumerFaces[0]->sentInterests) {
223 if (interest.getName().getSubName(0, 2) == Name("/psync/sync")) {
224 numSyncInterests++;
225 }
226 }
227 BOOST_CHECK_EQUAL(numSyncInterests, 2);
228 BOOST_CHECK_EQUAL(numSyncDataRcvd, 0);
229}
230
231BOOST_AUTO_TEST_CASE(MultipleConsumersWithSameSubList)
232{
233 vector<string> subscribeTo{"testUser-2", "testUser-4", "testUser-6"};
234 addConsumer(0, subscribeTo);
235 addConsumer(1, subscribeTo);
236 addConsumer(2, subscribeTo);
237
238 consumers[0]->sendHelloInterest();
239 consumers[1]->sendHelloInterest();
240 consumers[2]->sendHelloInterest();
241 advanceClocks(ndn::time::milliseconds(10));
242
243 BOOST_CHECK_EQUAL(numHelloDataRcvd, 3);
244
245 publishUpdateFor("testUser-2");
246 BOOST_CHECK_EQUAL(numSyncDataRcvd, 3);
247
248 publishUpdateFor("testUser-3");
249 BOOST_CHECK_EQUAL(numSyncDataRcvd, 3);
250}
251
252BOOST_AUTO_TEST_CASE(MultipleConsumersWithDifferentSubList)
253{
254 vector<string> subscribeTo{"testUser-2", "testUser-4", "testUser-6"};
255 addConsumer(0, subscribeTo);
256
257 vector<string> subscribeTo1{"testUser-1", "testUser-3", "testUser-5"};
258 addConsumer(1, subscribeTo1);
259
260 vector<string> subscribeTo2{"testUser-2", "testUser-3"};
261 addConsumer(2, subscribeTo2);
262
263 consumers[0]->sendHelloInterest();
264 consumers[1]->sendHelloInterest();
265 consumers[2]->sendHelloInterest();
266 advanceClocks(ndn::time::milliseconds(10));
267
268 BOOST_CHECK_EQUAL(numHelloDataRcvd, 3);
269
270 publishUpdateFor("testUser-2");
271 BOOST_CHECK_EQUAL(numSyncDataRcvd, 2);
272
273 numSyncDataRcvd = 0;
274 publishUpdateFor("testUser-3");
275 BOOST_CHECK_EQUAL(numSyncDataRcvd, 2);
276}
277
278BOOST_AUTO_TEST_CASE(ReplicatedProducer)
279{
280 vector<string> subscribeTo{"testUser-2", "testUser-4", "testUser-6"};
281 addConsumer(0, subscribeTo);
282
283 consumers[0]->sendHelloInterest();
284 advanceClocks(ndn::time::milliseconds(10));
285 BOOST_CHECK_EQUAL(numHelloDataRcvd, 1);
286
287 publishUpdateFor("testUser-2");
288 BOOST_CHECK_EQUAL(numSyncDataRcvd, 1);
289
290 // Link to first producer goes down
291 face.unlink();
292
293 util::DummyClientFace face2(io, {true, true});
294 PartialProducer replicatedProducer(40, face2, syncPrefix, userPrefix);
295 for (int i = 1; i < 10; i++) {
296 replicatedProducer.addUserNode("testUser-" + to_string(i));
297 }
298 advanceClocks(ndn::time::milliseconds(10));
299 replicatedProducer.publishName("testUser-2");
300 // Link to a replicated producer comes up
301 face2.linkTo(*consumerFaces[0]);
302
303 BOOST_CHECK_EQUAL(face2.sentData.size(), 0);
304
305 // Update in first producer as well so consumer on sync data
306 // callback checks still pass
307 publishUpdateFor("testUser-2");
308 replicatedProducer.publishName("testUser-2");
309 advanceClocks(ndn::time::milliseconds(15), 100);
310 BOOST_CHECK_EQUAL(numSyncDataRcvd, 2);
311 BOOST_CHECK_EQUAL(face2.sentData.size(), 1);
312}
313
314BOOST_AUTO_TEST_CASE(ApplicationNack)
315{
316 // 50 is more than expected number of entries of 40 in the producer's IBF
317 addUserNodes("testUser", 50);
318
319 vector<string> subscribeTo{"testUser-2", "testUser-4", "testUser-6"};
320 addConsumer(0, subscribeTo);
321
322 consumers[0]->sendHelloInterest();
323 advanceClocks(ndn::time::milliseconds(10));
324 BOOST_CHECK_EQUAL(numHelloDataRcvd, 1);
325
326 publishUpdateFor("testUser-2");
327 BOOST_CHECK_EQUAL(numSyncDataRcvd, 1);
328
329 oldSeqMap = producer->m_prefixes;
330 for (int i = 0; i < 50; i++) {
331 ndn::Name prefix("testUser-" + to_string(i));
332 producer->updateSeqNo(prefix, producer->getSeqNo(prefix).value() + 1);
333 }
334 // Next sync interest should trigger the nack
335 advanceClocks(ndn::time::milliseconds(15), 100);
336
Ashlesh Gawandea9296472018-08-04 08:21:39 -0500337 // Application should have been notified that new data is available
338 // from the hello itself.
339 BOOST_CHECK_EQUAL(numSyncDataRcvd, 2);
Ashlesh Gawande0b2897e2018-06-20 14:40:47 -0500340
341 bool nackRcvd = false;
342 for (const auto& data : face.sentData) {
343 if (data.getContentType() == ndn::tlv::ContentType_Nack) {
344 nackRcvd = true;
345 break;
346 }
347 }
348 BOOST_CHECK(nackRcvd);
349
Ashlesh Gawandea9296472018-08-04 08:21:39 -0500350 publishUpdateFor("testUser-4");
Ashlesh Gawande0b2897e2018-06-20 14:40:47 -0500351 advanceClocks(ndn::time::milliseconds(10));
Ashlesh Gawandea9296472018-08-04 08:21:39 -0500352 BOOST_CHECK_EQUAL(numSyncDataRcvd, 3);
Ashlesh Gawande0b2897e2018-06-20 14:40:47 -0500353}
354
Ashlesh Gawandeec43b362018-08-01 15:15:01 -0500355BOOST_AUTO_TEST_CASE(SegmentedHello)
356{
357 vector<string> subscribeTo{"testUser-2", "testUser-4", "testUser-6"};
358 addConsumer(0, subscribeTo);
359
360 addUserNodes("testUser", 400);
361
362 consumers[0]->sendHelloInterest();
363 advanceClocks(ndn::time::milliseconds(10));
364 BOOST_CHECK_EQUAL(numHelloDataRcvd, 1);
Ashlesh Gawande2e82df12018-12-08 21:42:29 -0600365
366 // Simulate sending delayed interest for second segment
367 Name dataName = face.sentData.back().getName();
368 face.sentData.clear();
369 BOOST_CHECK_EQUAL(producer->m_segmentPublisher.m_ims.size(), 2);
370
371 advanceClocks(ndn::time::milliseconds(1000));
372 BOOST_CHECK_EQUAL(producer->m_segmentPublisher.m_ims.size(), 0);
373
374 producer->onHelloInterest(consumers[0]->m_helloInterestPrefix, Interest(dataName));
375 advanceClocks(ndn::time::milliseconds(10));
376 BOOST_CHECK_EQUAL(producer->m_segmentPublisher.m_ims.size(), 2);
377 BOOST_CHECK_EQUAL(face.sentData.front().getName()[-1].toSegment(), 1);
Ashlesh Gawandeec43b362018-08-01 15:15:01 -0500378}
379
380BOOST_AUTO_TEST_CASE(SegmentedSync)
381{
382 ndn::Name longNameToExceedDataSize;
383 for (int i = 0; i < 100; i++) {
384 longNameToExceedDataSize.append("test-" + std::to_string(i));
385 }
386 addUserNodes(longNameToExceedDataSize.toUri(), 10);
387
388 vector<string> subscribeTo;
389 for (int i = 1; i < 10; i++) {
390 subscribeTo.push_back(longNameToExceedDataSize.toUri() + "-" + to_string(i));
391 }
392 addConsumer(0, subscribeTo);
393
394 consumers[0]->sendHelloInterest();
395 advanceClocks(ndn::time::milliseconds(10));
396 BOOST_CHECK_EQUAL(numHelloDataRcvd, 1);
397
Ashlesh Gawande2e82df12018-12-08 21:42:29 -0600398 // To be used later to simulate sending delayed segmented interest
399 ndn::Name syncInterestName(consumers[0]->m_syncInterestPrefix);
400 consumers[0]->m_bloomFilter.appendToName(syncInterestName);
401 syncInterestName.append(consumers[0]->m_iblt);
402 syncInterestName.appendVersion();
403 syncInterestName.appendSegment(1);
404
Ashlesh Gawandeec43b362018-08-01 15:15:01 -0500405 oldSeqMap = producer->m_prefixes;
406 for (int i = 1; i < 10; i++) {
407 producer->updateSeqNo(longNameToExceedDataSize.toUri() + "-" + to_string(i), 1);
408 }
409
410 advanceClocks(ndn::time::milliseconds(1000));
411 BOOST_CHECK_EQUAL(numSyncDataRcvd, 0);
412
413 advanceClocks(ndn::time::milliseconds(1500));
414 BOOST_CHECK_EQUAL(numSyncDataRcvd, 1);
Ashlesh Gawande2e82df12018-12-08 21:42:29 -0600415
416 // Simulate sending delayed interest for second segment
417 face.sentData.clear();
418 consumerFaces[0]->sentData.clear();
419
420 BOOST_CHECK_EQUAL(producer->m_segmentPublisher.m_ims.size(), 2);
421
422 advanceClocks(ndn::time::milliseconds(2000));
423 BOOST_CHECK_EQUAL(producer->m_segmentPublisher.m_ims.size(), 0);
424
425 producer->onSyncInterest(consumers[0]->m_syncInterestPrefix, Interest(syncInterestName));
426 advanceClocks(ndn::time::milliseconds(10));
427 BOOST_CHECK_EQUAL(producer->m_segmentPublisher.m_ims.size(), 2);
428 BOOST_CHECK_EQUAL(face.sentData.front().getName()[-1].toSegment(), 1);
Ashlesh Gawandeec43b362018-08-01 15:15:01 -0500429}
430
Ashlesh Gawande0b2897e2018-06-20 14:40:47 -0500431BOOST_AUTO_TEST_SUITE_END()
432
433} // namespace psync