blob: 806119472788d979cdc599fc6545dad0677eb725 [file] [log] [blame]
Ashlesh Gawande0b2897e2018-06-20 14:40:47 -05001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/*
3 * Copyright (c) 2014-2018, The University of Memphis
4 *
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
9 * of the GNU General Public License as published by the Free Software Foundation,
10 * 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
14 * PURPOSE. See the GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along with
17 * PSync, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
18 **/
19
20#include "partial-producer.hpp"
21#include "consumer.hpp"
22#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
28#include <iostream>
29
30namespace psync {
31
32using namespace ndn;
33using namespace std;
34
35class PartialSyncFixture : public tests::UnitTestTimeFixture
36{
37public:
38 PartialSyncFixture()
39 : face(io, {true, true})
40 , syncPrefix("psync")
41 , userPrefix("testUser-0")
42 , numHelloDataRcvd(0)
43 , numSyncDataRcvd(0)
44 {
45 producer = make_shared<PartialProducer>(40, face, syncPrefix, userPrefix);
46 addUserNodes("testUser", 10);
47 }
48
49 void
50 addConsumer(int id, const vector<string>& subscribeTo)
51 {
52 consumerFaces[id] = make_shared<util::DummyClientFace>(io, util::DummyClientFace::Options{true, true});
53
54 face.linkTo(*consumerFaces[id]);
55
56 consumers[id] = make_shared<Consumer>(syncPrefix, *consumerFaces[id],
57 [&, id] (const vector<Name>& availableSubs)
58 {
59 numHelloDataRcvd++;
Ashlesh Gawandedeb73f82018-08-09 11:08:02 -050060 BOOST_CHECK(checkSubList(availableSubs));
Ashlesh Gawande0b2897e2018-06-20 14:40:47 -050061
62 checkIBFUpdated(id);
63
64 for (const auto& sub : subscribeTo) {
65 consumers[id]->addSubscription(sub);
66 }
67 consumers[id]->sendSyncInterest();
68 },
69 [&, id] (const std::vector<MissingDataInfo>& updates) {
70 numSyncDataRcvd++;
71
72 checkIBFUpdated(id);
73
74 for (const auto& update : updates) {
75 BOOST_CHECK(consumers[id]->isSubscribed(update.prefix));
76 BOOST_CHECK_EQUAL(oldSeqMap.at(update.prefix) + 1, update.lowSeq);
77 BOOST_CHECK_EQUAL(producer->m_prefixes.at(update.prefix), update.highSeq);
78 BOOST_CHECK_EQUAL(consumers[id]->getSeqNo(update.prefix).value(), update.highSeq);
79 }
80 }, 40, 0.001);
81
82 advanceClocks(ndn::time::milliseconds(10));
83 }
84
85 void
86 checkIBFUpdated(int id)
87 {
88 Name emptyName;
89 producer->m_iblt.appendToName(emptyName);
90 BOOST_CHECK_EQUAL(consumers[id]->m_iblt, emptyName);
91 }
92
93 bool
94 checkSubList(const vector<Name>& availableSubs)
95 {
96 for (const auto& prefix : producer->m_prefixes ) {
Ashlesh Gawandedeb73f82018-08-09 11:08:02 -050097 if (std::find(availableSubs.begin(), availableSubs.end(), prefix.first) == availableSubs.end()) {
98 return false;
Ashlesh Gawande0b2897e2018-06-20 14:40:47 -050099 }
100 }
101 return true;
102 }
103
104 void
105 addUserNodes(const std::string& prefix, int numOfUserNodes)
106 {
107 // zeroth is added through constructor
108 for (int i = 1; i < numOfUserNodes; i++) {
109 producer->addUserNode(prefix + "-" + to_string(i));
110 }
111 }
112
113 void
114 publishUpdateFor(const std::string& prefix)
115 {
116 oldSeqMap = producer->m_prefixes;
117 producer->publishName(prefix);
118 advanceClocks(ndn::time::milliseconds(10));
119 }
120
121 void
122 updateSeqFor(const std::string& prefix, uint64_t seq)
123 {
124 oldSeqMap = producer->m_prefixes;
125 producer->updateSeqNo(prefix, seq);
126 }
127
128 util::DummyClientFace face;
129 Name syncPrefix;
130 Name userPrefix;
131
132 shared_ptr<PartialProducer> producer;
133 std::map <ndn::Name, uint64_t> oldSeqMap;
134
135 shared_ptr<Consumer> consumers[3];
136 shared_ptr<util::DummyClientFace> consumerFaces[3];
137 int numHelloDataRcvd;
138 int numSyncDataRcvd;
139};
140
141BOOST_FIXTURE_TEST_SUITE(PartialSync, PartialSyncFixture)
142
143BOOST_AUTO_TEST_CASE(Simple)
144{
145 vector<string> subscribeTo{"testUser-2", "testUser-4", "testUser-6"};
146 addConsumer(0, subscribeTo);
147
148 consumers[0]->sendHelloInterest();
149 advanceClocks(ndn::time::milliseconds(10));
150 BOOST_CHECK_EQUAL(numHelloDataRcvd, 1);
151
152 publishUpdateFor("testUser-2");
153 BOOST_CHECK_EQUAL(numSyncDataRcvd, 1);
154 publishUpdateFor("testUser-3");
155 BOOST_CHECK_EQUAL(numSyncDataRcvd, 1);
156 publishUpdateFor("testUser-2");
157 BOOST_CHECK_EQUAL(numSyncDataRcvd, 2);
158}
159
160BOOST_AUTO_TEST_CASE(MissedUpdate)
161{
162 vector<string> subscribeTo{"testUser-2", "testUser-4", "testUser-6"};
163 addConsumer(0, subscribeTo);
164
165 consumers[0]->sendHelloInterest();
166 advanceClocks(ndn::time::milliseconds(10));
167 BOOST_CHECK_EQUAL(numHelloDataRcvd, 1);
168
169 updateSeqFor("testUser-2", 3);
170 BOOST_CHECK_EQUAL(numSyncDataRcvd, 0);
171
172 // The sync interest sent after hello will timeout
173 advanceClocks(ndn::time::milliseconds(1000));
174 BOOST_CHECK_EQUAL(numSyncDataRcvd, 0);
175
176 // Next sync interest will bring back the sync data
177 advanceClocks(ndn::time::milliseconds(1000));
178 BOOST_CHECK_EQUAL(numSyncDataRcvd, 1);
179}
180
181BOOST_AUTO_TEST_CASE(LateSubscription)
182{
183 vector<string> subscribeTo{"testUser-2", "testUser-4", "testUser-6"};
184 addConsumer(0, subscribeTo);
185
186 consumers[0]->sendHelloInterest();
187 advanceClocks(ndn::time::milliseconds(10));
188
189 BOOST_CHECK_EQUAL(numHelloDataRcvd, 1);
190 publishUpdateFor("testUser-2");
191 BOOST_CHECK_EQUAL(numSyncDataRcvd, 1);
192
193 consumers[0]->addSubscription("testUser-3");
194 consumers[0]->sendSyncInterest();
195 publishUpdateFor("testUser-3");
196 BOOST_CHECK_EQUAL(numSyncDataRcvd, 2);
197}
198
199BOOST_AUTO_TEST_CASE(ConsumerSyncTimeout)
200{
201 vector<string> subscribeTo{"testUser-2", "testUser-4", "testUser-6"};
202 addConsumer(0, subscribeTo);
203
204 consumers[0]->sendHelloInterest();
205 BOOST_CHECK_EQUAL(producer->m_pendingEntries.size(), 0);
206 advanceClocks(ndn::time::milliseconds(10));
207 BOOST_CHECK_EQUAL(producer->m_pendingEntries.size(), 1);
208 advanceClocks(ndn::time::milliseconds(10), 100);
209 BOOST_CHECK_EQUAL(producer->m_pendingEntries.size(), 0);
210 advanceClocks(ndn::time::milliseconds(10), 100);
211
212 int numSyncInterests = 0;
213 for (const auto& interest : consumerFaces[0]->sentInterests) {
214 if (interest.getName().getSubName(0, 2) == Name("/psync/sync")) {
215 numSyncInterests++;
216 }
217 }
218 BOOST_CHECK_EQUAL(numSyncInterests, 2);
219 BOOST_CHECK_EQUAL(numSyncDataRcvd, 0);
220}
221
222BOOST_AUTO_TEST_CASE(MultipleConsumersWithSameSubList)
223{
224 vector<string> subscribeTo{"testUser-2", "testUser-4", "testUser-6"};
225 addConsumer(0, subscribeTo);
226 addConsumer(1, subscribeTo);
227 addConsumer(2, subscribeTo);
228
229 consumers[0]->sendHelloInterest();
230 consumers[1]->sendHelloInterest();
231 consumers[2]->sendHelloInterest();
232 advanceClocks(ndn::time::milliseconds(10));
233
234 BOOST_CHECK_EQUAL(numHelloDataRcvd, 3);
235
236 publishUpdateFor("testUser-2");
237 BOOST_CHECK_EQUAL(numSyncDataRcvd, 3);
238
239 publishUpdateFor("testUser-3");
240 BOOST_CHECK_EQUAL(numSyncDataRcvd, 3);
241}
242
243BOOST_AUTO_TEST_CASE(MultipleConsumersWithDifferentSubList)
244{
245 vector<string> subscribeTo{"testUser-2", "testUser-4", "testUser-6"};
246 addConsumer(0, subscribeTo);
247
248 vector<string> subscribeTo1{"testUser-1", "testUser-3", "testUser-5"};
249 addConsumer(1, subscribeTo1);
250
251 vector<string> subscribeTo2{"testUser-2", "testUser-3"};
252 addConsumer(2, subscribeTo2);
253
254 consumers[0]->sendHelloInterest();
255 consumers[1]->sendHelloInterest();
256 consumers[2]->sendHelloInterest();
257 advanceClocks(ndn::time::milliseconds(10));
258
259 BOOST_CHECK_EQUAL(numHelloDataRcvd, 3);
260
261 publishUpdateFor("testUser-2");
262 BOOST_CHECK_EQUAL(numSyncDataRcvd, 2);
263
264 numSyncDataRcvd = 0;
265 publishUpdateFor("testUser-3");
266 BOOST_CHECK_EQUAL(numSyncDataRcvd, 2);
267}
268
269BOOST_AUTO_TEST_CASE(ReplicatedProducer)
270{
271 vector<string> subscribeTo{"testUser-2", "testUser-4", "testUser-6"};
272 addConsumer(0, subscribeTo);
273
274 consumers[0]->sendHelloInterest();
275 advanceClocks(ndn::time::milliseconds(10));
276 BOOST_CHECK_EQUAL(numHelloDataRcvd, 1);
277
278 publishUpdateFor("testUser-2");
279 BOOST_CHECK_EQUAL(numSyncDataRcvd, 1);
280
281 // Link to first producer goes down
282 face.unlink();
283
284 util::DummyClientFace face2(io, {true, true});
285 PartialProducer replicatedProducer(40, face2, syncPrefix, userPrefix);
286 for (int i = 1; i < 10; i++) {
287 replicatedProducer.addUserNode("testUser-" + to_string(i));
288 }
289 advanceClocks(ndn::time::milliseconds(10));
290 replicatedProducer.publishName("testUser-2");
291 // Link to a replicated producer comes up
292 face2.linkTo(*consumerFaces[0]);
293
294 BOOST_CHECK_EQUAL(face2.sentData.size(), 0);
295
296 // Update in first producer as well so consumer on sync data
297 // callback checks still pass
298 publishUpdateFor("testUser-2");
299 replicatedProducer.publishName("testUser-2");
300 advanceClocks(ndn::time::milliseconds(15), 100);
301 BOOST_CHECK_EQUAL(numSyncDataRcvd, 2);
302 BOOST_CHECK_EQUAL(face2.sentData.size(), 1);
303}
304
305BOOST_AUTO_TEST_CASE(ApplicationNack)
306{
307 // 50 is more than expected number of entries of 40 in the producer's IBF
308 addUserNodes("testUser", 50);
309
310 vector<string> subscribeTo{"testUser-2", "testUser-4", "testUser-6"};
311 addConsumer(0, subscribeTo);
312
313 consumers[0]->sendHelloInterest();
314 advanceClocks(ndn::time::milliseconds(10));
315 BOOST_CHECK_EQUAL(numHelloDataRcvd, 1);
316
317 publishUpdateFor("testUser-2");
318 BOOST_CHECK_EQUAL(numSyncDataRcvd, 1);
319
320 oldSeqMap = producer->m_prefixes;
321 for (int i = 0; i < 50; i++) {
322 ndn::Name prefix("testUser-" + to_string(i));
323 producer->updateSeqNo(prefix, producer->getSeqNo(prefix).value() + 1);
324 }
325 // Next sync interest should trigger the nack
326 advanceClocks(ndn::time::milliseconds(15), 100);
327
Ashlesh Gawandea9296472018-08-04 08:21:39 -0500328 // Application should have been notified that new data is available
329 // from the hello itself.
330 BOOST_CHECK_EQUAL(numSyncDataRcvd, 2);
Ashlesh Gawande0b2897e2018-06-20 14:40:47 -0500331
332 bool nackRcvd = false;
333 for (const auto& data : face.sentData) {
334 if (data.getContentType() == ndn::tlv::ContentType_Nack) {
335 nackRcvd = true;
336 break;
337 }
338 }
339 BOOST_CHECK(nackRcvd);
340
Ashlesh Gawandea9296472018-08-04 08:21:39 -0500341 publishUpdateFor("testUser-4");
Ashlesh Gawande0b2897e2018-06-20 14:40:47 -0500342 advanceClocks(ndn::time::milliseconds(10));
Ashlesh Gawandea9296472018-08-04 08:21:39 -0500343 BOOST_CHECK_EQUAL(numSyncDataRcvd, 3);
Ashlesh Gawande0b2897e2018-06-20 14:40:47 -0500344}
345
346BOOST_AUTO_TEST_SUITE_END()
347
348} // namespace psync