blob: 0b2800a76644ff5a79fa9120213fb5d158daa621 [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++;
60 checkSubList(availableSubs);
61
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 ) {
97 for (const auto& sub : availableSubs) {
98 if (prefix.first != sub) {
99 return false;
100 }
101 }
102 }
103 return true;
104 }
105
106 void
107 addUserNodes(const std::string& prefix, int numOfUserNodes)
108 {
109 // zeroth is added through constructor
110 for (int i = 1; i < numOfUserNodes; i++) {
111 producer->addUserNode(prefix + "-" + to_string(i));
112 }
113 }
114
115 void
116 publishUpdateFor(const std::string& prefix)
117 {
118 oldSeqMap = producer->m_prefixes;
119 producer->publishName(prefix);
120 advanceClocks(ndn::time::milliseconds(10));
121 }
122
123 void
124 updateSeqFor(const std::string& prefix, uint64_t seq)
125 {
126 oldSeqMap = producer->m_prefixes;
127 producer->updateSeqNo(prefix, seq);
128 }
129
130 util::DummyClientFace face;
131 Name syncPrefix;
132 Name userPrefix;
133
134 shared_ptr<PartialProducer> producer;
135 std::map <ndn::Name, uint64_t> oldSeqMap;
136
137 shared_ptr<Consumer> consumers[3];
138 shared_ptr<util::DummyClientFace> consumerFaces[3];
139 int numHelloDataRcvd;
140 int numSyncDataRcvd;
141};
142
143BOOST_FIXTURE_TEST_SUITE(PartialSync, PartialSyncFixture)
144
145BOOST_AUTO_TEST_CASE(Simple)
146{
147 vector<string> subscribeTo{"testUser-2", "testUser-4", "testUser-6"};
148 addConsumer(0, subscribeTo);
149
150 consumers[0]->sendHelloInterest();
151 advanceClocks(ndn::time::milliseconds(10));
152 BOOST_CHECK_EQUAL(numHelloDataRcvd, 1);
153
154 publishUpdateFor("testUser-2");
155 BOOST_CHECK_EQUAL(numSyncDataRcvd, 1);
156 publishUpdateFor("testUser-3");
157 BOOST_CHECK_EQUAL(numSyncDataRcvd, 1);
158 publishUpdateFor("testUser-2");
159 BOOST_CHECK_EQUAL(numSyncDataRcvd, 2);
160}
161
162BOOST_AUTO_TEST_CASE(MissedUpdate)
163{
164 vector<string> subscribeTo{"testUser-2", "testUser-4", "testUser-6"};
165 addConsumer(0, subscribeTo);
166
167 consumers[0]->sendHelloInterest();
168 advanceClocks(ndn::time::milliseconds(10));
169 BOOST_CHECK_EQUAL(numHelloDataRcvd, 1);
170
171 updateSeqFor("testUser-2", 3);
172 BOOST_CHECK_EQUAL(numSyncDataRcvd, 0);
173
174 // The sync interest sent after hello will timeout
175 advanceClocks(ndn::time::milliseconds(1000));
176 BOOST_CHECK_EQUAL(numSyncDataRcvd, 0);
177
178 // Next sync interest will bring back the sync data
179 advanceClocks(ndn::time::milliseconds(1000));
180 BOOST_CHECK_EQUAL(numSyncDataRcvd, 1);
181}
182
183BOOST_AUTO_TEST_CASE(LateSubscription)
184{
185 vector<string> subscribeTo{"testUser-2", "testUser-4", "testUser-6"};
186 addConsumer(0, subscribeTo);
187
188 consumers[0]->sendHelloInterest();
189 advanceClocks(ndn::time::milliseconds(10));
190
191 BOOST_CHECK_EQUAL(numHelloDataRcvd, 1);
192 publishUpdateFor("testUser-2");
193 BOOST_CHECK_EQUAL(numSyncDataRcvd, 1);
194
195 consumers[0]->addSubscription("testUser-3");
196 consumers[0]->sendSyncInterest();
197 publishUpdateFor("testUser-3");
198 BOOST_CHECK_EQUAL(numSyncDataRcvd, 2);
199}
200
201BOOST_AUTO_TEST_CASE(ConsumerSyncTimeout)
202{
203 vector<string> subscribeTo{"testUser-2", "testUser-4", "testUser-6"};
204 addConsumer(0, subscribeTo);
205
206 consumers[0]->sendHelloInterest();
207 BOOST_CHECK_EQUAL(producer->m_pendingEntries.size(), 0);
208 advanceClocks(ndn::time::milliseconds(10));
209 BOOST_CHECK_EQUAL(producer->m_pendingEntries.size(), 1);
210 advanceClocks(ndn::time::milliseconds(10), 100);
211 BOOST_CHECK_EQUAL(producer->m_pendingEntries.size(), 0);
212 advanceClocks(ndn::time::milliseconds(10), 100);
213
214 int numSyncInterests = 0;
215 for (const auto& interest : consumerFaces[0]->sentInterests) {
216 if (interest.getName().getSubName(0, 2) == Name("/psync/sync")) {
217 numSyncInterests++;
218 }
219 }
220 BOOST_CHECK_EQUAL(numSyncInterests, 2);
221 BOOST_CHECK_EQUAL(numSyncDataRcvd, 0);
222}
223
224BOOST_AUTO_TEST_CASE(MultipleConsumersWithSameSubList)
225{
226 vector<string> subscribeTo{"testUser-2", "testUser-4", "testUser-6"};
227 addConsumer(0, subscribeTo);
228 addConsumer(1, subscribeTo);
229 addConsumer(2, subscribeTo);
230
231 consumers[0]->sendHelloInterest();
232 consumers[1]->sendHelloInterest();
233 consumers[2]->sendHelloInterest();
234 advanceClocks(ndn::time::milliseconds(10));
235
236 BOOST_CHECK_EQUAL(numHelloDataRcvd, 3);
237
238 publishUpdateFor("testUser-2");
239 BOOST_CHECK_EQUAL(numSyncDataRcvd, 3);
240
241 publishUpdateFor("testUser-3");
242 BOOST_CHECK_EQUAL(numSyncDataRcvd, 3);
243}
244
245BOOST_AUTO_TEST_CASE(MultipleConsumersWithDifferentSubList)
246{
247 vector<string> subscribeTo{"testUser-2", "testUser-4", "testUser-6"};
248 addConsumer(0, subscribeTo);
249
250 vector<string> subscribeTo1{"testUser-1", "testUser-3", "testUser-5"};
251 addConsumer(1, subscribeTo1);
252
253 vector<string> subscribeTo2{"testUser-2", "testUser-3"};
254 addConsumer(2, subscribeTo2);
255
256 consumers[0]->sendHelloInterest();
257 consumers[1]->sendHelloInterest();
258 consumers[2]->sendHelloInterest();
259 advanceClocks(ndn::time::milliseconds(10));
260
261 BOOST_CHECK_EQUAL(numHelloDataRcvd, 3);
262
263 publishUpdateFor("testUser-2");
264 BOOST_CHECK_EQUAL(numSyncDataRcvd, 2);
265
266 numSyncDataRcvd = 0;
267 publishUpdateFor("testUser-3");
268 BOOST_CHECK_EQUAL(numSyncDataRcvd, 2);
269}
270
271BOOST_AUTO_TEST_CASE(ReplicatedProducer)
272{
273 vector<string> subscribeTo{"testUser-2", "testUser-4", "testUser-6"};
274 addConsumer(0, subscribeTo);
275
276 consumers[0]->sendHelloInterest();
277 advanceClocks(ndn::time::milliseconds(10));
278 BOOST_CHECK_EQUAL(numHelloDataRcvd, 1);
279
280 publishUpdateFor("testUser-2");
281 BOOST_CHECK_EQUAL(numSyncDataRcvd, 1);
282
283 // Link to first producer goes down
284 face.unlink();
285
286 util::DummyClientFace face2(io, {true, true});
287 PartialProducer replicatedProducer(40, face2, syncPrefix, userPrefix);
288 for (int i = 1; i < 10; i++) {
289 replicatedProducer.addUserNode("testUser-" + to_string(i));
290 }
291 advanceClocks(ndn::time::milliseconds(10));
292 replicatedProducer.publishName("testUser-2");
293 // Link to a replicated producer comes up
294 face2.linkTo(*consumerFaces[0]);
295
296 BOOST_CHECK_EQUAL(face2.sentData.size(), 0);
297
298 // Update in first producer as well so consumer on sync data
299 // callback checks still pass
300 publishUpdateFor("testUser-2");
301 replicatedProducer.publishName("testUser-2");
302 advanceClocks(ndn::time::milliseconds(15), 100);
303 BOOST_CHECK_EQUAL(numSyncDataRcvd, 2);
304 BOOST_CHECK_EQUAL(face2.sentData.size(), 1);
305}
306
307BOOST_AUTO_TEST_CASE(ApplicationNack)
308{
309 // 50 is more than expected number of entries of 40 in the producer's IBF
310 addUserNodes("testUser", 50);
311
312 vector<string> subscribeTo{"testUser-2", "testUser-4", "testUser-6"};
313 addConsumer(0, subscribeTo);
314
315 consumers[0]->sendHelloInterest();
316 advanceClocks(ndn::time::milliseconds(10));
317 BOOST_CHECK_EQUAL(numHelloDataRcvd, 1);
318
319 publishUpdateFor("testUser-2");
320 BOOST_CHECK_EQUAL(numSyncDataRcvd, 1);
321
322 oldSeqMap = producer->m_prefixes;
323 for (int i = 0; i < 50; i++) {
324 ndn::Name prefix("testUser-" + to_string(i));
325 producer->updateSeqNo(prefix, producer->getSeqNo(prefix).value() + 1);
326 }
327 // Next sync interest should trigger the nack
328 advanceClocks(ndn::time::milliseconds(15), 100);
329
330 // Nack does not contain any content so still should be 1
331 BOOST_CHECK_EQUAL(numSyncDataRcvd, 1);
332
333 bool nackRcvd = false;
334 for (const auto& data : face.sentData) {
335 if (data.getContentType() == ndn::tlv::ContentType_Nack) {
336 nackRcvd = true;
337 break;
338 }
339 }
340 BOOST_CHECK(nackRcvd);
341
342 producer->publishName("testUser-4");
343 advanceClocks(ndn::time::milliseconds(10));
344 BOOST_CHECK_EQUAL(numSyncDataRcvd, 2);
345}
346
347BOOST_AUTO_TEST_SUITE_END()
348
349} // namespace psync