blob: 31058ffb52232ccfaf45a76d061e13f508f5f2ed [file] [log] [blame]
Ashlesh Gawande0b2897e2018-06-20 14:40:47 -05001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/*
Ashlesh Gawande6a5157f2019-12-09 11:49:07 -06003 * Copyright (c) 2014-2020, 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
Davide Pesavento5b3cf762020-04-03 16:20:04 -040023#include "tests/boost-test.hpp"
24#include "tests/unit-test-time-fixture.hpp"
25
Ashlesh Gawande0b2897e2018-06-20 14:40:47 -050026#include <ndn-cxx/name.hpp>
27#include <ndn-cxx/util/dummy-client-face.hpp>
28
Ashlesh Gawande0b2897e2018-06-20 14:40:47 -050029namespace psync {
30
31using namespace ndn;
Ashlesh Gawande0b2897e2018-06-20 14:40:47 -050032
33class PartialSyncFixture : public tests::UnitTestTimeFixture
34{
35public:
36 PartialSyncFixture()
Davide Pesavento5b3cf762020-04-03 16:20:04 -040037 : face(io, {true, true})
38 , syncPrefix("psync")
39 , userPrefix("testUser-0")
40 , numHelloDataRcvd(0)
41 , numSyncDataRcvd(0)
Ashlesh Gawande0b2897e2018-06-20 14:40:47 -050042 {
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
Davide Pesavento5b3cf762020-04-03 16:20:04 -040057 addConsumer(int id, const std::vector<std::string>& subscribeTo, bool linkToProducer = true)
Ashlesh Gawande0b2897e2018-06-20 14:40:47 -050058 {
Davide Pesavento5b3cf762020-04-03 16:20:04 -040059 consumerFaces[id] = std::make_shared<util::DummyClientFace>(io, util::DummyClientFace::Options{true, true});
Ashlesh Gawande0b2897e2018-06-20 14:40:47 -050060
Ashlesh Gawandeec43b362018-08-01 15:15:01 -050061 if (linkToProducer) {
62 face.linkTo(*consumerFaces[id]);
63 }
Ashlesh Gawande0b2897e2018-06-20 14:40:47 -050064
Davide Pesavento5b3cf762020-04-03 16:20:04 -040065 consumers[id] = std::make_shared<Consumer>(syncPrefix, *consumerFaces[id],
66 [&, id] (const std::vector<Name>& availableSubs)
Ashlesh Gawande0b2897e2018-06-20 14:40:47 -050067 {
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
Davide Pesavento5b3cf762020-04-03 16:20:04 -0400103 checkSubList(const std::vector<Name>& availableSubs) const
Ashlesh Gawande0b2897e2018-06-20 14:40:47 -0500104 {
Davide Pesavento5b3cf762020-04-03 16:20:04 -0400105 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
Ashlesh Gawande6a5157f2019-12-09 11:49:07 -0600150BOOST_FIXTURE_TEST_SUITE(TestPartialSync, PartialSyncFixture)
Ashlesh Gawande0b2897e2018-06-20 14:40:47 -0500151
152BOOST_AUTO_TEST_CASE(Simple)
153{
Davide Pesavento5b3cf762020-04-03 16:20:04 -0400154 std::vector<std::string> subscribeTo{"testUser-2", "testUser-4", "testUser-6"};
Ashlesh Gawande0b2897e2018-06-20 14:40:47 -0500155 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{
Davide Pesavento5b3cf762020-04-03 16:20:04 -0400171 std::vector<std::string> subscribeTo{"testUser-2", "testUser-4", "testUser-6"};
Ashlesh Gawande0b2897e2018-06-20 14:40:47 -0500172 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{
Davide Pesavento5b3cf762020-04-03 16:20:04 -0400192 std::vector<std::string> subscribeTo{"testUser-2", "testUser-4", "testUser-6"};
Ashlesh Gawande0b2897e2018-06-20 14:40:47 -0500193 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{
Davide Pesavento5b3cf762020-04-03 16:20:04 -0400210 std::vector<std::string> subscribeTo{"testUser-2", "testUser-4", "testUser-6"};
Ashlesh Gawande0b2897e2018-06-20 14:40:47 -0500211 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{
Davide Pesavento5b3cf762020-04-03 16:20:04 -0400233 std::vector<std::string> subscribeTo{"testUser-2", "testUser-4", "testUser-6"};
Ashlesh Gawande0b2897e2018-06-20 14:40:47 -0500234 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{
Davide Pesavento5b3cf762020-04-03 16:20:04 -0400254 std::vector<std::string> subscribeTo{"testUser-2", "testUser-4", "testUser-6"};
Ashlesh Gawande0b2897e2018-06-20 14:40:47 -0500255 addConsumer(0, subscribeTo);
256
Davide Pesavento5b3cf762020-04-03 16:20:04 -0400257 std::vector<std::string> subscribeTo1{"testUser-1", "testUser-3", "testUser-5"};
Ashlesh Gawande0b2897e2018-06-20 14:40:47 -0500258 addConsumer(1, subscribeTo1);
259
Davide Pesavento5b3cf762020-04-03 16:20:04 -0400260 std::vector<std::string> subscribeTo2{"testUser-2", "testUser-3"};
Ashlesh Gawande0b2897e2018-06-20 14:40:47 -0500261 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{
Davide Pesavento5b3cf762020-04-03 16:20:04 -0400280 std::vector<std::string> subscribeTo{"testUser-2", "testUser-4", "testUser-6"};
Ashlesh Gawande0b2897e2018-06-20 14:40:47 -0500281 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
Davide Pesavento5b3cf762020-04-03 16:20:04 -0400319 std::vector<std::string> subscribeTo{"testUser-2", "testUser-4", "testUser-6"};
Ashlesh Gawande0b2897e2018-06-20 14:40:47 -0500320 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{
Davide Pesavento5b3cf762020-04-03 16:20:04 -0400357 std::vector<std::string> subscribeTo{"testUser-2", "testUser-4", "testUser-6"};
Ashlesh Gawandeec43b362018-08-01 15:15:01 -0500358 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
Davide Pesavento5b3cf762020-04-03 16:20:04 -0400367 BOOST_REQUIRE(!face.sentData.empty());
Ashlesh Gawande2e82df12018-12-08 21:42:29 -0600368 Name dataName = face.sentData.back().getName();
369 face.sentData.clear();
370 BOOST_CHECK_EQUAL(producer->m_segmentPublisher.m_ims.size(), 2);
371
372 advanceClocks(ndn::time::milliseconds(1000));
373 BOOST_CHECK_EQUAL(producer->m_segmentPublisher.m_ims.size(), 0);
374
375 producer->onHelloInterest(consumers[0]->m_helloInterestPrefix, Interest(dataName));
376 advanceClocks(ndn::time::milliseconds(10));
377 BOOST_CHECK_EQUAL(producer->m_segmentPublisher.m_ims.size(), 2);
Davide Pesavento5b3cf762020-04-03 16:20:04 -0400378 BOOST_REQUIRE(!face.sentData.empty());
379 BOOST_CHECK_EQUAL(face.sentData.front().getName().at(-1).toSegment(), 1);
Ashlesh Gawandeec43b362018-08-01 15:15:01 -0500380}
381
382BOOST_AUTO_TEST_CASE(SegmentedSync)
383{
384 ndn::Name longNameToExceedDataSize;
385 for (int i = 0; i < 100; i++) {
386 longNameToExceedDataSize.append("test-" + std::to_string(i));
387 }
388 addUserNodes(longNameToExceedDataSize.toUri(), 10);
389
Davide Pesavento5b3cf762020-04-03 16:20:04 -0400390 std::vector<std::string> subscribeTo;
Ashlesh Gawandeec43b362018-08-01 15:15:01 -0500391 for (int i = 1; i < 10; i++) {
392 subscribeTo.push_back(longNameToExceedDataSize.toUri() + "-" + to_string(i));
393 }
394 addConsumer(0, subscribeTo);
395
396 consumers[0]->sendHelloInterest();
397 advanceClocks(ndn::time::milliseconds(10));
398 BOOST_CHECK_EQUAL(numHelloDataRcvd, 1);
399
Ashlesh Gawande2e82df12018-12-08 21:42:29 -0600400 // To be used later to simulate sending delayed segmented interest
401 ndn::Name syncInterestName(consumers[0]->m_syncInterestPrefix);
402 consumers[0]->m_bloomFilter.appendToName(syncInterestName);
403 syncInterestName.append(consumers[0]->m_iblt);
404 syncInterestName.appendVersion();
405 syncInterestName.appendSegment(1);
406
Ashlesh Gawandeec43b362018-08-01 15:15:01 -0500407 oldSeqMap = producer->m_prefixes;
408 for (int i = 1; i < 10; i++) {
409 producer->updateSeqNo(longNameToExceedDataSize.toUri() + "-" + to_string(i), 1);
410 }
411
412 advanceClocks(ndn::time::milliseconds(1000));
413 BOOST_CHECK_EQUAL(numSyncDataRcvd, 0);
414
415 advanceClocks(ndn::time::milliseconds(1500));
416 BOOST_CHECK_EQUAL(numSyncDataRcvd, 1);
Ashlesh Gawande2e82df12018-12-08 21:42:29 -0600417
418 // Simulate sending delayed interest for second segment
419 face.sentData.clear();
420 consumerFaces[0]->sentData.clear();
421
422 BOOST_CHECK_EQUAL(producer->m_segmentPublisher.m_ims.size(), 2);
423
424 advanceClocks(ndn::time::milliseconds(2000));
425 BOOST_CHECK_EQUAL(producer->m_segmentPublisher.m_ims.size(), 0);
426
427 producer->onSyncInterest(consumers[0]->m_syncInterestPrefix, Interest(syncInterestName));
428 advanceClocks(ndn::time::milliseconds(10));
429 BOOST_CHECK_EQUAL(producer->m_segmentPublisher.m_ims.size(), 2);
Davide Pesavento5b3cf762020-04-03 16:20:04 -0400430 BOOST_REQUIRE(!face.sentData.empty());
431 BOOST_CHECK_EQUAL(face.sentData.front().getName().at(-1).toSegment(), 1);
Ashlesh Gawandeec43b362018-08-01 15:15:01 -0500432}
433
Ashlesh Gawande0b2897e2018-06-20 14:40:47 -0500434BOOST_AUTO_TEST_SUITE_END()
435
Ashlesh Gawande6a5157f2019-12-09 11:49:07 -0600436} // namespace psync