PSync: initial commit
refs: #4641
Change-Id: Iabed3ad7632544d97559e6798547b7972b416784
diff --git a/tests/boost-multi-log-formatter.hpp b/tests/boost-multi-log-formatter.hpp
new file mode 100644
index 0000000..26946f9
--- /dev/null
+++ b/tests/boost-multi-log-formatter.hpp
@@ -0,0 +1,214 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014-2018 Regents of the University of California.
+ *
+ * Based on work by Martin Ba (http://stackoverflow.com/a/26718189)
+ *
+ * This file is distributed under the Boost Software License, Version 1.0.
+ * (See http://www.boost.org/LICENSE_1_0.txt)
+ */
+
+#ifndef NDN_TESTS_BOOST_MULTI_LOG_FORMATTER_HPP
+#define NDN_TESTS_BOOST_MULTI_LOG_FORMATTER_HPP
+
+#include <boost/version.hpp>
+
+#if BOOST_VERSION >= 105900
+#include <boost/test/unit_test_parameters.hpp>
+#else
+#include <boost/test/detail/unit_test_parameters.hpp>
+#endif // BOOST_VERSION >= 105900
+
+#include <boost/test/unit_test_log_formatter.hpp>
+#include <boost/test/output/compiler_log_formatter.hpp>
+#include <boost/test/output/xml_log_formatter.hpp>
+
+namespace boost {
+namespace unit_test {
+namespace output {
+
+/**
+ * @brief Log formatter for Boost.Test that outputs the logging to multiple formatters
+ *
+ * The log formatter is designed to output to one or multiple formatters at the same time. For
+ * example, one HRF formatter can output to the standard output, while XML formatter output to
+ * the file.
+ *
+ * Usage:
+ *
+ * // Call in init_unit_test_suite: (this will override the --log_format parameter)
+ * auto formatter = new boost::unit_test::output::multi_log_formatter; // same as already configured logger
+ *
+ * // Prepare and add additional logger(s)
+ * formatter.add(std::make_shared<boost::unit_test::output::xml_log_formatter>(),
+ * std::make_shared<std::ofstream>("out.xml"));
+ *
+ * boost::unit_test::unit_test_log.set_formatter(formatter);
+ *
+ * @note Calling `boost::unit_test::unit_test_log.set_stream(...)` will change the stream for
+ * the original logger.
+ */
+class multi_log_formatter : public unit_test_log_formatter
+{
+public:
+ /**
+ * @brief Create instance of the logger, based on the configured logger instance
+ */
+ multi_log_formatter()
+ {
+ auto format =
+#if BOOST_VERSION > 105900
+ runtime_config::get<output_format>(runtime_config::LOG_FORMAT);
+#else
+ runtime_config::log_format();
+#endif // BOOST_VERSION > 105900
+
+ switch (format) {
+ default:
+#if BOOST_VERSION >= 105900
+ case OF_CLF:
+#else
+ case CLF:
+#endif // BOOST_VERSION >= 105900
+ m_loggers.push_back({std::make_shared<compiler_log_formatter>(), nullptr});
+ break;
+#if BOOST_VERSION >= 105900
+ case OF_XML:
+#else
+ case XML:
+#endif // BOOST_VERSION >= 105900
+ m_loggers.push_back({std::make_shared<xml_log_formatter>(), nullptr});
+ break;
+ }
+ }
+
+ void
+ add(std::shared_ptr<unit_test_log_formatter> formatter, std::shared_ptr<std::ostream> os)
+ {
+ m_loggers.push_back({formatter, os});
+ }
+
+ // Formatter interface
+ void
+ log_start(std::ostream& os, counter_t test_cases_amount)
+ {
+ for (auto& l : m_loggers)
+ l.logger->log_start(l.os == nullptr ? os : *l.os, test_cases_amount);
+ }
+
+ void
+ log_finish(std::ostream& os)
+ {
+ for (auto& l : m_loggers)
+ l.logger->log_finish(l.os == nullptr ? os : *l.os);
+ }
+
+ void
+ log_build_info(std::ostream& os)
+ {
+ for (auto& l : m_loggers)
+ l.logger->log_build_info(l.os == nullptr ? os : *l.os);
+ }
+
+ void
+ test_unit_start(std::ostream& os, const test_unit& tu)
+ {
+ for (auto& l : m_loggers)
+ l.logger->test_unit_start(l.os == nullptr ? os : *l.os, tu);
+ }
+
+ void
+ test_unit_finish(std::ostream& os, const test_unit& tu, unsigned long elapsed)
+ {
+ for (auto& l : m_loggers)
+ l.logger->test_unit_finish(l.os == nullptr ? os : *l.os, tu, elapsed);
+ }
+
+ void
+ test_unit_skipped(std::ostream& os, const test_unit& tu)
+ {
+ for (auto& l : m_loggers)
+ l.logger->test_unit_skipped(l.os == nullptr ? os : *l.os, tu);
+ }
+
+#if BOOST_VERSION >= 105900
+ void
+ log_exception_start(std::ostream& os, const log_checkpoint_data& lcd, const execution_exception& ex)
+ {
+ for (auto& l : m_loggers)
+ l.logger->log_exception_start(l.os == nullptr ? os : *l.os, lcd, ex);
+ }
+
+ void
+ log_exception_finish(std::ostream& os)
+ {
+ for (auto& l : m_loggers)
+ l.logger->log_exception_finish(l.os == nullptr ? os : *l.os);
+ }
+#else
+ void
+ log_exception(std::ostream& os, const log_checkpoint_data& lcd, const execution_exception& ex)
+ {
+ for (auto& l : m_loggers)
+ l.logger->log_exception(l.os == nullptr ? os : *l.os, lcd, ex);
+ }
+#endif // BOOST_VERSION >= 105900
+
+ void
+ log_entry_start(std::ostream& os, const log_entry_data& entry_data, log_entry_types let)
+ {
+ for (auto& l : m_loggers)
+ l.logger->log_entry_start(l.os == nullptr ? os : *l.os, entry_data, let);
+ }
+
+ void
+ log_entry_value(std::ostream& os, const_string value)
+ {
+ for (auto& l : m_loggers)
+ l.logger->log_entry_value(l.os == nullptr ? os : *l.os, value);
+ }
+
+ void
+ log_entry_finish(std::ostream& os)
+ {
+ for (auto& l : m_loggers)
+ l.logger->log_entry_finish(l.os == nullptr ? os : *l.os);
+ }
+
+#if BOOST_VERSION >= 105900
+ void
+ entry_context_start(std::ostream& os, log_level level)
+ {
+ for (auto& l : m_loggers)
+ l.logger->entry_context_start(l.os == nullptr ? os : *l.os, level);
+ }
+
+ void
+ log_entry_context(std::ostream& os, const_string value)
+ {
+ for (auto& l : m_loggers)
+ l.logger->log_entry_context(l.os == nullptr ? os : *l.os, value);
+ }
+
+ void
+ entry_context_finish(std::ostream& os)
+ {
+ for (auto& l : m_loggers)
+ l.logger->entry_context_finish(l.os == nullptr ? os : *l.os);
+ }
+#endif // BOOST_VERSION >= 105900
+
+private:
+ struct LoggerInfo
+ {
+ std::shared_ptr<unit_test_log_formatter> logger;
+ std::shared_ptr<std::ostream> os;
+ };
+ std::vector<LoggerInfo> m_loggers;
+};
+
+} // namespace output
+} // namespace unit_test
+} // namespace boost
+
+#endif // NDN_TESTS_BOOST_MULTI_LOG_FORMATTER_HPP
diff --git a/tests/boost-test.hpp b/tests/boost-test.hpp
new file mode 100644
index 0000000..ec1f40f
--- /dev/null
+++ b/tests/boost-test.hpp
@@ -0,0 +1,35 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014-2018, The University of Memphis
+ * Regents of the University of California,
+ *
+ * This file is part of PSync.
+ * See AUTHORS.md for complete list of PSync authors and contributors.
+ *
+ * PSync is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ *
+ * PSync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * PSync, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * \author Yingdi Yu <yingdi@cs.ucla.edu>
+ *
+ **/
+
+#ifndef PSYNC_TESTS_BOOST_TEST_HPP
+#define PSYNC_TESTS_BOOST_TEST_HPP
+
+// suppress warnings from Boost.Test
+#pragma GCC system_header
+#pragma clang system_header
+
+#include <boost/test/unit_test.hpp>
+#include <boost/concept_check.hpp>
+#include <boost/test/output_test_stream.hpp>
+
+#endif // PSYNC_TESTS_BOOST_TEST_HPP
diff --git a/tests/main.cpp b/tests/main.cpp
new file mode 100644
index 0000000..65d5e4c
--- /dev/null
+++ b/tests/main.cpp
@@ -0,0 +1,115 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014-2018, Regents of the University of California,
+ * Arizona Board of Regents,
+ * Colorado State University,
+ * University Pierre & Marie Curie, Sorbonne University,
+ * Washington University in St. Louis,
+ * Beijing Institute of Technology,
+ * The University of Memphis.
+ *
+ * This file is part of PSync.
+ * See AUTHORS.md for complete list of PSync authors and contributors.
+ *
+ * PSync is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ *
+ * PSync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * PSync, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define BOOST_TEST_DYN_LINK
+#define BOOST_TEST_ALTERNATIVE_INIT_API
+
+#include <boost/version.hpp>
+
+#if BOOST_VERSION >= 106200
+// Boost.Test v3.3 (Boost 1.62) natively supports multi-logger output
+#include "boost-test.hpp"
+#else
+#define BOOST_TEST_NO_MAIN
+#include "boost-test.hpp"
+
+#include "boost-multi-log-formatter.hpp"
+
+#include <boost/program_options/options_description.hpp>
+#include <boost/program_options/variables_map.hpp>
+#include <boost/program_options/parsers.hpp>
+
+#include <fstream>
+#include <iostream>
+
+static bool
+init_tests()
+{
+ init_unit_test();
+
+ namespace po = boost::program_options;
+ namespace ut = boost::unit_test;
+
+ po::options_description extraOptions;
+ std::string logger;
+ std::string outputFile = "-";
+ extraOptions.add_options()
+ ("log_format2", po::value<std::string>(&logger), "Type of second log formatter: HRF or XML")
+ ("log_sink2", po::value<std::string>(&outputFile)->default_value(outputFile), "Second log sink, - for stdout")
+ ;
+ po::variables_map vm;
+ try {
+ po::store(po::command_line_parser(ut::framework::master_test_suite().argc,
+ ut::framework::master_test_suite().argv)
+ .options(extraOptions)
+ .run(),
+ vm);
+ po::notify(vm);
+ }
+ catch (const std::exception& e) {
+ std::cerr << "ERROR: " << e.what() << "\n"
+ << extraOptions << std::endl;
+ return false;
+ }
+
+ if (vm.count("log_format2") == 0) {
+ // second logger is not configured
+ return true;
+ }
+
+ std::shared_ptr<ut::unit_test_log_formatter> formatter;
+ if (logger == "XML") {
+ formatter = std::make_shared<ut::output::xml_log_formatter>();
+ }
+ else if (logger == "HRF") {
+ formatter = std::make_shared<ut::output::compiler_log_formatter>();
+ }
+ else {
+ std::cerr << "ERROR: only HRF or XML log formatter can be specified" << std::endl;
+ return false;
+ }
+
+ std::shared_ptr<std::ostream> output;
+ if (outputFile == "-") {
+ output = std::shared_ptr<std::ostream>(&std::cout, std::bind([]{}));
+ }
+ else {
+ output = std::make_shared<std::ofstream>(outputFile.c_str());
+ }
+
+ auto multiFormatter = new ut::output::multi_log_formatter;
+ multiFormatter->add(formatter, output);
+ ut::unit_test_log.set_formatter(multiFormatter);
+
+ return true;
+}
+
+int
+main(int argc, char* argv[])
+{
+ return ::boost::unit_test::unit_test_main(&init_tests, argc, argv);
+}
+
+#endif // BOOST_VERSION >= 106200
diff --git a/tests/test-bloom-filter.cpp b/tests/test-bloom-filter.cpp
new file mode 100644
index 0000000..55b71e8
--- /dev/null
+++ b/tests/test-bloom-filter.cpp
@@ -0,0 +1,59 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2014-2018, The University of Memphis
+ *
+ * This file is part of PSync.
+ * See AUTHORS.md for complete list of PSync authors and contributors.
+ *
+ * PSync is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ *
+ * PSync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * PSync, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+#include "detail/bloom-filter.hpp"
+
+#include <boost/test/unit_test.hpp>
+#include <ndn-cxx/name.hpp>
+
+namespace psync {
+
+using namespace ndn;
+
+BOOST_AUTO_TEST_SUITE(TestBloomFilter)
+
+BOOST_AUTO_TEST_CASE(Basic)
+{
+ BloomFilter bf(100, 0.001);
+
+ std::string insertName("/memphis");
+ bf.insert(insertName);
+ BOOST_CHECK(bf.contains(insertName));
+}
+
+BOOST_AUTO_TEST_CASE(NameAppendAndExtract)
+{
+ Name bfName("/test");
+ BloomFilter bf(100, 0.001);
+ bf.insert("/memphis");
+
+ bf.appendToName(bfName);
+
+ BloomFilter bfFromName(100, 0.001, bfName.get(-1));
+
+ BOOST_CHECK_EQUAL(bfName.get(1).toNumber(), 100);
+ BOOST_CHECK_EQUAL(bfName.get(2).toNumber(), 1);
+ BOOST_CHECK_EQUAL(bf, bfFromName);
+
+ BOOST_CHECK_THROW(BloomFilter inCompatibleBf(200, 0.001, bfName.get(-1)), std::runtime_error);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace psync
\ No newline at end of file
diff --git a/tests/test-consumer.cpp b/tests/test-consumer.cpp
new file mode 100644
index 0000000..c67afaf
--- /dev/null
+++ b/tests/test-consumer.cpp
@@ -0,0 +1,63 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2014-2018, The University of Memphis
+ *
+ * This file is part of PSync.
+ * See AUTHORS.md for complete list of PSync authors and contributors.
+ *
+ * PSync is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ *
+ * PSync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * PSync, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+#include "consumer.hpp"
+
+#include <boost/test/unit_test.hpp>
+#include <ndn-cxx/name.hpp>
+#include <ndn-cxx/util/dummy-client-face.hpp>
+
+#include <iostream>
+
+namespace psync {
+
+using namespace ndn;
+using namespace std;
+
+BOOST_AUTO_TEST_SUITE(TestConsumer)
+
+BOOST_AUTO_TEST_CASE(Constructor)
+{
+ util::DummyClientFace face({true, true});
+ BOOST_REQUIRE_NO_THROW(Consumer(Name("/psync"),
+ face,
+ [] (const vector<Name>& availableSubs) {},
+ [] (const vector<MissingDataInfo>) {},
+ 40,
+ 0.001));
+}
+
+BOOST_AUTO_TEST_CASE(AddSubscription)
+{
+ util::DummyClientFace face({true, true});
+ Consumer consumer(Name("/psync"), face,
+ [] (const vector<Name>& availableSubs) {},
+ [] (const vector<MissingDataInfo>) {},
+ 40, 0.001);
+
+ Name subscription("test");
+
+ BOOST_CHECK(!consumer.isSubscribed(subscription));
+ BOOST_CHECK(consumer.addSubscription(subscription));
+ BOOST_CHECK(!consumer.addSubscription(subscription));
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace psync
\ No newline at end of file
diff --git a/tests/test-full-producer.cpp b/tests/test-full-producer.cpp
new file mode 100644
index 0000000..23d2ab5
--- /dev/null
+++ b/tests/test-full-producer.cpp
@@ -0,0 +1,57 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2014-2018, The University of Memphis
+ *
+ * This file is part of PSync.
+ * See AUTHORS.md for complete list of PSync authors and contributors.
+ *
+ * PSync is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ *
+ * PSync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * PSync, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+#include "full-producer.hpp"
+
+#include <boost/test/unit_test.hpp>
+#include <ndn-cxx/name.hpp>
+#include <ndn-cxx/util/dummy-client-face.hpp>
+#include <ndn-cxx/mgmt/nfd/control-parameters.hpp>
+
+#include <iostream>
+
+namespace psync {
+
+using namespace ndn;
+using namespace std;
+
+BOOST_AUTO_TEST_SUITE(TestFullProducer)
+
+BOOST_AUTO_TEST_CASE(Constructor)
+{
+ util::DummyClientFace face({true, true});
+ BOOST_REQUIRE_NO_THROW(FullProducer(40, face, Name("/psync"), Name("/testUser"), nullptr));
+}
+
+BOOST_AUTO_TEST_CASE(OnInterest)
+{
+ Name syncPrefix("/psync"), userNode("/testUser");
+ util::DummyClientFace face({true, true});
+
+ FullProducer node(40, face, syncPrefix, userNode, nullptr);
+
+ Name syncInterestName(syncPrefix);
+ syncInterestName.append("malicious-IBF");
+
+ BOOST_REQUIRE_NO_THROW(node.onInterest(syncPrefix, Interest(syncInterestName)));
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace psync
\ No newline at end of file
diff --git a/tests/test-full-sync.cpp b/tests/test-full-sync.cpp
new file mode 100644
index 0000000..3553c86
--- /dev/null
+++ b/tests/test-full-sync.cpp
@@ -0,0 +1,430 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2014-2018, The University of Memphis
+ *
+ * This file is part of PSync.
+ * See AUTHORS.md for complete list of PSync authors and contributors.
+ *
+ * PSync is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ *
+ * PSync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * PSync, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+#include "full-producer.hpp"
+#include "consumer.hpp"
+#include "unit-test-time-fixture.hpp"
+#include "detail/state.hpp"
+
+#include <boost/test/unit_test.hpp>
+#include <ndn-cxx/name.hpp>
+#include <ndn-cxx/util/dummy-client-face.hpp>
+
+#include <iostream>
+
+namespace psync {
+
+using namespace ndn;
+using namespace std;
+
+class FullSyncFixture : public tests::UnitTestTimeFixture
+{
+public:
+ FullSyncFixture()
+ : syncPrefix("psync")
+ {
+ }
+
+ void
+ addNode(int id)
+ {
+ faces[id] = std::make_shared<util::DummyClientFace>(io, util::DummyClientFace::Options{true, true});
+ userPrefixes[id] = Name("userPrefix" + to_string(id));
+ nodes[id] = make_shared<FullProducer>(40, *faces[id], syncPrefix, userPrefixes[id],
+ [] (const std::vector<MissingDataInfo>& updates) {});
+ }
+
+ Name syncPrefix;
+ shared_ptr<util::DummyClientFace> faces[4];
+ Name userPrefixes[4];
+ shared_ptr<FullProducer> nodes[4];
+};
+
+BOOST_FIXTURE_TEST_SUITE(FullSync, FullSyncFixture)
+
+BOOST_AUTO_TEST_CASE(TwoNodesSimple)
+{
+ addNode(0);
+ addNode(1);
+
+ faces[0]->linkTo(*faces[1]);
+ advanceClocks(ndn::time::milliseconds(10));
+
+ nodes[0]->publishName(userPrefixes[0]);
+ advanceClocks(ndn::time::milliseconds(10), 100);
+
+ BOOST_CHECK_EQUAL(nodes[0]->getSeqNo(userPrefixes[0]).value_or(-1), 1);
+ BOOST_CHECK_EQUAL(nodes[1]->getSeqNo(userPrefixes[0]).value_or(-1), 1);
+
+ nodes[1]->publishName(userPrefixes[1]);
+ advanceClocks(ndn::time::milliseconds(10), 100);
+ BOOST_CHECK_EQUAL(nodes[0]->getSeqNo(userPrefixes[1]).value_or(-1), 1);
+ BOOST_CHECK_EQUAL(nodes[1]->getSeqNo(userPrefixes[1]).value_or(-1), 1);
+
+ nodes[1]->publishName(userPrefixes[1]);
+ advanceClocks(ndn::time::milliseconds(10), 100);
+ BOOST_CHECK_EQUAL(nodes[0]->getSeqNo(userPrefixes[1]).value_or(-1), 2);
+ BOOST_CHECK_EQUAL(nodes[1]->getSeqNo(userPrefixes[1]).value_or(-1), 2);
+}
+
+BOOST_AUTO_TEST_CASE(TwoNodesForceSeqNo)
+{
+ addNode(0);
+ addNode(1);
+
+ faces[0]->linkTo(*faces[1]);
+ advanceClocks(ndn::time::milliseconds(10));
+
+ nodes[0]->publishName(userPrefixes[0], 3);
+ advanceClocks(ndn::time::milliseconds(10), 100);
+
+ BOOST_CHECK_EQUAL(nodes[0]->getSeqNo(userPrefixes[0]).value_or(-1), 3);
+ BOOST_CHECK_EQUAL(nodes[1]->getSeqNo(userPrefixes[0]).value_or(-1), 3);
+}
+
+BOOST_AUTO_TEST_CASE(TwoNodesWithMultipleUserNodes)
+{
+ addNode(0);
+ addNode(1);
+
+ faces[0]->linkTo(*faces[1]);
+ advanceClocks(ndn::time::milliseconds(10));
+
+ Name nodeZeroExtraUser("userPrefix0-1");
+ Name nodeOneExtraUser("userPrefix1-1");
+
+ nodes[0]->addUserNode(nodeZeroExtraUser);
+ nodes[1]->addUserNode(nodeOneExtraUser);
+
+ nodes[0]->publishName(userPrefixes[0]);
+ advanceClocks(ndn::time::milliseconds(10), 100);
+
+ BOOST_CHECK_EQUAL(nodes[0]->getSeqNo(userPrefixes[0]).value_or(-1), 1);
+ BOOST_CHECK_EQUAL(nodes[1]->getSeqNo(userPrefixes[0]).value_or(-1), 1);
+
+ nodes[0]->publishName(nodeZeroExtraUser);
+ advanceClocks(ndn::time::milliseconds(10), 100);
+
+ BOOST_CHECK_EQUAL(nodes[0]->getSeqNo(nodeZeroExtraUser).value_or(-1), 1);
+ BOOST_CHECK_EQUAL(nodes[1]->getSeqNo(nodeZeroExtraUser).value_or(-1), 1);
+
+ nodes[1]->publishName(nodeOneExtraUser);
+ advanceClocks(ndn::time::milliseconds(10), 100);
+
+ BOOST_CHECK_EQUAL(nodes[0]->getSeqNo(nodeOneExtraUser).value_or(-1), 1);
+ BOOST_CHECK_EQUAL(nodes[1]->getSeqNo(nodeOneExtraUser).value_or(-1), 1);
+}
+
+BOOST_AUTO_TEST_CASE(MultipleNodes)
+{
+ for (int i = 0; i < 4; i++) {
+ addNode(i);
+ }
+
+ for (int i = 0; i < 3; i++) {
+ faces[i]->linkTo(*faces[i + 1]);
+ }
+
+ nodes[0]->publishName(userPrefixes[0]);
+ advanceClocks(ndn::time::milliseconds(10), 100);
+ for (int i = 0; i < 4; i++) {
+ BOOST_CHECK_EQUAL(nodes[i]->getSeqNo(userPrefixes[0]).value_or(-1), 1);
+ }
+
+ nodes[1]->publishName(userPrefixes[1]);
+ advanceClocks(ndn::time::milliseconds(10), 100);
+ for (int i = 0; i < 4; i++) {
+ BOOST_CHECK_EQUAL(nodes[i]->getSeqNo(userPrefixes[1]).value_or(-1), 1);
+ }
+
+ nodes[1]->publishName(userPrefixes[1]);
+ advanceClocks(ndn::time::milliseconds(10), 100);
+ for (int i = 0; i < 4; i++) {
+ BOOST_CHECK_EQUAL(nodes[i]->getSeqNo(userPrefixes[1]).value_or(-1), 2);
+ }
+}
+
+BOOST_AUTO_TEST_CASE(MultipleNodesSimulataneousPublish)
+{
+ for (int i = 0; i < 4; i++) {
+ addNode(i);
+ }
+
+ for (int i = 0; i < 3; i++) {
+ faces[i]->linkTo(*faces[i + 1]);
+ }
+
+ for (int i = 0; i < 4; i++) {
+ nodes[i]->publishName(userPrefixes[i]);
+ }
+
+ advanceClocks(ndn::time::milliseconds(10), 100);
+ for (int i = 0; i < 4; i++) {
+ for (int j = 0; j < 4; j++) {
+ BOOST_CHECK_EQUAL(nodes[i]->getSeqNo(userPrefixes[j]).value_or(-1), 1);
+ }
+ }
+
+ for (int i = 0; i < 4; i++) {
+ nodes[i]->publishName(userPrefixes[i], 4);
+ }
+
+ advanceClocks(ndn::time::milliseconds(10), 100);
+ for (int i = 0; i < 4; i++) {
+ for (int j = 0; j < 4; j++) {
+ BOOST_CHECK_EQUAL(nodes[i]->getSeqNo(userPrefixes[j]).value_or(-1), 4);
+ }
+ }
+}
+
+BOOST_AUTO_TEST_CASE(NetworkPartition)
+{
+ for (int i = 0; i < 4; i++) {
+ addNode(i);
+ }
+
+ for (int i = 0; i < 3; i++) {
+ faces[i]->linkTo(*faces[i + 1]);
+ }
+
+ nodes[0]->publishName(userPrefixes[0]);
+ advanceClocks(ndn::time::milliseconds(10), 100);
+ for (int i = 0; i < 4; i++) {
+ BOOST_CHECK_EQUAL(nodes[i]->getSeqNo(userPrefixes[0]).value_or(-1), 1);
+ }
+
+ for (int i = 0; i < 3; i++) {
+ faces[i]->unlink();
+ }
+
+ faces[0]->linkTo(*faces[1]);
+ faces[2]->linkTo(*faces[3]);
+
+ nodes[0]->publishName(userPrefixes[0]);
+ advanceClocks(ndn::time::milliseconds(10), 100);
+ BOOST_CHECK_EQUAL(nodes[1]->getSeqNo(userPrefixes[0]).value_or(-1), 2);
+ BOOST_CHECK_EQUAL(nodes[2]->getSeqNo(userPrefixes[0]).value_or(-1), 1);
+ BOOST_CHECK_EQUAL(nodes[3]->getSeqNo(userPrefixes[0]).value_or(-1), 1);
+
+ nodes[1]->publishName(userPrefixes[1], 2);
+ advanceClocks(ndn::time::milliseconds(10), 100);
+ BOOST_CHECK_EQUAL(nodes[0]->getSeqNo(userPrefixes[1]).value_or(-1), 2);
+
+ nodes[2]->publishName(userPrefixes[2], 2);
+ advanceClocks(ndn::time::milliseconds(10), 100);
+ BOOST_CHECK_EQUAL(nodes[3]->getSeqNo(userPrefixes[2]).value_or(-1), 2);
+
+ nodes[3]->publishName(userPrefixes[3], 2);
+ advanceClocks(ndn::time::milliseconds(10), 100);
+ BOOST_CHECK_EQUAL(nodes[2]->getSeqNo(userPrefixes[3]).value_or(-1), 2);
+
+ BOOST_CHECK_EQUAL(nodes[0]->getSeqNo(userPrefixes[3]).value_or(-1), -1);
+ BOOST_CHECK_EQUAL(nodes[1]->getSeqNo(userPrefixes[3]).value_or(-1), -1);
+
+ for (int i = 0; i < 3; i++) {
+ faces[i]->unlink();
+ }
+
+ for (int i = 0; i < 3; i++) {
+ faces[i]->linkTo(*faces[i + 1]);
+ }
+
+ advanceClocks(ndn::time::milliseconds(10), 100);
+ for (int i = 0; i < 4; i++) {
+ for (int j = 0; j < 4; j++) {
+ BOOST_CHECK_EQUAL(nodes[i]->getSeqNo(userPrefixes[j]).value_or(-1), 2);
+ }
+ }
+}
+
+BOOST_AUTO_TEST_CASE(IBFOverflow)
+{
+ addNode(0);
+ addNode(1);
+
+ faces[0]->linkTo(*faces[1]);
+ advanceClocks(ndn::time::milliseconds(10));
+
+ // 50 > 40 (expected number of entries in IBF)
+ for (int i = 0; i < 50; i++) {
+ nodes[0]->addUserNode(Name("userNode0-" + to_string(i)));
+ }
+
+ for (int i = 0; i < 20; i++) {
+ // Suppose all sync data were lost for these:
+ nodes[0]->updateSeqNo(Name("userNode0-" + to_string(i)), 1);
+ }
+ nodes[0]->publishName(Name("userNode0-" + to_string(20)));
+ advanceClocks(ndn::time::milliseconds(10), 100);
+
+ for (int i = 0; i <= 20; i++) {
+ Name userPrefix("userNode0-" + to_string(i));
+ BOOST_CHECK_EQUAL(nodes[1]->getSeqNo(userPrefix).value_or(-1), 1);
+ }
+
+ for (int i = 21; i < 49; i++) {
+ nodes[0]->updateSeqNo(Name("userNode0-" + to_string(i)), 1);
+ }
+ nodes[0]->publishName(Name("userNode0-49"));
+ advanceClocks(ndn::time::milliseconds(10), 100);
+
+ for (int i = 21; i < 49; i++) {
+ Name userPrefix("userNode0-" + to_string(i));
+ BOOST_CHECK_EQUAL(nodes[1]->getSeqNo(userPrefix).value_or(-1), 1);
+ }
+}
+
+BOOST_AUTO_TEST_CASE(DiffIBFDecodeFailureSimple)
+{
+ addNode(0);
+ addNode(1);
+
+ faces[0]->linkTo(*faces[1]);
+ advanceClocks(ndn::time::milliseconds(10));
+
+ // Lowest number that triggers a decode failure for IBF size of 40
+ int totalUpdates = 47;
+
+ for (int i = 0; i <= totalUpdates; i++) {
+ nodes[0]->addUserNode(Name("userNode0-" + to_string(i)));
+ if (i != totalUpdates) {
+ nodes[0]->updateSeqNo(Name("userNode0-" + to_string(i)), 1);
+ }
+ }
+ nodes[0]->publishName(Name("userNode0-" + to_string(totalUpdates)));
+ advanceClocks(ndn::time::milliseconds(10), 100);
+
+ // No mechanism to recover yet
+ for (int i = 0; i <= totalUpdates; i++) {
+ Name userPrefix("userNode0-" + to_string(i));
+ BOOST_CHECK_EQUAL(nodes[1]->getSeqNo(userPrefix).value_or(-1), 1);
+ }
+
+ BOOST_CHECK_EQUAL(nodes[0]->getSeqNo(userPrefixes[1]).value_or(-1), -1);
+ BOOST_CHECK_EQUAL(nodes[1]->getSeqNo(userPrefixes[0]).value_or(-1), -1);
+
+ nodes[1]->publishName(userPrefixes[1]);
+ advanceClocks(ndn::time::milliseconds(10), 100);
+ BOOST_CHECK_EQUAL(nodes[0]->getSeqNo(userPrefixes[1]).value_or(-1), 1);
+
+ nodes[0]->publishName(userPrefixes[0]);
+ advanceClocks(ndn::time::milliseconds(10), 100);
+ BOOST_CHECK_EQUAL(nodes[1]->getSeqNo(userPrefixes[0]).value_or(-1), 1);
+}
+
+BOOST_AUTO_TEST_CASE(DiffIBFDecodeFailureSimpleSegmentedRecovery)
+{
+ addNode(0);
+ addNode(1);
+
+ // Simple content store
+ faces[0]->onSendInterest.connect([this] (const Interest& interest) {
+ for (const auto& data : faces[1]->sentData) {
+ if (data.getName() == interest.getName()) {
+ faces[0]->receive(data);
+ return;
+ }
+ }
+ faces[1]->receive(interest);
+ });
+
+ faces[0]->onSendData.connect([this] (const Data& data) {
+ faces[1]->receive(data);
+ });
+
+ faces[1]->onSendInterest.connect([this] (const Interest& interest) {
+ for (const auto& data : faces[0]->sentData) {
+ if (data.getName() == interest.getName()) {
+ faces[1]->receive(data);
+ return;
+ }
+ }
+ faces[0]->receive(interest);
+ });
+
+ faces[1]->onSendData.connect([this] (const Data& data) {
+ faces[0]->receive(data);
+ });
+
+ advanceClocks(ndn::time::milliseconds(10));
+
+ // Lowest number that triggers a decode failure for IBF size of 40
+ int totalUpdates = 270;
+
+ for (int i = 0; i <= totalUpdates; i++) {
+ nodes[0]->addUserNode(Name("userNode0-" + to_string(i)));
+ if (i != totalUpdates) {
+ nodes[0]->updateSeqNo(Name("userNode0-" + to_string(i)), 1);
+ }
+ }
+ nodes[0]->publishName(Name("userNode0-" + to_string(totalUpdates)));
+ advanceClocks(ndn::time::milliseconds(10), 100);
+
+ // No mechanism to recover yet
+ for (int i = 0; i <= totalUpdates; i++) {
+ Name userPrefix("userNode0-" + to_string(i));
+ BOOST_CHECK_EQUAL(nodes[1]->getSeqNo(userPrefix).value_or(-1), 1);
+ }
+
+ BOOST_CHECK_EQUAL(nodes[0]->getSeqNo(userPrefixes[1]).value_or(-1), -1);
+ BOOST_CHECK_EQUAL(nodes[1]->getSeqNo(userPrefixes[0]).value_or(-1), -1);
+
+ nodes[1]->publishName(userPrefixes[1]);
+ advanceClocks(ndn::time::milliseconds(10), 100);
+ BOOST_CHECK_EQUAL(nodes[0]->getSeqNo(userPrefixes[1]).value_or(-1), 1);
+
+ nodes[0]->publishName(userPrefixes[0]);
+ advanceClocks(ndn::time::milliseconds(10), 100);
+ BOOST_CHECK_EQUAL(nodes[1]->getSeqNo(userPrefixes[0]).value_or(-1), 1);
+}
+
+BOOST_AUTO_TEST_CASE(DiffIBFDecodeFailureMultipleNodes)
+{
+ for (int i = 0; i < 4; i++) {
+ addNode(i);
+ }
+
+ for (int i = 0; i < 3; i++) {
+ faces[i]->linkTo(*faces[i + 1]);
+ }
+
+ // Lowest number that triggers a decode failure for IBF size of 40
+ int totalUpdates = 47;
+
+ for (int i = 0; i <= totalUpdates; i++) {
+ nodes[0]->addUserNode(Name("userNode0-" + to_string(i)));
+ if (i != totalUpdates) {
+ nodes[0]->updateSeqNo(Name("userNode0-" + to_string(i)), 1);
+ }
+ }
+ nodes[0]->publishName(Name("userNode0-" + to_string(totalUpdates)));
+ advanceClocks(ndn::time::milliseconds(10), 100);
+
+ // No mechanism to recover yet
+ for (int i = 0; i <= totalUpdates; i++) {
+ Name userPrefix("userNode0-" + to_string(i));
+ for (int j = 0; j < 4; j++) {
+ BOOST_CHECK_EQUAL(nodes[j]->getSeqNo(userPrefix).value_or(-1), 1);
+ }
+ }
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace psync
\ No newline at end of file
diff --git a/tests/test-iblt.cpp b/tests/test-iblt.cpp
new file mode 100644
index 0000000..3934938
--- /dev/null
+++ b/tests/test-iblt.cpp
@@ -0,0 +1,201 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2014-2018, The University of Memphis
+ *
+ * This file is part of PSync.
+ * See AUTHORS.md for complete list of PSync authors and contributors.
+ *
+ * PSync is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ *
+ * PSync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * PSync, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+#include "detail/iblt.hpp"
+#include "detail/util.hpp"
+
+#include <boost/test/unit_test.hpp>
+#include <ndn-cxx/name.hpp>
+#include <ndn-cxx/interest.hpp>
+
+namespace psync {
+
+using namespace ndn;
+
+BOOST_AUTO_TEST_SUITE(TestIBLT)
+
+BOOST_AUTO_TEST_CASE(Equal)
+{
+ int size = 10;
+
+ IBLT iblt1(size);
+ IBLT iblt2(size);
+ BOOST_CHECK_EQUAL(iblt1, iblt2);
+
+ std::string prefix = Name("/test/memphis").appendNumber(1).toUri();
+ uint32_t newHash = murmurHash3(11, prefix);
+ iblt1.insert(newHash);
+ iblt2.insert(newHash);
+
+ BOOST_CHECK_EQUAL(iblt1, iblt2);
+
+ Name ibfName1("sync"), ibfName2("sync");
+ iblt1.appendToName(ibfName1);
+ iblt2.appendToName(ibfName2);
+ BOOST_CHECK_EQUAL(ibfName1, ibfName2);
+}
+
+BOOST_AUTO_TEST_CASE(NameAppendAndExtract)
+{
+ int size = 10;
+
+ IBLT iblt(size);
+ std::string prefix = Name("/test/memphis").appendNumber(1).toUri();
+ uint32_t newHash = murmurHash3(11, prefix);
+ iblt.insert(newHash);
+
+ Name ibltName("sync");
+ iblt.appendToName(ibltName);
+
+ IBLT rcvd(size);
+ rcvd.initialize(ibltName.get(-1));
+
+ BOOST_CHECK_EQUAL(iblt, rcvd);
+
+ IBLT rcvdDiffSize(20);
+ BOOST_CHECK_THROW(rcvdDiffSize.initialize(ibltName.get(-1)), std::runtime_error);
+}
+
+BOOST_AUTO_TEST_CASE(CopyInsertErase)
+{
+ int size = 10;
+
+ IBLT iblt1(size);
+
+ std::string prefix = Name("/test/memphis").appendNumber(1).toUri();
+ uint32_t hash1 = murmurHash3(11, prefix);
+ iblt1.insert(hash1);
+
+ IBLT iblt2(iblt1);
+ iblt2.erase(hash1);
+ prefix = Name("/test/memphis").appendNumber(2).toUri();
+ uint32_t hash3 = murmurHash3(11, prefix);
+ iblt2.insert(hash3);
+
+ iblt1.erase(hash1);
+ prefix = Name("/test/memphis").appendNumber(5).toUri();
+ uint32_t hash5 = murmurHash3(11, prefix);
+ iblt1.insert(hash5);
+
+ iblt2.erase(hash3);
+ iblt2.insert(hash5);
+
+ BOOST_CHECK_EQUAL(iblt1, iblt2);
+}
+
+BOOST_AUTO_TEST_CASE(HigherSeqTest)
+{
+ // The case where we can't recognize if the rcvd IBF has higher sequence number
+ // Relevant to full sync case
+ int size = 10;
+
+ IBLT ownIBF(size);
+ IBLT rcvdIBF(size);
+
+ std::string prefix = Name("/test/memphis").appendNumber(3).toUri();
+ uint32_t hash1 = murmurHash3(11, prefix);
+ ownIBF.insert(hash1);
+
+ std::string prefix2 = Name("/test/memphis").appendNumber(4).toUri();
+ uint32_t hash2 = murmurHash3(11, prefix2);
+ rcvdIBF.insert(hash2);
+
+ IBLT diff = ownIBF - rcvdIBF;
+ std::set<uint32_t> positive;
+ std::set<uint32_t> negative;
+
+ BOOST_CHECK(diff.listEntries(positive, negative));
+ BOOST_CHECK(*positive.begin() == hash1);
+ BOOST_CHECK(*negative.begin() == hash2);
+}
+
+BOOST_AUTO_TEST_CASE(Difference)
+{
+ int size = 10;
+
+ IBLT ownIBF(size);
+
+ IBLT rcvdIBF = ownIBF;
+
+ IBLT diff = ownIBF - rcvdIBF;
+
+ std::set<uint32_t> positive; // non-empty Positive means we have some elements that the others don't
+ std::set<uint32_t> negative;
+
+ BOOST_CHECK(diff.listEntries(positive, negative));
+ BOOST_CHECK_EQUAL(positive.size(), 0);
+ BOOST_CHECK_EQUAL(negative.size(), 0);
+
+ std::string prefix = Name("/test/memphis").appendNumber(1).toUri();
+ uint32_t newHash = murmurHash3(11, prefix);
+ ownIBF.insert(newHash);
+
+ diff = ownIBF - rcvdIBF;
+ BOOST_CHECK(diff.listEntries(positive, negative));
+ BOOST_CHECK_EQUAL(positive.size(), 1);
+ BOOST_CHECK_EQUAL(negative.size(), 0);
+
+ prefix = Name("/test/csu").appendNumber(1).toUri();
+ newHash = murmurHash3(11, prefix);
+ rcvdIBF.insert(newHash);
+
+ diff = ownIBF - rcvdIBF;
+ BOOST_CHECK(diff.listEntries(positive, negative));
+ BOOST_CHECK_EQUAL(positive.size(), 1);
+ BOOST_CHECK_EQUAL(negative.size(), 1);
+}
+
+BOOST_AUTO_TEST_CASE(DifferenceBwOversizedIBFs)
+{
+ // Insert 50 elements into IBF of size 10
+ // Check that we can still list the difference
+ // even though we can't list the IBFs itself
+
+ int size = 10;
+
+ IBLT ownIBF(size);
+
+ for (int i = 0; i < 50; i++) {
+ std::string prefix = Name("/test/memphis" + std::to_string(i)).appendNumber(1).toUri();
+ uint32_t newHash = murmurHash3(11, prefix);
+ ownIBF.insert(newHash);
+ }
+
+ IBLT rcvdIBF = ownIBF;
+
+ std::string prefix = Name("/test/ucla").appendNumber(1).toUri();
+ uint32_t newHash = murmurHash3(11, prefix);
+ ownIBF.insert(newHash);
+
+ IBLT diff = ownIBF - rcvdIBF;
+
+ std::set<uint32_t> positive;
+ std::set<uint32_t> negative;
+ BOOST_CHECK(diff.listEntries(positive, negative));
+ BOOST_CHECK_EQUAL(positive.size(), 1);
+ BOOST_CHECK_EQUAL(*positive.begin(), newHash);
+ BOOST_CHECK_EQUAL(negative.size(), 0);
+
+ BOOST_CHECK(!ownIBF.listEntries(positive, negative));
+ BOOST_CHECK(!rcvdIBF.listEntries(positive, negative));
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace psync
\ No newline at end of file
diff --git a/tests/test-partial-producer.cpp b/tests/test-partial-producer.cpp
new file mode 100644
index 0000000..3bed1ac
--- /dev/null
+++ b/tests/test-partial-producer.cpp
@@ -0,0 +1,148 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2014-2018, The University of Memphis
+ *
+ * This file is part of PSync.
+ * See AUTHORS.md for complete list of PSync authors and contributors.
+ *
+ * PSync is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ *
+ * PSync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * PSync, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+#include "partial-producer.hpp"
+
+#include <boost/test/unit_test.hpp>
+#include <ndn-cxx/name.hpp>
+#include <ndn-cxx/util/dummy-client-face.hpp>
+#include <ndn-cxx/mgmt/nfd/control-parameters.hpp>
+
+#include <iostream>
+
+namespace psync {
+
+using namespace ndn;
+using namespace std;
+
+BOOST_AUTO_TEST_SUITE(TestPartialProducer)
+
+BOOST_AUTO_TEST_CASE(Constructor)
+{
+ util::DummyClientFace face({true, true});
+ BOOST_REQUIRE_NO_THROW(PartialProducer(40, face, Name("/psync"), Name("/testUser")));
+}
+
+BOOST_AUTO_TEST_CASE(RegisterPrefix)
+{
+ Name syncPrefix("/psync"), userNode("/testUser");
+ util::DummyClientFace face({true, true});
+ PartialProducer producer(40, face, syncPrefix, userNode);
+
+ face.processEvents(time::milliseconds(-1));
+
+ BOOST_REQUIRE_EQUAL(face.sentInterests.size(), 1);
+
+ face.sentInterests.back();
+ Interest interest = *face.sentInterests.begin();
+ BOOST_CHECK_EQUAL(interest.getName().get(3), name::Component("register"));
+ name::Component test = interest.getName().get(4);
+ nfd::ControlParameters params(test.blockFromValue());
+ BOOST_CHECK_EQUAL(params.getName(), syncPrefix);
+}
+
+BOOST_AUTO_TEST_CASE(PublishName)
+{
+ Name syncPrefix("/psync"), userNode("/testUser"), nonUser("/testUser2");
+ util::DummyClientFace face({true, true});
+ PartialProducer producer(40, face, syncPrefix, userNode);
+
+ BOOST_CHECK_EQUAL(producer.getSeqNo(userNode).value_or(-1), 0);
+ producer.publishName(userNode);
+ BOOST_CHECK_EQUAL(producer.getSeqNo(userNode).value_or(-1), 1);
+
+ producer.publishName(userNode);
+ BOOST_CHECK_EQUAL(producer.getSeqNo(userNode).value_or(-1), 2);
+
+ producer.publishName(userNode, 10);
+ BOOST_CHECK_EQUAL(producer.getSeqNo(userNode).value_or(-1), 10);
+
+ producer.publishName(nonUser);
+ BOOST_CHECK_EQUAL(producer.getSeqNo(nonUser).value_or(-1), -1);
+}
+
+BOOST_AUTO_TEST_CASE(SameSyncInterest)
+{
+ Name syncPrefix("/psync"), userNode("/testUser");
+ util::DummyClientFace face({true, true});
+ PartialProducer producer(40, face, syncPrefix, userNode);
+
+ Name syncInterestName(syncPrefix);
+ syncInterestName.append("sync");
+
+ BloomFilter bf(20, 0.001);
+ bf.appendToName(syncInterestName);
+
+ producer.m_iblt.appendToName(syncInterestName);
+
+ Interest syncInterest(syncInterestName);
+ syncInterest.setInterestLifetime(time::milliseconds(1000));
+ syncInterest.setNonce(1);
+ BOOST_REQUIRE_NO_THROW(producer.onSyncInterest(syncInterestName, syncInterest));
+ face.processEvents(time::milliseconds(10));
+ BOOST_CHECK_EQUAL(producer.m_pendingEntries.size(), 1);
+
+ face.processEvents(time::milliseconds(500));
+
+ // Same interest again - size of pending interest should remain same, but expirationEvent should change
+ syncInterest.setNonce(2);
+ BOOST_REQUIRE_NO_THROW(producer.onSyncInterest(syncInterestName, syncInterest));
+ face.processEvents(time::milliseconds(10));
+ BOOST_CHECK_EQUAL(producer.m_pendingEntries.size(), 1);
+
+ face.processEvents(time::milliseconds(500));
+ BOOST_CHECK_EQUAL(producer.m_pendingEntries.size(), 1);
+
+ face.processEvents(time::milliseconds(500));
+ BOOST_CHECK_EQUAL(producer.m_pendingEntries.size(), 0);
+}
+
+BOOST_AUTO_TEST_CASE(OnSyncInterest)
+{
+ Name syncPrefix("/psync"), userNode("/testUser");
+ util::DummyClientFace face({true, true});
+ PartialProducer producer(40, face, syncPrefix, userNode);
+
+ // Sync interest with no bloom filter attached
+ Name syncInterestName(syncPrefix);
+ syncInterestName.append("sync");
+ producer.m_iblt.appendToName(syncInterestName);
+ BOOST_REQUIRE_NO_THROW(producer.onSyncInterest(syncInterestName, Interest(syncInterestName)));
+
+ // Sync interest with malicious bloom filter
+ syncInterestName = syncPrefix;
+ syncInterestName.append("sync");
+ syncInterestName.appendNumber(20); // count of bloom filter
+ syncInterestName.appendNumber(1); // false positive probability * 1000 of bloom filter
+ syncInterestName.append("fake-name");
+ producer.m_iblt.appendToName(syncInterestName);
+ BOOST_REQUIRE_NO_THROW(producer.onSyncInterest(syncInterestName, Interest(syncInterestName)));
+
+ // Sync interest with malicious IBF
+ syncInterestName = syncPrefix;
+ syncInterestName.append("sync");
+ BloomFilter bf(20, 0.001);
+ bf.appendToName(syncInterestName);
+ syncInterestName.append("fake-name");
+ BOOST_REQUIRE_NO_THROW(producer.onSyncInterest(syncInterestName, Interest(syncInterestName)));
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace psync
\ No newline at end of file
diff --git a/tests/test-partial-sync.cpp b/tests/test-partial-sync.cpp
new file mode 100644
index 0000000..0b2800a
--- /dev/null
+++ b/tests/test-partial-sync.cpp
@@ -0,0 +1,349 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2014-2018, The University of Memphis
+ *
+ * This file is part of PSync.
+ * See AUTHORS.md for complete list of PSync authors and contributors.
+ *
+ * PSync is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ *
+ * PSync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * PSync, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+#include "partial-producer.hpp"
+#include "consumer.hpp"
+#include "unit-test-time-fixture.hpp"
+
+#include <boost/test/unit_test.hpp>
+#include <ndn-cxx/name.hpp>
+#include <ndn-cxx/util/dummy-client-face.hpp>
+
+#include <iostream>
+
+namespace psync {
+
+using namespace ndn;
+using namespace std;
+
+class PartialSyncFixture : public tests::UnitTestTimeFixture
+{
+public:
+ PartialSyncFixture()
+ : face(io, {true, true})
+ , syncPrefix("psync")
+ , userPrefix("testUser-0")
+ , numHelloDataRcvd(0)
+ , numSyncDataRcvd(0)
+ {
+ producer = make_shared<PartialProducer>(40, face, syncPrefix, userPrefix);
+ addUserNodes("testUser", 10);
+ }
+
+ void
+ addConsumer(int id, const vector<string>& subscribeTo)
+ {
+ consumerFaces[id] = make_shared<util::DummyClientFace>(io, util::DummyClientFace::Options{true, true});
+
+ face.linkTo(*consumerFaces[id]);
+
+ consumers[id] = make_shared<Consumer>(syncPrefix, *consumerFaces[id],
+ [&, id] (const vector<Name>& availableSubs)
+ {
+ numHelloDataRcvd++;
+ checkSubList(availableSubs);
+
+ checkIBFUpdated(id);
+
+ for (const auto& sub : subscribeTo) {
+ consumers[id]->addSubscription(sub);
+ }
+ consumers[id]->sendSyncInterest();
+ },
+ [&, id] (const std::vector<MissingDataInfo>& updates) {
+ numSyncDataRcvd++;
+
+ checkIBFUpdated(id);
+
+ for (const auto& update : updates) {
+ BOOST_CHECK(consumers[id]->isSubscribed(update.prefix));
+ BOOST_CHECK_EQUAL(oldSeqMap.at(update.prefix) + 1, update.lowSeq);
+ BOOST_CHECK_EQUAL(producer->m_prefixes.at(update.prefix), update.highSeq);
+ BOOST_CHECK_EQUAL(consumers[id]->getSeqNo(update.prefix).value(), update.highSeq);
+ }
+ }, 40, 0.001);
+
+ advanceClocks(ndn::time::milliseconds(10));
+ }
+
+ void
+ checkIBFUpdated(int id)
+ {
+ Name emptyName;
+ producer->m_iblt.appendToName(emptyName);
+ BOOST_CHECK_EQUAL(consumers[id]->m_iblt, emptyName);
+ }
+
+ bool
+ checkSubList(const vector<Name>& availableSubs)
+ {
+ for (const auto& prefix : producer->m_prefixes ) {
+ for (const auto& sub : availableSubs) {
+ if (prefix.first != sub) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ void
+ addUserNodes(const std::string& prefix, int numOfUserNodes)
+ {
+ // zeroth is added through constructor
+ for (int i = 1; i < numOfUserNodes; i++) {
+ producer->addUserNode(prefix + "-" + to_string(i));
+ }
+ }
+
+ void
+ publishUpdateFor(const std::string& prefix)
+ {
+ oldSeqMap = producer->m_prefixes;
+ producer->publishName(prefix);
+ advanceClocks(ndn::time::milliseconds(10));
+ }
+
+ void
+ updateSeqFor(const std::string& prefix, uint64_t seq)
+ {
+ oldSeqMap = producer->m_prefixes;
+ producer->updateSeqNo(prefix, seq);
+ }
+
+ util::DummyClientFace face;
+ Name syncPrefix;
+ Name userPrefix;
+
+ shared_ptr<PartialProducer> producer;
+ std::map <ndn::Name, uint64_t> oldSeqMap;
+
+ shared_ptr<Consumer> consumers[3];
+ shared_ptr<util::DummyClientFace> consumerFaces[3];
+ int numHelloDataRcvd;
+ int numSyncDataRcvd;
+};
+
+BOOST_FIXTURE_TEST_SUITE(PartialSync, PartialSyncFixture)
+
+BOOST_AUTO_TEST_CASE(Simple)
+{
+ vector<string> subscribeTo{"testUser-2", "testUser-4", "testUser-6"};
+ addConsumer(0, subscribeTo);
+
+ consumers[0]->sendHelloInterest();
+ advanceClocks(ndn::time::milliseconds(10));
+ BOOST_CHECK_EQUAL(numHelloDataRcvd, 1);
+
+ publishUpdateFor("testUser-2");
+ BOOST_CHECK_EQUAL(numSyncDataRcvd, 1);
+ publishUpdateFor("testUser-3");
+ BOOST_CHECK_EQUAL(numSyncDataRcvd, 1);
+ publishUpdateFor("testUser-2");
+ BOOST_CHECK_EQUAL(numSyncDataRcvd, 2);
+}
+
+BOOST_AUTO_TEST_CASE(MissedUpdate)
+{
+ vector<string> subscribeTo{"testUser-2", "testUser-4", "testUser-6"};
+ addConsumer(0, subscribeTo);
+
+ consumers[0]->sendHelloInterest();
+ advanceClocks(ndn::time::milliseconds(10));
+ BOOST_CHECK_EQUAL(numHelloDataRcvd, 1);
+
+ updateSeqFor("testUser-2", 3);
+ BOOST_CHECK_EQUAL(numSyncDataRcvd, 0);
+
+ // The sync interest sent after hello will timeout
+ advanceClocks(ndn::time::milliseconds(1000));
+ BOOST_CHECK_EQUAL(numSyncDataRcvd, 0);
+
+ // Next sync interest will bring back the sync data
+ advanceClocks(ndn::time::milliseconds(1000));
+ BOOST_CHECK_EQUAL(numSyncDataRcvd, 1);
+}
+
+BOOST_AUTO_TEST_CASE(LateSubscription)
+{
+ vector<string> subscribeTo{"testUser-2", "testUser-4", "testUser-6"};
+ addConsumer(0, subscribeTo);
+
+ consumers[0]->sendHelloInterest();
+ advanceClocks(ndn::time::milliseconds(10));
+
+ BOOST_CHECK_EQUAL(numHelloDataRcvd, 1);
+ publishUpdateFor("testUser-2");
+ BOOST_CHECK_EQUAL(numSyncDataRcvd, 1);
+
+ consumers[0]->addSubscription("testUser-3");
+ consumers[0]->sendSyncInterest();
+ publishUpdateFor("testUser-3");
+ BOOST_CHECK_EQUAL(numSyncDataRcvd, 2);
+}
+
+BOOST_AUTO_TEST_CASE(ConsumerSyncTimeout)
+{
+ vector<string> subscribeTo{"testUser-2", "testUser-4", "testUser-6"};
+ addConsumer(0, subscribeTo);
+
+ consumers[0]->sendHelloInterest();
+ BOOST_CHECK_EQUAL(producer->m_pendingEntries.size(), 0);
+ advanceClocks(ndn::time::milliseconds(10));
+ BOOST_CHECK_EQUAL(producer->m_pendingEntries.size(), 1);
+ advanceClocks(ndn::time::milliseconds(10), 100);
+ BOOST_CHECK_EQUAL(producer->m_pendingEntries.size(), 0);
+ advanceClocks(ndn::time::milliseconds(10), 100);
+
+ int numSyncInterests = 0;
+ for (const auto& interest : consumerFaces[0]->sentInterests) {
+ if (interest.getName().getSubName(0, 2) == Name("/psync/sync")) {
+ numSyncInterests++;
+ }
+ }
+ BOOST_CHECK_EQUAL(numSyncInterests, 2);
+ BOOST_CHECK_EQUAL(numSyncDataRcvd, 0);
+}
+
+BOOST_AUTO_TEST_CASE(MultipleConsumersWithSameSubList)
+{
+ vector<string> subscribeTo{"testUser-2", "testUser-4", "testUser-6"};
+ addConsumer(0, subscribeTo);
+ addConsumer(1, subscribeTo);
+ addConsumer(2, subscribeTo);
+
+ consumers[0]->sendHelloInterest();
+ consumers[1]->sendHelloInterest();
+ consumers[2]->sendHelloInterest();
+ advanceClocks(ndn::time::milliseconds(10));
+
+ BOOST_CHECK_EQUAL(numHelloDataRcvd, 3);
+
+ publishUpdateFor("testUser-2");
+ BOOST_CHECK_EQUAL(numSyncDataRcvd, 3);
+
+ publishUpdateFor("testUser-3");
+ BOOST_CHECK_EQUAL(numSyncDataRcvd, 3);
+}
+
+BOOST_AUTO_TEST_CASE(MultipleConsumersWithDifferentSubList)
+{
+ vector<string> subscribeTo{"testUser-2", "testUser-4", "testUser-6"};
+ addConsumer(0, subscribeTo);
+
+ vector<string> subscribeTo1{"testUser-1", "testUser-3", "testUser-5"};
+ addConsumer(1, subscribeTo1);
+
+ vector<string> subscribeTo2{"testUser-2", "testUser-3"};
+ addConsumer(2, subscribeTo2);
+
+ consumers[0]->sendHelloInterest();
+ consumers[1]->sendHelloInterest();
+ consumers[2]->sendHelloInterest();
+ advanceClocks(ndn::time::milliseconds(10));
+
+ BOOST_CHECK_EQUAL(numHelloDataRcvd, 3);
+
+ publishUpdateFor("testUser-2");
+ BOOST_CHECK_EQUAL(numSyncDataRcvd, 2);
+
+ numSyncDataRcvd = 0;
+ publishUpdateFor("testUser-3");
+ BOOST_CHECK_EQUAL(numSyncDataRcvd, 2);
+}
+
+BOOST_AUTO_TEST_CASE(ReplicatedProducer)
+{
+ vector<string> subscribeTo{"testUser-2", "testUser-4", "testUser-6"};
+ addConsumer(0, subscribeTo);
+
+ consumers[0]->sendHelloInterest();
+ advanceClocks(ndn::time::milliseconds(10));
+ BOOST_CHECK_EQUAL(numHelloDataRcvd, 1);
+
+ publishUpdateFor("testUser-2");
+ BOOST_CHECK_EQUAL(numSyncDataRcvd, 1);
+
+ // Link to first producer goes down
+ face.unlink();
+
+ util::DummyClientFace face2(io, {true, true});
+ PartialProducer replicatedProducer(40, face2, syncPrefix, userPrefix);
+ for (int i = 1; i < 10; i++) {
+ replicatedProducer.addUserNode("testUser-" + to_string(i));
+ }
+ advanceClocks(ndn::time::milliseconds(10));
+ replicatedProducer.publishName("testUser-2");
+ // Link to a replicated producer comes up
+ face2.linkTo(*consumerFaces[0]);
+
+ BOOST_CHECK_EQUAL(face2.sentData.size(), 0);
+
+ // Update in first producer as well so consumer on sync data
+ // callback checks still pass
+ publishUpdateFor("testUser-2");
+ replicatedProducer.publishName("testUser-2");
+ advanceClocks(ndn::time::milliseconds(15), 100);
+ BOOST_CHECK_EQUAL(numSyncDataRcvd, 2);
+ BOOST_CHECK_EQUAL(face2.sentData.size(), 1);
+}
+
+BOOST_AUTO_TEST_CASE(ApplicationNack)
+{
+ // 50 is more than expected number of entries of 40 in the producer's IBF
+ addUserNodes("testUser", 50);
+
+ vector<string> subscribeTo{"testUser-2", "testUser-4", "testUser-6"};
+ addConsumer(0, subscribeTo);
+
+ consumers[0]->sendHelloInterest();
+ advanceClocks(ndn::time::milliseconds(10));
+ BOOST_CHECK_EQUAL(numHelloDataRcvd, 1);
+
+ publishUpdateFor("testUser-2");
+ BOOST_CHECK_EQUAL(numSyncDataRcvd, 1);
+
+ oldSeqMap = producer->m_prefixes;
+ for (int i = 0; i < 50; i++) {
+ ndn::Name prefix("testUser-" + to_string(i));
+ producer->updateSeqNo(prefix, producer->getSeqNo(prefix).value() + 1);
+ }
+ // Next sync interest should trigger the nack
+ advanceClocks(ndn::time::milliseconds(15), 100);
+
+ // Nack does not contain any content so still should be 1
+ BOOST_CHECK_EQUAL(numSyncDataRcvd, 1);
+
+ bool nackRcvd = false;
+ for (const auto& data : face.sentData) {
+ if (data.getContentType() == ndn::tlv::ContentType_Nack) {
+ nackRcvd = true;
+ break;
+ }
+ }
+ BOOST_CHECK(nackRcvd);
+
+ producer->publishName("testUser-4");
+ advanceClocks(ndn::time::milliseconds(10));
+ BOOST_CHECK_EQUAL(numSyncDataRcvd, 2);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace psync
\ No newline at end of file
diff --git a/tests/test-producer-base.cpp b/tests/test-producer-base.cpp
new file mode 100644
index 0000000..9664b04
--- /dev/null
+++ b/tests/test-producer-base.cpp
@@ -0,0 +1,85 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2014-2018, The University of Memphis
+ *
+ * This file is part of PSync.
+ * See AUTHORS.md for complete list of PSync authors and contributors.
+ *
+ * PSync is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ *
+ * PSync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * PSync, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+#include "producer-base.hpp"
+#include "detail/util.hpp"
+
+#include <boost/test/unit_test.hpp>
+#include <ndn-cxx/name.hpp>
+#include <ndn-cxx/data.hpp>
+#include <ndn-cxx/interest.hpp>
+#include <ndn-cxx/util/dummy-client-face.hpp>
+
+namespace psync {
+
+using namespace ndn;
+
+BOOST_AUTO_TEST_SUITE(TestProducerBase)
+
+BOOST_AUTO_TEST_CASE(Ctor)
+{
+ util::DummyClientFace face;
+ BOOST_REQUIRE_NO_THROW(ProducerBase(40, face, Name("/psync"), Name("/testUser")));
+}
+
+BOOST_AUTO_TEST_CASE(Basic)
+{
+ util::DummyClientFace face;
+ Name userNode("/testUser");
+ ProducerBase producerBase(40, face, Name("/psync"), userNode);
+ // Hash table size should be 40 + 40/2 = 60 (which is perfectly divisible by N_HASH = 3)
+ BOOST_CHECK_EQUAL(producerBase.m_iblt.getHashTable().size(), 60);
+ BOOST_CHECK_EQUAL(producerBase.getSeqNo(userNode).value(), 0);
+
+ producerBase.updateSeqNo(userNode, 1);
+ BOOST_CHECK(producerBase.getSeqNo(userNode.toUri()).value() == 1);
+
+ std::string prefixWithSeq = Name(userNode).appendNumber(1).toUri();
+ uint32_t hash = producerBase.m_prefix2hash[prefixWithSeq];
+ BOOST_CHECK_EQUAL(producerBase.m_hash2prefix[hash], userNode.toUri());
+
+ producerBase.removeUserNode(userNode);
+ BOOST_CHECK(producerBase.getSeqNo(userNode.toUri()) == ndn::nullopt);
+ BOOST_CHECK(producerBase.m_prefix2hash.find(prefixWithSeq) == producerBase.m_prefix2hash.end());
+ BOOST_CHECK(producerBase.m_hash2prefix.find(hash) == producerBase.m_hash2prefix.end());
+
+ Name nonExistentUserNode("/notAUser");
+ producerBase.updateSeqNo(nonExistentUserNode, 1);
+ BOOST_CHECK(producerBase.m_prefix2hash.find(Name(nonExistentUserNode).appendNumber(1).toUri()) ==
+ producerBase.m_prefix2hash.end());
+}
+
+BOOST_AUTO_TEST_CASE(ApplicationNack)
+{
+ util::DummyClientFace face;
+ ProducerBase producerBase(40, face, Name("/psync"), Name("/testUser"));
+
+ BOOST_CHECK_EQUAL(face.sentData.size(), 0);
+ producerBase.m_syncReplyFreshness = time::milliseconds(1000);
+ producerBase.sendApplicationNack(Name("test"));
+ face.processEvents(time::milliseconds(10));
+ BOOST_CHECK_EQUAL(face.sentData.size(), 1);
+
+ Data data = *face.sentData.begin();
+ BOOST_CHECK_EQUAL(data.getContentType(), ndn::tlv::ContentType_Nack);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace psync
diff --git a/tests/test-state.cpp b/tests/test-state.cpp
new file mode 100644
index 0000000..47c1471
--- /dev/null
+++ b/tests/test-state.cpp
@@ -0,0 +1,47 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2014-2018, The University of Memphis
+ *
+ * This file is part of PSync.
+ * See AUTHORS.md for complete list of PSync authors and contributors.
+ *
+ * PSync is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ *
+ * PSync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * PSync, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+#include "detail/state.hpp"
+
+#include <boost/test/unit_test.hpp>
+#include <ndn-cxx/name.hpp>
+#include <ndn-cxx/data.hpp>
+
+namespace psync {
+
+using namespace ndn;
+
+BOOST_AUTO_TEST_SUITE(TestState)
+
+BOOST_AUTO_TEST_CASE(EncodeDeode)
+{
+ State state;
+ state.addContent(ndn::Name("test1"));
+ state.addContent(ndn::Name("test2"));
+
+ ndn::Data data;
+ data.setContent(state.wireEncode());
+ State rcvdState(data.getContent());
+
+ BOOST_CHECK(state.getContent() == rcvdState.getContent());
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace psync
\ No newline at end of file
diff --git a/tests/unit-test-time-fixture.hpp b/tests/unit-test-time-fixture.hpp
new file mode 100644
index 0000000..36293a8
--- /dev/null
+++ b/tests/unit-test-time-fixture.hpp
@@ -0,0 +1,67 @@
+/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil -*- */
+/*
+ * Copyright (c) 2012-2018 University of California, Los Angeles
+ * The University of Memphis
+ *
+ * This file is part of PSync.
+ *
+ * PSync is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * PSync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * PSync, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef NDN_TESTS_UNIT_TESTS_UNIT_TEST_TIME_FIXTURE_HPP
+#define NDN_TESTS_UNIT_TESTS_UNIT_TEST_TIME_FIXTURE_HPP
+
+#include <ndn-cxx/util/time-unit-test-clock.hpp>
+
+#include <boost/asio.hpp>
+
+namespace ndn {
+namespace tests {
+
+class UnitTestTimeFixture
+{
+public:
+ UnitTestTimeFixture()
+ : steadyClock(make_shared<time::UnitTestSteadyClock>())
+ , systemClock(make_shared<time::UnitTestSystemClock>())
+ {
+ time::setCustomClocks(steadyClock, systemClock);
+ }
+
+ ~UnitTestTimeFixture()
+ {
+ time::setCustomClocks(nullptr, nullptr);
+ }
+
+ void
+ advanceClocks(const time::nanoseconds& tick, size_t nTicks = 1)
+ {
+ for (size_t i = 0; i < nTicks; ++i) {
+ steadyClock->advance(tick);
+ systemClock->advance(tick);
+
+ if (io.stopped())
+ io.reset();
+ io.poll();
+ }
+ }
+
+public:
+ shared_ptr<time::UnitTestSteadyClock> steadyClock;
+ shared_ptr<time::UnitTestSystemClock> systemClock;
+ boost::asio::io_service io;
+};
+
+} // namespace tests
+} // namespace ndn
+
+#endif // NDN_TESTS_UNIT_TESTS_UNIT_TEST_TIME_FIXTURE_HPP
diff --git a/tests/wscript b/tests/wscript
new file mode 100644
index 0000000..facae68
--- /dev/null
+++ b/tests/wscript
@@ -0,0 +1,22 @@
+top = '..'
+
+def build(bld):
+ if not bld.env['WITH_TESTS']:
+ return
+
+ bld(
+ features='cxx',
+ name='unit-tests-main',
+ target='unit-tests-main',
+ source='main.cpp',
+ defines=['BOOST_TEST_MODULE=PSync Unit Tests'],
+ use='PSync'
+ )
+
+ bld.program(
+ target='../unit-tests',
+ features='cxx cxxprogram',
+ source=bld.path.ant_glob(['**/*.cpp'], excl=['main.cpp']),
+ use='PSync unit-tests-main',
+ install_path=None,
+ )