Rename 'tests/unit-tests' directory to 'tests/unit'
Change-Id: I78ea29938259fac288781bed12fb2399ac7eba26
diff --git a/tests/unit/util/backports.t.cpp b/tests/unit/util/backports.t.cpp
new file mode 100644
index 0000000..7a124e0
--- /dev/null
+++ b/tests/unit/util/backports.t.cpp
@@ -0,0 +1,92 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library 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 Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "util/backports.hpp"
+
+#include "boost-test.hpp"
+#include <numeric>
+
+namespace ndn {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Util)
+BOOST_AUTO_TEST_SUITE(TestBackports)
+
+BOOST_AUTO_TEST_CASE(Clamp)
+{
+ int x = clamp(5, 1, 10);
+ BOOST_CHECK_EQUAL(x, 5);
+
+ x = clamp(-5, 1, 10);
+ BOOST_CHECK_EQUAL(x, 1);
+
+ x = clamp(15, 1, 10);
+ BOOST_CHECK_EQUAL(x, 10);
+
+ x = clamp(5, 10, 1, std::greater<int>());
+ BOOST_CHECK_EQUAL(x, 5);
+
+ x = clamp(-5, 10, 1, std::greater<int>());
+ BOOST_CHECK_EQUAL(x, 1);
+
+ x = clamp(15, 10, 1, std::greater<int>());
+ BOOST_CHECK_EQUAL(x, 10);
+}
+
+BOOST_AUTO_TEST_CASE(OstreamJoiner)
+{
+ boost::test_tools::output_test_stream os;
+
+ auto joiner1 = ostream_joiner<char>(os, ' ');
+ auto joiner2 = make_ostream_joiner(os, ' ');
+ static_assert(std::is_same<decltype(joiner1), decltype(joiner2)>::value, "");
+
+ std::vector<int> v(5);
+ std::iota(v.begin(), v.end(), 1);
+ std::copy(v.begin(), v.end(), joiner2);
+ BOOST_CHECK(os.is_equal("1 2 3 4 5"));
+
+ auto joiner3 = make_ostream_joiner(os, "...");
+ std::copy(v.begin(), v.end(), joiner3);
+ BOOST_CHECK(os.is_equal("1...2...3...4...5"));
+
+ joiner3 = "one";
+ BOOST_CHECK(os.is_equal("one"));
+ joiner3 = "two";
+ BOOST_CHECK(os.is_equal("...two"));
+ ++joiner3 = "three";
+ BOOST_CHECK(os.is_equal("...three"));
+ joiner3++ = "four";
+ BOOST_CHECK(os.is_equal("...four"));
+
+ std::copy(v.begin(), v.end(), make_ostream_joiner(os, ""));
+ BOOST_CHECK(os.is_equal("12345"));
+
+ std::string delimiter("_");
+ std::copy(v.begin(), v.end(), make_ostream_joiner(os, delimiter));
+ BOOST_CHECK(os.is_equal("1_2_3_4_5"));
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestBackports
+BOOST_AUTO_TEST_SUITE_END() // Util
+
+} // namespace tests
+} // namespace ndn
diff --git a/tests/unit/util/concepts.t.cpp b/tests/unit/util/concepts.t.cpp
new file mode 100644
index 0000000..3141ec5
--- /dev/null
+++ b/tests/unit/util/concepts.t.cpp
@@ -0,0 +1,61 @@
+/* -*- 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 ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library 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 Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "util/concepts.hpp"
+
+namespace ndn {
+namespace tests {
+
+class WireEncodableType
+{
+public:
+ const Block&
+ wireEncode();
+};
+BOOST_CONCEPT_ASSERT((WireEncodable<WireEncodableType>));
+
+class WireEncodableType2
+{
+public:
+ Block
+ wireEncode();
+};
+BOOST_CONCEPT_ASSERT((WireEncodable<WireEncodableType2>));
+
+class WireDecodableType
+{
+public:
+ explicit
+ WireDecodableType(const Block& wire);
+
+ void
+ wireDecode(const Block& wire);
+};
+BOOST_CONCEPT_ASSERT((WireDecodable<WireDecodableType>));
+
+} // namespace tests
+} // namespace ndn
diff --git a/tests/unit/util/config-file-home/.ndn/client.conf b/tests/unit/util/config-file-home/.ndn/client.conf
new file mode 100644
index 0000000..ba9f623
--- /dev/null
+++ b/tests/unit/util/config-file-home/.ndn/client.conf
@@ -0,0 +1,2 @@
+a=/path/to/nowhere
+b=some-othervalue.01
diff --git a/tests/unit/util/config-file-malformed-home/.ndn/client.conf b/tests/unit/util/config-file-malformed-home/.ndn/client.conf
new file mode 100644
index 0000000..7898192
--- /dev/null
+++ b/tests/unit/util/config-file-malformed-home/.ndn/client.conf
@@ -0,0 +1 @@
+a
diff --git a/tests/unit/util/config-file.t.cpp b/tests/unit/util/config-file.t.cpp
new file mode 100644
index 0000000..658018b
--- /dev/null
+++ b/tests/unit/util/config-file.t.cpp
@@ -0,0 +1,70 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library 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 Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "util/config-file.hpp"
+#include "../test-home-env-saver.hpp"
+
+#include "boost-test.hpp"
+
+#include <cstdlib>
+
+namespace ndn {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Util)
+BOOST_FIXTURE_TEST_SUITE(TestConfigFile, TestHomeEnvSaver)
+
+BOOST_AUTO_TEST_CASE(Parse)
+{
+ namespace fs = boost::filesystem;
+
+ setenv("TEST_HOME", "tests/unit/util/config-file-home", 1);
+
+ fs::path homePath(fs::absolute(std::getenv("TEST_HOME")));
+ homePath /= ".ndn/client.conf";
+
+ ConfigFile config;
+ BOOST_REQUIRE_EQUAL(config.getPath(), homePath);
+
+ const ConfigFile::Parsed& parsed = config.getParsedConfiguration();
+ BOOST_CHECK_EQUAL(parsed.get<std::string>("a"), "/path/to/nowhere");
+ BOOST_CHECK_EQUAL(parsed.get<std::string>("b"), "some-othervalue.01");
+}
+
+BOOST_AUTO_TEST_CASE(ParseEmptyPath)
+{
+ setenv("TEST_HOME", "tests/unit/util/does/not/exist", 1);
+
+ BOOST_CHECK_NO_THROW(ConfigFile config);
+}
+
+BOOST_AUTO_TEST_CASE(ParseMalformed)
+{
+ setenv("TEST_HOME", "tests/unit/util/config-file-malformed-home", 1);
+
+ BOOST_CHECK_THROW(ConfigFile config, ConfigFile::Error);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestConfigFile
+BOOST_AUTO_TEST_SUITE_END() // Util
+
+} // namespace tests
+} // namespace ndn
diff --git a/tests/unit/util/dummy-client-face.t.cpp b/tests/unit/util/dummy-client-face.t.cpp
new file mode 100644
index 0000000..aed8cd6
--- /dev/null
+++ b/tests/unit/util/dummy-client-face.t.cpp
@@ -0,0 +1,132 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library 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 Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "util/dummy-client-face.hpp"
+
+#include "boost-test.hpp"
+#include "../identity-management-time-fixture.hpp"
+#include "make-interest-data.hpp"
+
+namespace ndn {
+namespace util {
+namespace tests {
+
+using namespace ndn::tests;
+
+BOOST_AUTO_TEST_SUITE(Util)
+BOOST_FIXTURE_TEST_SUITE(TestDummyClientFace, ndn::tests::IdentityManagementTimeFixture)
+
+BOOST_AUTO_TEST_CASE(ProcessEventsOverride)
+{
+ bool isOverrideInvoked = false;
+ auto override = [&] (time::milliseconds timeout) {
+ isOverrideInvoked = true;
+ BOOST_CHECK_EQUAL(timeout, 200_ms);
+ };
+
+ DummyClientFace face(io, {false, false, override});
+ face.processEvents(200_ms);
+ BOOST_CHECK(isOverrideInvoked);
+}
+
+BOOST_AUTO_TEST_CASE(BroadcastLink)
+{
+ DummyClientFace face1(io, m_keyChain, DummyClientFace::Options{true, true});
+ DummyClientFace face2(io, m_keyChain, DummyClientFace::Options{true, true});
+ face1.linkTo(face2);
+
+ int nFace1Interest = 0;
+ int nFace2Interest = 0;
+ face1.setInterestFilter("/face1",
+ [&] (const InterestFilter&, const Interest& interest) {
+ BOOST_CHECK_EQUAL(interest.getName().toUri(), "/face1/data");
+ nFace1Interest++;
+ face1.put(ndn::tests::makeNack(interest, lp::NackReason::NO_ROUTE));
+ }, nullptr, nullptr);
+ face2.setInterestFilter("/face2",
+ [&] (const InterestFilter&, const Interest& interest) {
+ BOOST_CHECK_EQUAL(interest.getName().toUri(), "/face2/data");
+ nFace2Interest++;
+ face2.put(*ndn::tests::makeData("/face2/data"));
+ return;
+ }, nullptr, nullptr);
+
+ advanceClocks(25_ms, 4);
+
+ int nFace1Data = 0;
+ int nFace2Nack = 0;
+ face1.expressInterest(*makeInterest("/face2/data"),
+ [&] (const Interest& i, const Data& d) {
+ BOOST_CHECK_EQUAL(d.getName().toUri(), "/face2/data");
+ nFace1Data++;
+ }, nullptr, nullptr);
+ face2.expressInterest(*makeInterest("/face1/data"),
+ [&] (const Interest& i, const Data& d) {
+ BOOST_CHECK(false);
+ },
+ [&] (const Interest& i, const lp::Nack& n) {
+ BOOST_CHECK_EQUAL(n.getInterest().getName().toUri(), "/face1/data");
+ nFace2Nack++;
+ }, nullptr);
+
+ advanceClocks(10_ms, 100);
+
+ BOOST_CHECK_EQUAL(nFace1Data, 1);
+ BOOST_CHECK_EQUAL(nFace2Nack, 1);
+ BOOST_CHECK_EQUAL(nFace1Interest, 1);
+ BOOST_CHECK_EQUAL(nFace2Interest, 1);
+}
+
+BOOST_AUTO_TEST_CASE(BroadcastLinkDestroy)
+{
+ DummyClientFace face1(io, m_keyChain, DummyClientFace::Options{true, true});
+ DummyClientFace face2(io, m_keyChain, DummyClientFace::Options{true, true});
+
+ face1.linkTo(face2);
+ face2.unlink();
+ BOOST_CHECK(face1.m_bcastLink == nullptr);
+
+ DummyClientFace face3(io, m_keyChain, DummyClientFace::Options{true, true});
+ face1.linkTo(face2);
+ face3.linkTo(face1);
+ face2.unlink();
+ BOOST_CHECK(face1.m_bcastLink != nullptr);
+}
+
+BOOST_AUTO_TEST_CASE(AlreadyLinkException)
+{
+ DummyClientFace face1(io, m_keyChain, DummyClientFace::Options{true, true});
+ DummyClientFace face2(io, m_keyChain, DummyClientFace::Options{true, true});
+ DummyClientFace face3(io, m_keyChain, DummyClientFace::Options{true, true});
+ DummyClientFace face4(io, m_keyChain, DummyClientFace::Options{true, true});
+
+ face1.linkTo(face2);
+ face3.linkTo(face4);
+
+ BOOST_CHECK_THROW(face2.linkTo(face3), DummyClientFace::AlreadyLinkedError);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestDummyClientFace
+BOOST_AUTO_TEST_SUITE_END() // Util
+
+} // namespace tests
+} // namespace util
+} // namespace ndn
diff --git a/tests/unit/util/indented-stream.t.cpp b/tests/unit/util/indented-stream.t.cpp
new file mode 100644
index 0000000..e450c4a
--- /dev/null
+++ b/tests/unit/util/indented-stream.t.cpp
@@ -0,0 +1,80 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library 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 Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "util/indented-stream.hpp"
+
+#include "boost-test.hpp"
+
+namespace ndn {
+namespace util {
+namespace tests {
+
+using boost::test_tools::output_test_stream;
+
+BOOST_AUTO_TEST_SUITE(Util)
+BOOST_AUTO_TEST_SUITE(TestIndentedStream)
+
+BOOST_AUTO_TEST_CASE(Basic)
+{
+ output_test_stream os;
+
+ os << "Hello" << std::endl;
+ {
+ IndentedStream os1(os, " [prefix] ");
+ os1 << "," << "\n";
+ {
+ IndentedStream os2(os1, " [another prefix] ");
+ os2 << "World!" << "\n";
+ }
+ }
+
+ BOOST_CHECK(os.is_equal("Hello\n"
+ " [prefix] ,\n"
+ " [prefix] [another prefix] World!\n"
+ ));
+}
+
+BOOST_AUTO_TEST_CASE(BasicWithFlushes) // Bug #2723
+{
+ output_test_stream os;
+
+ os << "Hello" << std::endl;
+ {
+ IndentedStream os1(os, " [prefix] ");
+ os1 << "," << std::endl;
+ {
+ IndentedStream os2(os1, " [another prefix] ");
+ os2 << "World!" << std::endl;
+ }
+ }
+
+ BOOST_CHECK(os.is_equal("Hello\n"
+ " [prefix] ,\n"
+ " [prefix] [another prefix] World!\n"
+ ));
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestIndentedStream
+BOOST_AUTO_TEST_SUITE_END() // Util
+
+} // namespace tests
+} // namespace util
+} // namespace ndn
diff --git a/tests/unit/util/io.t.cpp b/tests/unit/util/io.t.cpp
new file mode 100644
index 0000000..1b46bca
--- /dev/null
+++ b/tests/unit/util/io.t.cpp
@@ -0,0 +1,288 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library 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 Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "util/io.hpp"
+
+#include "boost-test.hpp"
+#include "identity-management-fixture.hpp"
+
+#include <boost/filesystem.hpp>
+
+namespace ndn {
+namespace tests {
+
+class IoFixture
+{
+protected:
+ IoFixture()
+ : filepath(boost::filesystem::path(UNIT_TEST_CONFIG_PATH) /= "TestIo")
+ , filename(filepath.string())
+ {
+ boost::filesystem::create_directories(filepath.parent_path());
+ }
+
+ ~IoFixture()
+ {
+ boost::system::error_code ec;
+ boost::filesystem::remove(filepath, ec); // ignore error
+ }
+
+ /** \brief create a directory at filename, so that it's neither readable nor writable as a file
+ */
+ void
+ mkdir() const
+ {
+ boost::filesystem::create_directory(filepath);
+ }
+
+ template<typename Container, typename CharT = typename Container::value_type>
+ Container
+ readFile() const
+ {
+ Container container;
+ std::ifstream fs(filename, std::ios_base::binary);
+ char ch;
+ while (fs.get(ch)) {
+ container.push_back(static_cast<CharT>(ch));
+ }
+ return container;
+ }
+
+ template<typename Container, typename CharT = typename Container::value_type>
+ void
+ writeFile(const Container& content) const
+ {
+ std::ofstream fs(filename, std::ios_base::binary);
+ for (CharT ch : content) {
+ fs.put(static_cast<char>(ch));
+ }
+ fs.close();
+ BOOST_REQUIRE_MESSAGE(fs, "error writing file");
+ }
+
+protected:
+ const boost::filesystem::path filepath;
+ const std::string filename;
+};
+
+BOOST_AUTO_TEST_SUITE(Util)
+BOOST_FIXTURE_TEST_SUITE(TestIo, IoFixture)
+
+class EncodableType
+{
+public:
+ Block
+ wireEncode() const
+ {
+ if (shouldThrow) {
+ BOOST_THROW_EXCEPTION(tlv::Error("encode error"));
+ }
+
+ // block will be 0xAA, 0x01, 0xDD
+ return makeNonNegativeIntegerBlock(0xAA, 0xDD);
+ }
+
+public:
+ bool shouldThrow = false;
+};
+
+template<bool SHOULD_THROW = false>
+class DecodableTypeTpl
+{
+public:
+ DecodableTypeTpl() = default;
+
+ explicit
+ DecodableTypeTpl(const Block& block)
+ {
+ this->wireDecode(block);
+ }
+
+ void
+ wireDecode(const Block& block)
+ {
+ if (m_shouldThrow) {
+ BOOST_THROW_EXCEPTION(tlv::Error("decode error"));
+ }
+
+ // block must be 0xBB, 0x01, 0xEE
+ BOOST_CHECK_EQUAL(block.type(), 0xBB);
+ BOOST_REQUIRE_EQUAL(block.value_size(), 1);
+ BOOST_CHECK_EQUAL(block.value()[0], 0xEE);
+ }
+
+private:
+ bool m_shouldThrow = SHOULD_THROW;
+};
+
+typedef DecodableTypeTpl<false> DecodableType;
+typedef DecodableTypeTpl<true> DecodableTypeThrow;
+
+BOOST_AUTO_TEST_CASE(LoadNoEncoding)
+{
+ this->writeFile<std::vector<uint8_t>>({0xBB, 0x01, 0xEE});
+ shared_ptr<DecodableType> decoded = io::load<DecodableType>(filename, io::NO_ENCODING);
+ BOOST_CHECK(decoded != nullptr);
+}
+
+BOOST_AUTO_TEST_CASE(LoadBase64)
+{
+ this->writeFile<std::string>("uwHu\n"); // printf '\xBB\x01\xEE' | base64
+ shared_ptr<DecodableType> decoded = io::load<DecodableType>(filename, io::BASE64);
+ BOOST_CHECK(decoded != nullptr);
+}
+
+BOOST_AUTO_TEST_CASE(LoadBase64Newline64)
+{
+ this->writeFile<std::string>(
+ "CEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n"
+ "AAAAAAAAAAAA\n");
+ // printf '\x08\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00
+ // \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00
+ // \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00
+ // \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' | base64
+ shared_ptr<name::Component> decoded = io::load<name::Component>(filename, io::BASE64);
+ BOOST_CHECK(decoded != nullptr);
+}
+
+BOOST_AUTO_TEST_CASE(LoadBase64Newline32)
+{
+ this->writeFile<std::string>(
+ "CEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n"
+ "AAAAAAAAAAAA\n");
+ shared_ptr<name::Component> decoded = io::load<name::Component>(filename, io::BASE64);
+ BOOST_CHECK(decoded != nullptr);
+}
+
+BOOST_AUTO_TEST_CASE(LoadBase64NewlineEnd)
+{
+ this->writeFile<std::string>(
+ "CEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n");
+ shared_ptr<name::Component> decoded = io::load<name::Component>(filename, io::BASE64);
+ BOOST_CHECK(decoded != nullptr);
+}
+
+BOOST_AUTO_TEST_CASE(LoadBase64NoNewline)
+{
+ this->writeFile<std::string>(
+ "CEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
+ shared_ptr<name::Component> decoded = io::load<name::Component>(filename, io::BASE64);
+ BOOST_CHECK(decoded != nullptr);
+}
+
+BOOST_AUTO_TEST_CASE(LoadHex)
+{
+ this->writeFile<std::string>("BB01EE");
+ shared_ptr<DecodableType> decoded = io::load<DecodableType>(filename, io::HEX);
+ BOOST_CHECK(decoded != nullptr);
+}
+
+BOOST_AUTO_TEST_CASE(LoadException)
+{
+ this->writeFile<std::vector<uint8_t>>({0xBB, 0x01, 0xEE});
+ shared_ptr<DecodableTypeThrow> decoded;
+ BOOST_CHECK_NO_THROW(decoded = io::load<DecodableTypeThrow>(filename, io::NO_ENCODING));
+ BOOST_CHECK(decoded == nullptr);
+}
+
+BOOST_AUTO_TEST_CASE(LoadNotHex)
+{
+ this->writeFile<std::string>("not-hex");
+ shared_ptr<DecodableType> decoded;
+ BOOST_CHECK_NO_THROW(decoded = io::load<DecodableType>(filename, io::HEX));
+ BOOST_CHECK(decoded == nullptr);
+}
+
+BOOST_AUTO_TEST_CASE(LoadFileNotReadable)
+{
+ shared_ptr<DecodableType> decoded;
+ BOOST_CHECK_NO_THROW(decoded = io::load<DecodableType>(filename, io::NO_ENCODING));
+ BOOST_CHECK(decoded == nullptr);
+}
+
+BOOST_AUTO_TEST_CASE(SaveNoEncoding)
+{
+ EncodableType encoded;
+ BOOST_CHECK_NO_THROW(io::save(encoded, filename, io::NO_ENCODING));
+ auto content = this->readFile<std::vector<uint8_t>>();
+ uint8_t expected[] = {0xAA, 0x01, 0xDD};
+ BOOST_CHECK_EQUAL_COLLECTIONS(content.begin(), content.end(),
+ expected, expected + sizeof(expected));
+}
+
+BOOST_AUTO_TEST_CASE(SaveBase64)
+{
+ EncodableType encoded;
+ BOOST_CHECK_NO_THROW(io::save(encoded, filename, io::BASE64));
+ auto content = this->readFile<std::string>();
+ BOOST_CHECK_EQUAL(content, "qgHd\n"); // printf '\xAA\x01\xDD' | base64
+}
+
+BOOST_AUTO_TEST_CASE(SaveHex)
+{
+ EncodableType encoded;
+ BOOST_CHECK_NO_THROW(io::save(encoded, filename, io::HEX));
+ auto content = this->readFile<std::string>();
+ BOOST_CHECK_EQUAL(content, "AA01DD");
+}
+
+BOOST_AUTO_TEST_CASE(SaveException)
+{
+ EncodableType encoded;
+ encoded.shouldThrow = true;
+ BOOST_CHECK_THROW(io::save(encoded, filename, io::NO_ENCODING), io::Error);
+}
+
+BOOST_AUTO_TEST_CASE(SaveFileNotWritable)
+{
+ this->mkdir();
+ EncodableType encoded;
+ encoded.shouldThrow = true;
+ BOOST_CHECK_THROW(io::save(encoded, filename, io::NO_ENCODING), io::Error);
+}
+
+class IdCertFixture : public IoFixture
+ , public IdentityManagementFixture
+{
+};
+
+BOOST_FIXTURE_TEST_CASE(IdCert, IdCertFixture)
+{
+ auto identity = addIdentity("/TestIo/IdCert", RsaKeyParams());
+ const auto& cert = identity.getDefaultKey().getDefaultCertificate();
+ io::save(cert, filename);
+
+ auto readCert = io::load<security::v2::Certificate>(filename);
+
+ BOOST_REQUIRE(readCert != nullptr);
+ BOOST_CHECK_EQUAL(cert.getName(), readCert->getName());
+
+ this->writeFile<std::string>("");
+ readCert = io::load<security::v2::Certificate>(filename);
+ BOOST_REQUIRE(readCert == nullptr);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestIo
+BOOST_AUTO_TEST_SUITE_END() // Util
+
+} // namespace tests
+} // namespace ndn
diff --git a/tests/unit/util/log-filter-module.cpp b/tests/unit/util/log-filter-module.cpp
new file mode 100644
index 0000000..cf3b628
--- /dev/null
+++ b/tests/unit/util/log-filter-module.cpp
@@ -0,0 +1,44 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library 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 Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "util/logger.hpp"
+
+NDN_LOG_INIT(fm.FilterModule);
+
+namespace ndn {
+namespace util {
+namespace tests {
+
+void
+logFromFilterModule()
+{
+ NDN_LOG_TRACE("traceFM");
+ NDN_LOG_DEBUG("debugFM");
+ NDN_LOG_INFO("infoFM");
+ NDN_LOG_WARN("warnFM");
+ NDN_LOG_ERROR("errorFM");
+ NDN_LOG_FATAL("fatalFM");
+}
+
+} // namespace tests
+} // namespace util
+} // namespace ndn
+
diff --git a/tests/unit/util/log-module1.cpp b/tests/unit/util/log-module1.cpp
new file mode 100644
index 0000000..2099a75
--- /dev/null
+++ b/tests/unit/util/log-module1.cpp
@@ -0,0 +1,43 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library 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 Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "util/logger.hpp"
+
+NDN_LOG_INIT(Module1);
+
+namespace ndn {
+namespace util {
+namespace tests {
+
+void
+logFromModule1()
+{
+ NDN_LOG_TRACE("trace" << 1);
+ NDN_LOG_DEBUG("debug" << 1);
+ NDN_LOG_INFO("info" << 1);
+ NDN_LOG_WARN("warn" << 1);
+ NDN_LOG_ERROR("error" << 1);
+ NDN_LOG_FATAL("fatal" << 1);
+}
+
+} // namespace tests
+} // namespace util
+} // namespace ndn
diff --git a/tests/unit/util/log-module2.cpp b/tests/unit/util/log-module2.cpp
new file mode 100644
index 0000000..273165d
--- /dev/null
+++ b/tests/unit/util/log-module2.cpp
@@ -0,0 +1,43 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library 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 Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "util/logger.hpp"
+
+NDN_LOG_INIT(Module2);
+
+namespace ndn {
+namespace util {
+namespace tests {
+
+void
+logFromModule2()
+{
+ NDN_LOG_TRACE("trace" << 2);
+ NDN_LOG_DEBUG("debug" << 2);
+ NDN_LOG_INFO("info" << 2);
+ NDN_LOG_WARN("warn" << 2);
+ NDN_LOG_ERROR("error" << 2);
+ NDN_LOG_FATAL("fatal" << 2);
+}
+
+} // namespace tests
+} // namespace util
+} // namespace ndn
diff --git a/tests/unit/util/logger.t.cpp b/tests/unit/util/logger.t.cpp
new file mode 100644
index 0000000..cc48425
--- /dev/null
+++ b/tests/unit/util/logger.t.cpp
@@ -0,0 +1,82 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library 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 Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "util/logger.hpp"
+#include "util/logging.hpp"
+
+#include "boost-test.hpp"
+
+namespace ndn {
+namespace util {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Util)
+BOOST_AUTO_TEST_SUITE(TestLogger)
+
+BOOST_AUTO_TEST_CASE(LegalAlphanumeric)
+{
+ Logger logger("ndnTest123");
+ auto mNames = Logging::getLoggerNames();
+ BOOST_CHECK_EQUAL(mNames.count("ndnTest123"), 1);
+ BOOST_CHECK(logger.isLevelEnabled(LogLevel::NONE));
+ Logging::get().removeLogger(logger);
+}
+
+BOOST_AUTO_TEST_CASE(AllLegalSymbols)
+{
+ Logger logger("ndn.~#%.test_<check>1-2-3");
+ auto mNames = Logging::getLoggerNames();
+ BOOST_CHECK_EQUAL(mNames.count("ndn.~#%.test_<check>1-2-3"), 1);
+ BOOST_CHECK(logger.isLevelEnabled(LogLevel::NONE));
+ Logging::get().removeLogger(logger);
+}
+
+BOOST_AUTO_TEST_CASE(EmptyLogger)
+{
+ BOOST_CHECK_THROW(Logger logger(""), std::invalid_argument);
+}
+
+BOOST_AUTO_TEST_CASE(InvalidSymbol)
+{
+ BOOST_CHECK_THROW(Logger logger("ndn.test.*"), std::invalid_argument);
+}
+
+BOOST_AUTO_TEST_CASE(StartsWithPeriod)
+{
+ BOOST_CHECK_THROW(Logger logger(".ndn.test"), std::invalid_argument);
+}
+
+BOOST_AUTO_TEST_CASE(EndsWithPeriod)
+{
+ BOOST_CHECK_THROW(Logger logger("ndn.test."), std::invalid_argument);
+}
+
+BOOST_AUTO_TEST_CASE(ConsecutivePeriods)
+{
+ BOOST_CHECK_THROW(Logger logger("ndn..test"), std::invalid_argument);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestLogger
+BOOST_AUTO_TEST_SUITE_END() // Util
+
+} // namespace tests
+} // namespace util
+} // namespace ndn
diff --git a/tests/unit/util/logging.t.cpp b/tests/unit/util/logging.t.cpp
new file mode 100644
index 0000000..b431a05
--- /dev/null
+++ b/tests/unit/util/logging.t.cpp
@@ -0,0 +1,652 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library 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 Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "util/logging.hpp"
+#include "util/logger.hpp"
+
+#include "../unit-test-time-fixture.hpp"
+#include "boost-test.hpp"
+
+namespace ndn {
+namespace util {
+namespace tests {
+
+NDN_LOG_INIT(ndn.util.tests.Logging);
+
+void
+logFromModule1();
+
+void
+logFromModule2();
+
+void
+logFromFilterModule();
+
+static void
+logFromNewLogger(const char* moduleName)
+{
+ Logger logger(moduleName);
+ auto ndn_cxx_getLogger = [&logger] () -> Logger& { return logger; };
+
+ NDN_LOG_TRACE("trace" << moduleName);
+ NDN_LOG_DEBUG("debug" << moduleName);
+ NDN_LOG_INFO("info" << moduleName);
+ NDN_LOG_WARN("warn" << moduleName);
+ NDN_LOG_ERROR("error" << moduleName);
+ NDN_LOG_FATAL("fatal" << moduleName);
+
+ BOOST_CHECK(Logging::get().removeLogger(logger));
+}
+
+namespace ns1 {
+
+NDN_LOG_INIT(ndn.util.tests.ns1);
+
+static void
+logFromNamespace1()
+{
+ NDN_LOG_INFO("hello world from ns1");
+}
+
+} // namespace ns1
+
+namespace ns2 {
+
+NDN_LOG_INIT(ndn.util.tests.ns2);
+
+static void
+logFromNamespace2()
+{
+ NDN_LOG_INFO("hi there from ns2");
+}
+
+} // namespace ns2
+
+class ClassWithLogger
+{
+public:
+ void
+ logFromConstMemberFunction() const
+ {
+ NDN_LOG_INFO("const member function");
+ }
+
+ static void
+ logFromStaticMemberFunction()
+ {
+ NDN_LOG_INFO("static member function");
+ }
+
+private:
+ NDN_LOG_MEMBER_DECL();
+};
+
+NDN_LOG_MEMBER_INIT(ClassWithLogger, ndn.util.tests.ClassWithLogger);
+
+template<class T, class U>
+class ClassTemplateWithLogger
+{
+public:
+ void
+ logFromMemberFunction()
+ {
+ NDN_LOG_INFO("class template non-static member function");
+ }
+
+ static void
+ logFromStaticMemberFunction()
+ {
+ NDN_LOG_INFO("class template static member function");
+ }
+
+private:
+ NDN_LOG_MEMBER_DECL();
+};
+
+// Technically this declaration is not necessary in this case,
+// but we want to test that the macro expands to well-formed code
+NDN_LOG_MEMBER_DECL_SPECIALIZED((ClassTemplateWithLogger<int, double>));
+
+NDN_LOG_MEMBER_INIT_SPECIALIZED((ClassTemplateWithLogger<int, double>), ndn.util.tests.Specialized1);
+NDN_LOG_MEMBER_INIT_SPECIALIZED((ClassTemplateWithLogger<int, std::string>), ndn.util.tests.Specialized2);
+
+const time::microseconds LOG_SYSTIME(1468108800311239LL);
+const std::string LOG_SYSTIME_STR("1468108800.311239");
+
+class LoggingFixture : public ndn::tests::UnitTestTimeFixture
+{
+protected:
+ LoggingFixture()
+ : m_oldEnabledLevel(Logging::get().getLevels())
+ , m_oldDestination(Logging::get().getDestination())
+ {
+ this->systemClock->setNow(LOG_SYSTIME);
+ Logging::get().resetLevels();
+ Logging::setDestination(os);
+ }
+
+ ~LoggingFixture()
+ {
+ Logging::get().setLevelImpl(m_oldEnabledLevel);
+ Logging::setDestination(m_oldDestination);
+ }
+
+protected:
+ boost::test_tools::output_test_stream os;
+
+private:
+ std::unordered_map<std::string, LogLevel> m_oldEnabledLevel;
+ shared_ptr<std::ostream> m_oldDestination;
+};
+
+BOOST_AUTO_TEST_SUITE(Util)
+BOOST_FIXTURE_TEST_SUITE(TestLogging, LoggingFixture)
+
+BOOST_AUTO_TEST_CASE(GetLoggerNames)
+{
+ // check that all names are registered from the start even if
+ // logger instances are lazily created on first use
+ auto n = Logging::getLoggerNames().size();
+ NDN_LOG_TRACE("GetLoggerNames");
+ auto names = Logging::getLoggerNames();
+ BOOST_CHECK_EQUAL(names.size(), n);
+ BOOST_CHECK_EQUAL(names.count("ndn.util.tests.Logging"), 1);
+}
+
+BOOST_AUTO_TEST_SUITE(Severity)
+
+BOOST_AUTO_TEST_CASE(None)
+{
+ Logging::setLevel("Module1", LogLevel::NONE);
+ logFromModule1();
+
+ Logging::flush();
+ BOOST_CHECK(os.is_equal(
+ LOG_SYSTIME_STR + " FATAL: [Module1] fatal1\n"
+ ));
+}
+
+BOOST_AUTO_TEST_CASE(Error)
+{
+ Logging::setLevel("Module1", LogLevel::ERROR);
+ logFromModule1();
+
+ Logging::flush();
+ BOOST_CHECK(os.is_equal(
+ LOG_SYSTIME_STR + " ERROR: [Module1] error1\n" +
+ LOG_SYSTIME_STR + " FATAL: [Module1] fatal1\n"
+ ));
+}
+
+BOOST_AUTO_TEST_CASE(Warn)
+{
+ Logging::setLevel("Module1", LogLevel::WARN);
+ logFromModule1();
+
+ Logging::flush();
+ BOOST_CHECK(os.is_equal(
+ LOG_SYSTIME_STR + " WARNING: [Module1] warn1\n" +
+ LOG_SYSTIME_STR + " ERROR: [Module1] error1\n" +
+ LOG_SYSTIME_STR + " FATAL: [Module1] fatal1\n"
+ ));
+}
+
+BOOST_AUTO_TEST_CASE(Info)
+{
+ Logging::setLevel("Module1", LogLevel::INFO);
+ logFromModule1();
+
+ Logging::flush();
+ BOOST_CHECK(os.is_equal(
+ LOG_SYSTIME_STR + " INFO: [Module1] info1\n" +
+ LOG_SYSTIME_STR + " WARNING: [Module1] warn1\n" +
+ LOG_SYSTIME_STR + " ERROR: [Module1] error1\n" +
+ LOG_SYSTIME_STR + " FATAL: [Module1] fatal1\n"
+ ));
+}
+
+BOOST_AUTO_TEST_CASE(Debug)
+{
+ Logging::setLevel("Module1", LogLevel::DEBUG);
+ logFromModule1();
+
+ Logging::flush();
+ BOOST_CHECK(os.is_equal(
+ LOG_SYSTIME_STR + " DEBUG: [Module1] debug1\n" +
+ LOG_SYSTIME_STR + " INFO: [Module1] info1\n" +
+ LOG_SYSTIME_STR + " WARNING: [Module1] warn1\n" +
+ LOG_SYSTIME_STR + " ERROR: [Module1] error1\n" +
+ LOG_SYSTIME_STR + " FATAL: [Module1] fatal1\n"
+ ));
+}
+
+BOOST_AUTO_TEST_CASE(Trace)
+{
+ Logging::setLevel("Module1", LogLevel::TRACE);
+ logFromModule1();
+
+ Logging::flush();
+ BOOST_CHECK(os.is_equal(
+ LOG_SYSTIME_STR + " TRACE: [Module1] trace1\n" +
+ LOG_SYSTIME_STR + " DEBUG: [Module1] debug1\n" +
+ LOG_SYSTIME_STR + " INFO: [Module1] info1\n" +
+ LOG_SYSTIME_STR + " WARNING: [Module1] warn1\n" +
+ LOG_SYSTIME_STR + " ERROR: [Module1] error1\n" +
+ LOG_SYSTIME_STR + " FATAL: [Module1] fatal1\n"
+ ));
+}
+
+BOOST_AUTO_TEST_CASE(All)
+{
+ Logging::setLevel("Module1", LogLevel::ALL);
+ logFromModule1();
+
+ Logging::flush();
+ BOOST_CHECK(os.is_equal(
+ LOG_SYSTIME_STR + " TRACE: [Module1] trace1\n" +
+ LOG_SYSTIME_STR + " DEBUG: [Module1] debug1\n" +
+ LOG_SYSTIME_STR + " INFO: [Module1] info1\n" +
+ LOG_SYSTIME_STR + " WARNING: [Module1] warn1\n" +
+ LOG_SYSTIME_STR + " ERROR: [Module1] error1\n" +
+ LOG_SYSTIME_STR + " FATAL: [Module1] fatal1\n"
+ ));
+}
+
+BOOST_AUTO_TEST_SUITE_END() // Severity
+
+BOOST_AUTO_TEST_CASE(NamespaceLogger)
+{
+ Logging::setLevel("ndn.util.tests.ns1", LogLevel::INFO);
+ Logging::setLevel("ndn.util.tests.ns2", LogLevel::DEBUG);
+
+ const auto& levels = Logging::get().getLevels();
+ BOOST_CHECK_EQUAL(levels.size(), 2);
+ BOOST_CHECK_EQUAL(levels.at("ndn.util.tests.ns1"), LogLevel::INFO);
+ BOOST_CHECK_EQUAL(levels.at("ndn.util.tests.ns2"), LogLevel::DEBUG);
+
+ const auto& names = Logging::getLoggerNames();
+ BOOST_CHECK_EQUAL(names.count("ndn.util.tests.ns1"), 1);
+ BOOST_CHECK_EQUAL(names.count("ndn.util.tests.ns2"), 1);
+
+ ns1::logFromNamespace1();
+ ns2::logFromNamespace2();
+
+ Logging::flush();
+ BOOST_CHECK(os.is_equal(
+ LOG_SYSTIME_STR + " INFO: [ndn.util.tests.ns1] hello world from ns1\n" +
+ LOG_SYSTIME_STR + " INFO: [ndn.util.tests.ns2] hi there from ns2\n"
+ ));
+}
+
+BOOST_AUTO_TEST_CASE(MemberLogger)
+{
+ Logging::setLevel("ndn.util.tests.ClassWithLogger", LogLevel::INFO);
+ Logging::setLevel("ndn.util.tests.Specialized1", LogLevel::INFO);
+ // ndn.util.tests.Specialized2 is not enabled
+
+ const auto& names = Logging::getLoggerNames();
+ BOOST_CHECK_EQUAL(names.count("ndn.util.tests.ClassWithLogger"), 1);
+ BOOST_CHECK_EQUAL(names.count("ndn.util.tests.Specialized1"), 1);
+ BOOST_CHECK_EQUAL(names.count("ndn.util.tests.Specialized2"), 1);
+
+ ClassWithLogger::logFromStaticMemberFunction();
+ ClassWithLogger{}.logFromConstMemberFunction();
+
+ Logging::flush();
+ BOOST_CHECK(os.is_equal(
+ LOG_SYSTIME_STR + " INFO: [ndn.util.tests.ClassWithLogger] static member function\n" +
+ LOG_SYSTIME_STR + " INFO: [ndn.util.tests.ClassWithLogger] const member function\n"
+ ));
+
+ ClassTemplateWithLogger<int, double>::logFromStaticMemberFunction();
+ ClassTemplateWithLogger<int, double>{}.logFromMemberFunction();
+ ClassTemplateWithLogger<int, std::string>::logFromStaticMemberFunction();
+ ClassTemplateWithLogger<int, std::string>{}.logFromMemberFunction();
+
+ Logging::flush();
+ BOOST_CHECK(os.is_equal(
+ LOG_SYSTIME_STR + " INFO: [ndn.util.tests.Specialized1] class template static member function\n" +
+ LOG_SYSTIME_STR + " INFO: [ndn.util.tests.Specialized1] class template non-static member function\n"
+ ));
+}
+
+BOOST_AUTO_TEST_CASE(SameNameLoggers)
+{
+ Logging::setLevel("Module1", LogLevel::WARN);
+ logFromModule1();
+ logFromNewLogger("Module1");
+
+ BOOST_CHECK_EQUAL(Logging::getLoggerNames().count("Module1"), 1);
+
+ Logging::flush();
+ BOOST_CHECK(os.is_equal(
+ LOG_SYSTIME_STR + " WARNING: [Module1] warn1\n" +
+ LOG_SYSTIME_STR + " ERROR: [Module1] error1\n" +
+ LOG_SYSTIME_STR + " FATAL: [Module1] fatal1\n" +
+ LOG_SYSTIME_STR + " WARNING: [Module1] warnModule1\n" +
+ LOG_SYSTIME_STR + " ERROR: [Module1] errorModule1\n" +
+ LOG_SYSTIME_STR + " FATAL: [Module1] fatalModule1\n"
+ ));
+}
+
+BOOST_AUTO_TEST_CASE(LateRegistration)
+{
+ Logging::setLevel("Module3", LogLevel::DEBUG);
+ logFromNewLogger("Module3");
+
+ Logging::flush();
+ BOOST_CHECK(os.is_equal(
+ LOG_SYSTIME_STR + " DEBUG: [Module3] debugModule3\n" +
+ LOG_SYSTIME_STR + " INFO: [Module3] infoModule3\n" +
+ LOG_SYSTIME_STR + " WARNING: [Module3] warnModule3\n" +
+ LOG_SYSTIME_STR + " ERROR: [Module3] errorModule3\n" +
+ LOG_SYSTIME_STR + " FATAL: [Module3] fatalModule3\n"
+ ));
+}
+
+BOOST_AUTO_TEST_SUITE(DefaultSeverity)
+
+BOOST_AUTO_TEST_CASE(Unset)
+{
+ logFromModule1();
+ logFromModule2();
+
+ Logging::flush();
+ BOOST_CHECK(os.is_equal(
+ LOG_SYSTIME_STR + " FATAL: [Module1] fatal1\n" +
+ LOG_SYSTIME_STR + " FATAL: [Module2] fatal2\n"
+ ));
+}
+
+BOOST_AUTO_TEST_CASE(NoOverride)
+{
+ Logging::setLevel("*", LogLevel::WARN);
+ logFromModule1();
+ logFromModule2();
+
+ Logging::flush();
+ BOOST_CHECK(os.is_equal(
+ LOG_SYSTIME_STR + " WARNING: [Module1] warn1\n" +
+ LOG_SYSTIME_STR + " ERROR: [Module1] error1\n" +
+ LOG_SYSTIME_STR + " FATAL: [Module1] fatal1\n" +
+ LOG_SYSTIME_STR + " WARNING: [Module2] warn2\n" +
+ LOG_SYSTIME_STR + " ERROR: [Module2] error2\n" +
+ LOG_SYSTIME_STR + " FATAL: [Module2] fatal2\n"
+ ));
+}
+
+BOOST_AUTO_TEST_CASE(Override)
+{
+ Logging::setLevel("*", LogLevel::WARN);
+ Logging::setLevel("Module2", LogLevel::DEBUG);
+ logFromModule1();
+ logFromModule2();
+
+ Logging::flush();
+ BOOST_CHECK(os.is_equal(
+ LOG_SYSTIME_STR + " WARNING: [Module1] warn1\n" +
+ LOG_SYSTIME_STR + " ERROR: [Module1] error1\n" +
+ LOG_SYSTIME_STR + " FATAL: [Module1] fatal1\n" +
+ LOG_SYSTIME_STR + " DEBUG: [Module2] debug2\n" +
+ LOG_SYSTIME_STR + " INFO: [Module2] info2\n" +
+ LOG_SYSTIME_STR + " WARNING: [Module2] warn2\n" +
+ LOG_SYSTIME_STR + " ERROR: [Module2] error2\n" +
+ LOG_SYSTIME_STR + " FATAL: [Module2] fatal2\n"
+ ));
+}
+
+BOOST_AUTO_TEST_SUITE_END() // DefaultSeverity
+
+BOOST_AUTO_TEST_SUITE(SeverityConfig)
+
+BOOST_AUTO_TEST_CASE(SetEmpty)
+{
+ Logging::setLevel("");
+ const auto& prefixMap = Logging::get().getLevels();
+ BOOST_CHECK_EQUAL(prefixMap.size(), 0);
+ logFromModule1();
+ logFromModule2();
+
+ Logging::flush();
+ BOOST_CHECK(os.is_equal(
+ LOG_SYSTIME_STR + " FATAL: [Module1] fatal1\n" +
+ LOG_SYSTIME_STR + " FATAL: [Module2] fatal2\n"
+ ));
+}
+
+BOOST_AUTO_TEST_CASE(SetDefault)
+{
+ Logging::setLevel("*=WARN");
+ const auto& prefixMap = Logging::get().getLevels();
+ // "*" is treated as "" internally
+ BOOST_CHECK_EQUAL(prefixMap.size(), 1);
+ BOOST_CHECK_EQUAL(prefixMap.at(""), LogLevel::WARN);
+ logFromModule1();
+ logFromModule2();
+
+ Logging::flush();
+ BOOST_CHECK(os.is_equal(
+ LOG_SYSTIME_STR + " WARNING: [Module1] warn1\n" +
+ LOG_SYSTIME_STR + " ERROR: [Module1] error1\n" +
+ LOG_SYSTIME_STR + " FATAL: [Module1] fatal1\n" +
+ LOG_SYSTIME_STR + " WARNING: [Module2] warn2\n" +
+ LOG_SYSTIME_STR + " ERROR: [Module2] error2\n" +
+ LOG_SYSTIME_STR + " FATAL: [Module2] fatal2\n"
+ ));
+}
+
+BOOST_AUTO_TEST_CASE(SetModule)
+{
+ Logging::setLevel("Module1=ERROR");
+ const auto& prefixMap = Logging::get().getLevels();
+ BOOST_CHECK_EQUAL(prefixMap.size(), 1);
+ BOOST_CHECK_EQUAL(prefixMap.at("Module1"), LogLevel::ERROR);
+ logFromModule1();
+ logFromModule2();
+
+ Logging::flush();
+ BOOST_CHECK(os.is_equal(
+ LOG_SYSTIME_STR + " ERROR: [Module1] error1\n" +
+ LOG_SYSTIME_STR + " FATAL: [Module1] fatal1\n" +
+ LOG_SYSTIME_STR + " FATAL: [Module2] fatal2\n"
+ ));
+}
+
+BOOST_AUTO_TEST_CASE(SetOverride)
+{
+ Logging::setLevel("*=WARN:Module2=DEBUG");
+ const auto& prefixMap = Logging::get().getLevels();
+ BOOST_CHECK_EQUAL(prefixMap.size(), 2);
+ // "*" is treated as "" internally
+ BOOST_CHECK_EQUAL(prefixMap.at(""), LogLevel::WARN);
+ BOOST_CHECK_EQUAL(prefixMap.at("Module2"), LogLevel::DEBUG);
+ logFromModule1();
+ logFromModule2();
+
+ Logging::flush();
+ BOOST_CHECK(os.is_equal(
+ LOG_SYSTIME_STR + " WARNING: [Module1] warn1\n" +
+ LOG_SYSTIME_STR + " ERROR: [Module1] error1\n" +
+ LOG_SYSTIME_STR + " FATAL: [Module1] fatal1\n" +
+ LOG_SYSTIME_STR + " DEBUG: [Module2] debug2\n" +
+ LOG_SYSTIME_STR + " INFO: [Module2] info2\n" +
+ LOG_SYSTIME_STR + " WARNING: [Module2] warn2\n" +
+ LOG_SYSTIME_STR + " ERROR: [Module2] error2\n" +
+ LOG_SYSTIME_STR + " FATAL: [Module2] fatal2\n"
+ ));
+}
+
+BOOST_AUTO_TEST_CASE(SetTwice)
+{
+ Logging::setLevel("*=WARN");
+ Logging::setLevel("Module2=DEBUG");
+ const auto& prefixMap = Logging::get().getLevels();
+ BOOST_CHECK_EQUAL(prefixMap.size(), 2);
+ // "*" is treated as "" internally
+ BOOST_CHECK_EQUAL(prefixMap.at(""), LogLevel::WARN);
+ BOOST_CHECK_EQUAL(prefixMap.at("Module2"), LogLevel::DEBUG);
+ logFromModule1();
+ logFromModule2();
+
+ Logging::flush();
+ BOOST_CHECK(os.is_equal(
+ LOG_SYSTIME_STR + " WARNING: [Module1] warn1\n" +
+ LOG_SYSTIME_STR + " ERROR: [Module1] error1\n" +
+ LOG_SYSTIME_STR + " FATAL: [Module1] fatal1\n" +
+ LOG_SYSTIME_STR + " DEBUG: [Module2] debug2\n" +
+ LOG_SYSTIME_STR + " INFO: [Module2] info2\n" +
+ LOG_SYSTIME_STR + " WARNING: [Module2] warn2\n" +
+ LOG_SYSTIME_STR + " ERROR: [Module2] error2\n" +
+ LOG_SYSTIME_STR + " FATAL: [Module2] fatal2\n"
+ ));
+}
+
+BOOST_AUTO_TEST_CASE(Reset)
+{
+ Logging::setLevel("Module2=DEBUG");
+ Logging::setLevel("*=ERROR");
+ const auto& prefixMap = Logging::get().getLevels();
+ BOOST_CHECK_EQUAL(prefixMap.size(), 1);
+ // "*" is treated as "" internally
+ BOOST_CHECK_EQUAL(prefixMap.at(""), LogLevel::ERROR);
+ logFromModule1();
+ logFromModule2();
+
+ Logging::flush();
+ BOOST_CHECK(os.is_equal(
+ LOG_SYSTIME_STR + " ERROR: [Module1] error1\n" +
+ LOG_SYSTIME_STR + " FATAL: [Module1] fatal1\n" +
+ LOG_SYSTIME_STR + " ERROR: [Module2] error2\n" +
+ LOG_SYSTIME_STR + " FATAL: [Module2] fatal2\n"
+ ));
+}
+
+BOOST_AUTO_TEST_CASE(Malformed)
+{
+ BOOST_CHECK_THROW(Logging::setLevel("Module1=INVALID-LEVEL"), std::invalid_argument);
+ BOOST_CHECK_THROW(Logging::setLevel("Module1-MISSING-EQUAL-SIGN"), std::invalid_argument);
+}
+
+BOOST_AUTO_TEST_CASE(SetFilter)
+{
+ Logging::setLevel("*=FATAL:fm.*=DEBUG");
+ const auto& prefixMap = Logging::get().getLevels();
+ BOOST_CHECK_EQUAL(prefixMap.size(), 2);
+ // "*" is treated as "" internally
+ BOOST_CHECK_EQUAL(prefixMap.at(""), LogLevel::FATAL);
+ // "name.*" is treated as "name." internally
+ BOOST_CHECK_EQUAL(prefixMap.at("fm."), LogLevel::DEBUG);
+ logFromModule1();
+ logFromFilterModule();
+
+ Logging::flush();
+ BOOST_CHECK(os.is_equal(
+ LOG_SYSTIME_STR + " FATAL: [Module1] fatal1\n" +
+ LOG_SYSTIME_STR + " DEBUG: [fm.FilterModule] debugFM\n" +
+ LOG_SYSTIME_STR + " INFO: [fm.FilterModule] infoFM\n" +
+ LOG_SYSTIME_STR + " WARNING: [fm.FilterModule] warnFM\n" +
+ LOG_SYSTIME_STR + " ERROR: [fm.FilterModule] errorFM\n" +
+ LOG_SYSTIME_STR + " FATAL: [fm.FilterModule] fatalFM\n"
+ ));
+}
+
+BOOST_AUTO_TEST_CASE(SetOverrideFilter)
+{
+ Logging::setLevel("*=FATAL:fm.FilterModule=DEBUG");
+ Logging::setLevel("fm.*", LogLevel::INFO);
+ const auto& prefixMap = Logging::get().getLevels();
+ BOOST_CHECK_EQUAL(prefixMap.size(), 2);
+ // "*" is treated as "" internally
+ BOOST_CHECK_EQUAL(prefixMap.at(""), LogLevel::FATAL);
+ // "name.*" is treated as "name." internally
+ BOOST_CHECK_EQUAL(prefixMap.at("fm."), LogLevel::INFO);
+ logFromModule1();
+ logFromFilterModule();
+
+ Logging::flush();
+ BOOST_CHECK(os.is_equal(
+ LOG_SYSTIME_STR + " FATAL: [Module1] fatal1\n" +
+ LOG_SYSTIME_STR + " INFO: [fm.FilterModule] infoFM\n" +
+ LOG_SYSTIME_STR + " WARNING: [fm.FilterModule] warnFM\n" +
+ LOG_SYSTIME_STR + " ERROR: [fm.FilterModule] errorFM\n" +
+ LOG_SYSTIME_STR + " FATAL: [fm.FilterModule] fatalFM\n"
+ ));
+}
+
+BOOST_AUTO_TEST_CASE(FindPrefixRule)
+{
+ Logging::setLevel("*=FATAL:fm.a.*=ERROR:fm.a.b=INFO");
+ logFromNewLogger("fm.a.b");
+ logFromNewLogger("fm.a.b.c");
+ logFromNewLogger("fm.a.b.d");
+ logFromNewLogger("fm.b");
+
+ Logging::flush();
+ BOOST_CHECK(os.is_equal(
+ LOG_SYSTIME_STR + " INFO: [fm.a.b] infofm.a.b\n" +
+ LOG_SYSTIME_STR + " WARNING: [fm.a.b] warnfm.a.b\n" +
+ LOG_SYSTIME_STR + " ERROR: [fm.a.b] errorfm.a.b\n" +
+ LOG_SYSTIME_STR + " FATAL: [fm.a.b] fatalfm.a.b\n" +
+ LOG_SYSTIME_STR + " ERROR: [fm.a.b.c] errorfm.a.b.c\n" +
+ LOG_SYSTIME_STR + " FATAL: [fm.a.b.c] fatalfm.a.b.c\n" +
+ LOG_SYSTIME_STR + " ERROR: [fm.a.b.d] errorfm.a.b.d\n" +
+ LOG_SYSTIME_STR + " FATAL: [fm.a.b.d] fatalfm.a.b.d\n" +
+ LOG_SYSTIME_STR + " FATAL: [fm.b] fatalfm.b\n"
+ ));
+}
+
+BOOST_AUTO_TEST_SUITE_END() // SeverityConfig
+
+BOOST_AUTO_TEST_CASE(ChangeDestination)
+{
+ using boost::test_tools::output_test_stream;
+
+ logFromModule1();
+
+ auto os2 = make_shared<output_test_stream>();
+ Logging::setDestination(os2);
+ weak_ptr<output_test_stream> os2weak(os2);
+ os2.reset();
+
+ logFromModule2();
+
+ Logging::flush();
+ os2 = os2weak.lock();
+ BOOST_REQUIRE(os2 != nullptr);
+
+ BOOST_CHECK(os.is_equal(
+ LOG_SYSTIME_STR + " FATAL: [Module1] fatal1\n"
+ ));
+ BOOST_CHECK(os2->is_equal(
+ LOG_SYSTIME_STR + " FATAL: [Module2] fatal2\n"
+ ));
+
+ os2.reset();
+ Logging::setDestination(os);
+ BOOST_CHECK(os2weak.expired());
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestLogging
+BOOST_AUTO_TEST_SUITE_END() // Util
+
+} // namespace tests
+} // namespace util
+} // namespace ndn
diff --git a/tests/unit/util/notification-stream.t.cpp b/tests/unit/util/notification-stream.t.cpp
new file mode 100644
index 0000000..3721091
--- /dev/null
+++ b/tests/unit/util/notification-stream.t.cpp
@@ -0,0 +1,76 @@
+/* -*- 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 ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library 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 Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "util/notification-stream.hpp"
+#include "util/dummy-client-face.hpp"
+
+#include "boost-test.hpp"
+#include "simple-notification.hpp"
+#include "../identity-management-time-fixture.hpp"
+
+namespace ndn {
+namespace util {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Util)
+BOOST_FIXTURE_TEST_SUITE(TestNotificationStream, ndn::tests::IdentityManagementTimeFixture)
+
+BOOST_AUTO_TEST_CASE(Post)
+{
+ DummyClientFace face(io, m_keyChain);
+ util::NotificationStream<SimpleNotification> notificationStream(face,
+ "/localhost/nfd/NotificationStreamTest", m_keyChain);
+
+ SimpleNotification event1("msg1");
+ notificationStream.postNotification(event1);
+
+ advanceClocks(1_ms);
+
+ BOOST_REQUIRE_EQUAL(face.sentData.size(), 1);
+ BOOST_CHECK_EQUAL(face.sentData[0].getName(), "/localhost/nfd/NotificationStreamTest/%FE%00");
+ SimpleNotification decoded1;
+ BOOST_CHECK_NO_THROW(decoded1.wireDecode(face.sentData[0].getContent().blockFromValue()));
+ BOOST_CHECK_EQUAL(decoded1.getMessage(), "msg1");
+
+ SimpleNotification event2("msg2");
+ notificationStream.postNotification(event2);
+
+ advanceClocks(1_ms);
+
+ BOOST_REQUIRE_EQUAL(face.sentData.size(), 2);
+ BOOST_CHECK_EQUAL(face.sentData[1].getName(), "/localhost/nfd/NotificationStreamTest/%FE%01");
+ SimpleNotification decoded2;
+ BOOST_CHECK_NO_THROW(decoded2.wireDecode(face.sentData[1].getContent().blockFromValue()));
+ BOOST_CHECK_EQUAL(decoded2.getMessage(), "msg2");
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestNotificationStream
+BOOST_AUTO_TEST_SUITE_END() // Util
+
+} // namespace tests
+} // namespace util
+} // namespace ndn
diff --git a/tests/unit/util/notification-subscriber.t.cpp b/tests/unit/util/notification-subscriber.t.cpp
new file mode 100644
index 0000000..3fe3fd6
--- /dev/null
+++ b/tests/unit/util/notification-subscriber.t.cpp
@@ -0,0 +1,319 @@
+/* -*- 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 ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library 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 Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "util/notification-subscriber.hpp"
+#include "util/dummy-client-face.hpp"
+
+#include "boost-test.hpp"
+#include "make-interest-data.hpp"
+#include "simple-notification.hpp"
+#include "../identity-management-time-fixture.hpp"
+
+namespace ndn {
+namespace util {
+namespace tests {
+
+using namespace ndn::tests;
+
+class NotificationSubscriberFixture : public IdentityManagementTimeFixture
+{
+public:
+ NotificationSubscriberFixture()
+ : streamPrefix("ndn:/NotificationSubscriberTest")
+ , subscriberFace(io, m_keyChain)
+ , subscriber(subscriberFace, streamPrefix, 1_s)
+ , nextSendNotificationNo(0)
+ {
+ }
+
+ /** \brief deliver one notification to subscriber
+ */
+ void
+ deliverNotification(const std::string& msg)
+ {
+ SimpleNotification notification(msg);
+
+ Name dataName = streamPrefix;
+ dataName.appendSequenceNumber(nextSendNotificationNo);
+ Data data(dataName);
+ data.setContent(notification.wireEncode());
+ data.setFreshnessPeriod(1_s);
+ m_keyChain.sign(data);
+
+ lastDeliveredSeqNo = nextSendNotificationNo;
+ lastNotification.setMessage("");
+ ++nextSendNotificationNo;
+ subscriberFace.receive(data);
+ }
+
+ /** \brief deliver a Nack to subscriber
+ */
+ void
+ deliverNack(const Interest& interest, const lp::NackReason& reason)
+ {
+ lp::Nack nack = makeNack(interest, reason);
+ subscriberFace.receive(nack);
+ }
+
+ void
+ afterNotification(const SimpleNotification& notification)
+ {
+ lastNotification = notification;
+ }
+
+ void
+ afterNack(const lp::Nack& nack)
+ {
+ lastNack = nack;
+ }
+
+ void
+ afterTimeout()
+ {
+ hasTimeout = true;
+ }
+
+ void
+ afterDecodeError(const Data& data)
+ {
+ lastDecodeErrorData = data;
+ }
+
+ void
+ connectHandlers()
+ {
+ notificationConn = subscriber.onNotification.connect(
+ bind(&NotificationSubscriberFixture::afterNotification, this, _1));
+ nackConn = subscriber.onNack.connect(
+ bind(&NotificationSubscriberFixture::afterNack, this, _1));
+ subscriber.onTimeout.connect(
+ bind(&NotificationSubscriberFixture::afterTimeout, this));
+ subscriber.onDecodeError.connect(
+ bind(&NotificationSubscriberFixture::afterDecodeError, this, _1));
+ }
+
+ void
+ disconnectHandlers()
+ {
+ notificationConn.disconnect();
+ nackConn.disconnect();
+ }
+
+ /** \return true if subscriberFace has an initial request (first sent Interest)
+ */
+ bool
+ hasInitialRequest() const
+ {
+ if (subscriberFace.sentInterests.empty())
+ return false;
+
+ const Interest& interest = subscriberFace.sentInterests[0];
+ return interest.getName() == streamPrefix &&
+ interest.getChildSelector() == 1 &&
+ interest.getMustBeFresh() &&
+ interest.getInterestLifetime() == subscriber.getInterestLifetime();
+ }
+
+ /** \return sequence number of the continuation request sent from subscriberFace
+ * or 0 if there's no such request as sole sent Interest
+ */
+ uint64_t
+ getRequestSeqNo() const
+ {
+ if (subscriberFace.sentInterests.size() != 1)
+ return 0;
+
+ const Interest& interest = subscriberFace.sentInterests[0];
+ const Name& name = interest.getName();
+ if (streamPrefix.isPrefixOf(name) &&
+ name.size() == streamPrefix.size() + 1 &&
+ interest.getInterestLifetime() == subscriber.getInterestLifetime())
+ return name[-1].toSequenceNumber();
+ else
+ return 0;
+ }
+
+protected:
+ Name streamPrefix;
+ DummyClientFace subscriberFace;
+ util::NotificationSubscriber<SimpleNotification> subscriber;
+ util::signal::Connection notificationConn;
+ util::signal::Connection nackConn;
+ uint64_t nextSendNotificationNo;
+ uint64_t lastDeliveredSeqNo;
+ SimpleNotification lastNotification;
+ lp::Nack lastNack;
+ bool hasTimeout;
+ Data lastDecodeErrorData;
+};
+
+BOOST_AUTO_TEST_SUITE(Util)
+BOOST_FIXTURE_TEST_SUITE(TestNotificationSubscriber, NotificationSubscriberFixture)
+
+BOOST_AUTO_TEST_CASE(StartStop)
+{
+ BOOST_REQUIRE_EQUAL(subscriber.isRunning(), false);
+
+ // has no effect because onNotification has no handler
+ subscriber.start();
+ BOOST_REQUIRE_EQUAL(subscriber.isRunning(), false);
+
+ this->connectHandlers();
+ subscriber.start();
+ BOOST_REQUIRE_EQUAL(subscriber.isRunning(), true);
+ advanceClocks(1_ms);
+ BOOST_CHECK(this->hasInitialRequest());
+
+ subscriberFace.sentInterests.clear();
+ this->disconnectHandlers();
+ this->deliverNotification("n1");
+ BOOST_REQUIRE_EQUAL(subscriberFace.sentInterests.size(), 0);
+}
+
+BOOST_AUTO_TEST_CASE(Notifications)
+{
+ this->connectHandlers();
+ subscriber.start();
+ advanceClocks(1_ms);
+
+ // respond to initial request
+ subscriberFace.sentInterests.clear();
+ this->deliverNotification("n1");
+ advanceClocks(1_ms);
+ BOOST_CHECK_EQUAL(lastNotification.getMessage(), "n1");
+ BOOST_CHECK_EQUAL(this->getRequestSeqNo(), lastDeliveredSeqNo + 1);
+
+ // respond to continuation request
+ subscriberFace.sentInterests.clear();
+ this->deliverNotification("n2");
+ advanceClocks(1_ms);
+ BOOST_CHECK_EQUAL(lastNotification.getMessage(), "n2");
+ BOOST_CHECK_EQUAL(this->getRequestSeqNo(), lastDeliveredSeqNo + 1);
+}
+
+BOOST_AUTO_TEST_CASE(Nack)
+{
+ this->connectHandlers();
+ subscriber.start();
+ advanceClocks(1_ms);
+
+ // send the first Nack to initial request
+ BOOST_REQUIRE_EQUAL(subscriberFace.sentInterests.size(), 1);
+ Interest interest = subscriberFace.sentInterests[0];
+ subscriberFace.sentInterests.clear();
+ this->deliverNack(interest, lp::NackReason::CONGESTION);
+ advanceClocks(1_ms);
+ BOOST_CHECK_EQUAL(lastNack.getReason(), lp::NackReason::CONGESTION);
+ BOOST_REQUIRE_EQUAL(this->hasInitialRequest(), false);
+ advanceClocks(300_ms);
+ BOOST_REQUIRE_EQUAL(this->hasInitialRequest(), true);
+
+ // send the second Nack to initial request
+ BOOST_REQUIRE_EQUAL(subscriberFace.sentInterests.size(), 1);
+ interest = subscriberFace.sentInterests[0];
+ subscriberFace.sentInterests.clear();
+ this->deliverNack(interest, lp::NackReason::CONGESTION);
+ advanceClocks(301_ms);
+ BOOST_REQUIRE_EQUAL(this->hasInitialRequest(), false);
+ advanceClocks(200_ms);
+ BOOST_REQUIRE_EQUAL(this->hasInitialRequest(), true);
+
+ // send a notification to initial request
+ subscriberFace.sentInterests.clear();
+ this->deliverNotification("n1");
+ advanceClocks(1_ms);
+
+ // send a Nack to subsequent request
+ BOOST_REQUIRE_EQUAL(subscriberFace.sentInterests.size(), 1);
+ interest = subscriberFace.sentInterests[0];
+ subscriberFace.sentInterests.clear();
+ this->deliverNack(interest, lp::NackReason::CONGESTION);
+ advanceClocks(1_ms);
+ BOOST_CHECK_EQUAL(lastNack.getReason(), lp::NackReason::CONGESTION);
+ BOOST_REQUIRE_EQUAL(this->hasInitialRequest(), false);
+ advanceClocks(300_ms);
+ BOOST_REQUIRE_EQUAL(this->hasInitialRequest(), true);
+}
+
+BOOST_AUTO_TEST_CASE(Timeout)
+{
+ this->connectHandlers();
+ subscriber.start();
+ advanceClocks(1_ms);
+
+ subscriberFace.sentInterests.clear();
+ lastNotification.setMessage("");
+ advanceClocks(subscriber.getInterestLifetime(), 2);
+ BOOST_CHECK(lastNotification.getMessage().empty());
+ BOOST_CHECK_EQUAL(hasTimeout, true);
+ BOOST_CHECK(this->hasInitialRequest());
+
+ subscriberFace.sentInterests.clear();
+ this->deliverNotification("n1");
+ advanceClocks(1_ms);
+ BOOST_CHECK_EQUAL(lastNotification.getMessage(), "n1");
+}
+
+BOOST_AUTO_TEST_CASE(SequenceError)
+{
+ this->connectHandlers();
+ subscriber.start();
+ advanceClocks(1_ms);
+
+ Name wrongName = streamPrefix;
+ wrongName.append("%07%07");
+ Data wrongData(wrongName);
+ m_keyChain.sign(wrongData);
+ subscriberFace.receive(wrongData);
+ subscriberFace.sentInterests.clear();
+ lastNotification.setMessage("");
+ advanceClocks(1_ms);
+ BOOST_CHECK(lastNotification.getMessage().empty());
+ BOOST_CHECK_EQUAL(lastDecodeErrorData.getName(), wrongName);
+ BOOST_CHECK(this->hasInitialRequest());
+}
+
+BOOST_AUTO_TEST_CASE(PayloadError)
+{
+ this->connectHandlers();
+ subscriber.start();
+ advanceClocks(1_ms);
+
+ subscriberFace.sentInterests.clear();
+ lastNotification.setMessage("");
+ this->deliverNotification("\x07n4");
+ advanceClocks(1_ms);
+ BOOST_CHECK(lastNotification.getMessage().empty());
+ BOOST_CHECK(this->hasInitialRequest());
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestNotificationSubscriber
+BOOST_AUTO_TEST_SUITE_END() // Util
+
+} // namespace tests
+} // namespace util
+} // namespace ndn
diff --git a/tests/unit/util/placeholders.t.cpp b/tests/unit/util/placeholders.t.cpp
new file mode 100644
index 0000000..0dc8c70
--- /dev/null
+++ b/tests/unit/util/placeholders.t.cpp
@@ -0,0 +1,46 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library 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 Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+// Bug 2109 test case
+
+// interest.hpp includes common.hpp; common.hpp shouldn't be used from external program
+#include "interest.hpp"
+
+// util/config-file.hpp indirectly includes <boost/property_tree/ptree.hpp>
+// which in turn imports Boost placeholders
+#include "util/config-file.hpp"
+
+// It's intentional to write "using namespace",
+// to simulate an external program linked against ndn-cxx.
+using namespace ndn;
+
+void
+placeholdersTestFunction(int i)
+{
+}
+
+int
+placeholdersTestMain()
+{
+ auto f = std::bind(&placeholdersTestFunction, _1);
+ f(1);
+ return 0;
+}
diff --git a/tests/unit/util/placeholders2.t.cpp b/tests/unit/util/placeholders2.t.cpp
new file mode 100644
index 0000000..187daa9
--- /dev/null
+++ b/tests/unit/util/placeholders2.t.cpp
@@ -0,0 +1,40 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library 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 Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+// Bug 2109 test case
+
+// interest.hpp includes common.hpp; common.hpp shouldn't be used from external program
+#include "interest.hpp"
+
+#include <boost/bind.hpp>
+
+void
+placeholders2TestFunction(int i)
+{
+}
+
+int
+placeholders2TestMain()
+{
+ auto f = boost::bind(&placeholders2TestFunction, _1);
+ f(1);
+ return 0;
+}
diff --git a/tests/unit/util/random.t.cpp b/tests/unit/util/random.t.cpp
new file mode 100644
index 0000000..e830f7b
--- /dev/null
+++ b/tests/unit/util/random.t.cpp
@@ -0,0 +1,225 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library 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 Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "util/random.hpp"
+#include "security/detail/openssl.hpp"
+
+#include "boost-test.hpp"
+
+#include <boost/mpl/vector.hpp>
+#include <cmath>
+
+namespace ndn {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Util)
+BOOST_AUTO_TEST_SUITE(TestRandom)
+
+class PseudoRandomWord32
+{
+public:
+ static uint32_t
+ generate()
+ {
+ return random::generateWord32();
+ }
+};
+
+class PseudoRandomWord64
+{
+public:
+ static uint64_t
+ generate()
+ {
+ return random::generateWord64();
+ }
+};
+
+class SecureRandomWord32
+{
+public:
+ static uint32_t
+ generate()
+ {
+ return random::generateSecureWord32();
+ }
+};
+
+class SecureRandomWord64
+{
+public:
+ static uint64_t
+ generate()
+ {
+ return random::generateSecureWord64();
+ }
+};
+
+typedef boost::mpl::vector<PseudoRandomWord32,
+ PseudoRandomWord64,
+ SecureRandomWord32,
+ SecureRandomWord64> RandomGenerators;
+
+
+static double
+getDeviation(const std::vector<uint32_t>& counts, size_t size)
+{
+ // Kolmogorov-Smirnov Goodness-of-Fit Test
+ // http://www.itl.nist.gov/div898/handbook/eda/section3/eda35g.htm
+
+ std::vector<double> edf(counts.size(), 0.0);
+ double probability = 0.0;
+ for (size_t i = 0; i < counts.size(); i++) {
+ probability += 1.0 * counts[i] / size;
+ edf[i] = probability;
+ }
+
+ double t = 0.0;
+ for (size_t i = 0; i < counts.size(); i++) {
+ t = std::max(t, std::abs(edf[i] - (i * 1.0 / counts.size())));
+ }
+
+ return t;
+}
+
+
+BOOST_AUTO_TEST_CASE_TEMPLATE(GoodnessOfFit, RandomGenerator, RandomGenerators)
+{
+ const size_t MAX_BINS = 32;
+ const uint32_t MAX_ITERATIONS = 35;
+
+ std::vector<uint32_t> counts(MAX_BINS, 0);
+
+ for (uint32_t i = 0; i < MAX_ITERATIONS; i++) {
+ counts[RandomGenerator::generate() % MAX_BINS]++;
+ }
+
+ // Check if it is uniform distribution with confidence 0.95
+ // http://dlc.erieri.com/onlinetextbook/index.cfm?fuseaction=textbook.appendix&FileName=Table7
+ BOOST_WARN_LE(getDeviation(counts, MAX_ITERATIONS), 0.230);
+}
+
+BOOST_AUTO_TEST_CASE(GenerateRandomBytes)
+{
+ // Kolmogorov-Smirnov Goodness-of-Fit Test
+ // http://www.itl.nist.gov/div898/handbook/eda/section3/eda35g.htm
+
+ uint8_t buf[1024] = {0};
+ random::generateSecureBytes(buf, sizeof(buf));
+
+ std::vector<uint32_t> counts(256, 0);
+
+ for (size_t i = 0; i < sizeof(buf); i++) {
+ counts[buf[i]]++;
+ }
+
+ // Check if it is uniform distribution with confidence 0.95
+ // http://dlc.erieri.com/onlinetextbook/index.cfm?fuseaction=textbook.appendix&FileName=Table7
+ BOOST_WARN_LE(getDeviation(counts, sizeof(buf)), 0.230);
+}
+
+// This fixture uses OpenSSL routines to set a dummy random generator that always fails
+class FailRandMethodFixture
+{
+public:
+ FailRandMethodFixture()
+ : m_dummyRandMethod{&FailRandMethodFixture::seed,
+ &FailRandMethodFixture::bytes,
+ &FailRandMethodFixture::cleanup,
+ &FailRandMethodFixture::add,
+ &FailRandMethodFixture::pseudorand,
+ &FailRandMethodFixture::status}
+ {
+ m_origRandMethod = RAND_get_rand_method();
+ RAND_set_rand_method(&m_dummyRandMethod);
+ }
+
+ ~FailRandMethodFixture()
+ {
+ RAND_set_rand_method(m_origRandMethod);
+ }
+
+private: // RAND_METHOD callbacks
+#if OPENSSL_VERSION_NUMBER < 0x1010000fL
+ static void
+ seed(const void* buf, int num)
+ {
+ }
+#else
+ static int
+ seed(const void* buf, int num)
+ {
+ return 0;
+ }
+#endif // OPENSSL_VERSION_NUMBER < 0x1010000fL
+
+ static int
+ bytes(unsigned char* buf, int num)
+ {
+ return 0;
+ }
+
+ static void
+ cleanup()
+ {
+ }
+
+#if OPENSSL_VERSION_NUMBER < 0x1010000fL
+ static void
+ add(const void* buf, int num, double entropy)
+ {
+ }
+#else
+ static int
+ add(const void* buf, int num, double entropy)
+ {
+ return 0;
+ }
+#endif // OPENSSL_VERSION_NUMBER < 0x1010000fL
+
+ static int
+ pseudorand(unsigned char* buf, int num)
+ {
+ return 0;
+ }
+
+ static int
+ status()
+ {
+ return 0;
+ }
+
+private:
+ const RAND_METHOD* m_origRandMethod;
+ RAND_METHOD m_dummyRandMethod;
+};
+
+BOOST_FIXTURE_TEST_CASE(Error, FailRandMethodFixture)
+{
+ uint8_t buf[1024] = {0};
+ BOOST_CHECK_THROW(random::generateSecureBytes(buf, sizeof(buf)), std::runtime_error);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestRandom
+BOOST_AUTO_TEST_SUITE_END() // Util
+
+} // namespace tests
+} // namespace ndn
diff --git a/tests/unit/util/regex.t.cpp b/tests/unit/util/regex.t.cpp
new file mode 100644
index 0000000..777defb
--- /dev/null
+++ b/tests/unit/util/regex.t.cpp
@@ -0,0 +1,463 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library 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 Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ *
+ * @author Yingdi Yu <http://irl.cs.ucla.edu/~yingdi/>
+ */
+
+#include "util/regex.hpp"
+#include "util/regex/regex-backref-manager.hpp"
+#include "util/regex/regex-backref-matcher.hpp"
+#include "util/regex/regex-component-matcher.hpp"
+#include "util/regex/regex-component-set-matcher.hpp"
+#include "util/regex/regex-pattern-list-matcher.hpp"
+#include "util/regex/regex-repeat-matcher.hpp"
+#include "util/regex/regex-top-matcher.hpp"
+
+#include "boost-test.hpp"
+
+namespace ndn {
+namespace tests {
+
+using std::string;
+
+BOOST_AUTO_TEST_SUITE(Util)
+BOOST_AUTO_TEST_SUITE(TestRegex)
+
+BOOST_AUTO_TEST_CASE(ComponentMatcher)
+{
+ shared_ptr<RegexBackrefManager> backRef = make_shared<RegexBackrefManager>();
+ shared_ptr<RegexComponentMatcher> cm = make_shared<RegexComponentMatcher>("a", backRef);
+ bool res = cm->match(Name("/a/b/"), 0, 1);
+ BOOST_CHECK_EQUAL(res, true);
+ BOOST_CHECK_EQUAL(cm->getMatchResult().size(), 1);
+ BOOST_CHECK_EQUAL(cm->getMatchResult()[0].toUri(), string("a"));
+
+ backRef = make_shared<RegexBackrefManager>();
+ cm = make_shared<RegexComponentMatcher>("a", backRef);
+ res = cm->match(Name("/a/b/"), 1, 1);
+ BOOST_CHECK_EQUAL(res, false);
+ BOOST_CHECK_EQUAL(cm->getMatchResult().size(), 0);
+
+ backRef = make_shared<RegexBackrefManager>();
+ cm = make_shared<RegexComponentMatcher>("(c+)\\.(cd)", backRef);
+ res = cm->match(Name("/ccc.cd/b/"), 0, 1);
+ BOOST_CHECK_EQUAL(res, true);
+ BOOST_CHECK_EQUAL(cm->getMatchResult().size(), 1);
+ BOOST_CHECK_EQUAL(cm->getMatchResult()[0].toUri(), string("ccc.cd"));
+
+ BOOST_REQUIRE_EQUAL(backRef->size(), 2);
+ BOOST_CHECK_EQUAL(backRef->getBackref(0)->getMatchResult()[0].toUri(), string("ccc"));
+ BOOST_CHECK_EQUAL(backRef->getBackref(1)->getMatchResult()[0].toUri(), string("cd"));
+}
+
+BOOST_AUTO_TEST_CASE(ComponentSetMatcher)
+{
+ shared_ptr<RegexBackrefManager> backRef = make_shared<RegexBackrefManager>();
+ shared_ptr<RegexComponentSetMatcher> cm = make_shared<RegexComponentSetMatcher>("<a>", backRef);
+ bool res = cm->match(Name("/a/b/"), 0, 1);
+ BOOST_CHECK_EQUAL(res, true);
+ BOOST_CHECK_EQUAL(cm->getMatchResult().size(), 1);
+ BOOST_CHECK_EQUAL(cm->getMatchResult()[0].toUri(), string("a"));
+
+ res = cm->match(Name("/a/b/"), 1, 1);
+ BOOST_CHECK_EQUAL(res, false);
+ BOOST_CHECK_EQUAL(cm->getMatchResult().size(), 0);
+
+ res = cm->match(Name("/a/b/"), 0, 2);
+ BOOST_CHECK_EQUAL(res, false);
+ BOOST_CHECK_EQUAL(cm->getMatchResult().size(), 0);
+
+ backRef = make_shared<RegexBackrefManager>();
+ cm = make_shared<RegexComponentSetMatcher>("[<a><b><c>]", backRef);
+ res = cm->match(Name("/a/b/d"), 1, 1);
+ BOOST_CHECK_EQUAL(res, true);
+ BOOST_CHECK_EQUAL(cm->getMatchResult().size(), 1);
+ BOOST_CHECK_EQUAL(cm->getMatchResult()[0].toUri(), string("b"));
+
+ res = cm->match(Name("/a/b/d"), 2, 1);
+ BOOST_CHECK_EQUAL(res, false);
+ BOOST_CHECK_EQUAL(cm->getMatchResult().size(), 0);
+
+ backRef = make_shared<RegexBackrefManager>();
+ cm = make_shared<RegexComponentSetMatcher>("[^<a><b><c>]", backRef);
+ res = cm->match(Name("/b/d"), 1, 1);
+ BOOST_CHECK_EQUAL(res, true);
+ BOOST_CHECK_EQUAL(cm->getMatchResult().size(), 1);
+ BOOST_CHECK_EQUAL(cm->getMatchResult()[0].toUri(), string("d"));
+}
+
+BOOST_AUTO_TEST_CASE(RepeatMatcher)
+{
+ shared_ptr<RegexBackrefManager> backRef = make_shared<RegexBackrefManager>();
+ shared_ptr<RegexRepeatMatcher> cm = make_shared<RegexRepeatMatcher>("[<a><b>]*", backRef, 8);
+ bool res = cm->match(Name("/a/b/c"), 0, 0);
+ BOOST_CHECK_EQUAL(res, true);
+ BOOST_CHECK_EQUAL(cm->getMatchResult().size(), 0);
+
+ res = cm->match(Name("/a/b/c"), 0, 2);
+ BOOST_CHECK_EQUAL(res, true);
+ BOOST_CHECK_EQUAL(cm->getMatchResult().size(), 2);
+ BOOST_CHECK_EQUAL(cm->getMatchResult()[0].toUri(), string("a"));
+ BOOST_CHECK_EQUAL(cm->getMatchResult()[1].toUri(), string("b"));
+
+ backRef = make_shared<RegexBackrefManager>();
+ cm = make_shared<RegexRepeatMatcher>("[<a><b>]+", backRef, 8);
+ res = cm->match(Name("/a/b/c"), 0, 0);
+ BOOST_CHECK_EQUAL(res, false);
+ BOOST_CHECK_EQUAL(cm->getMatchResult().size(), 0);
+
+ res = cm->match(Name("/a/b/c"), 0, 2);
+ BOOST_CHECK_EQUAL(res, true);
+ BOOST_CHECK_EQUAL(cm->getMatchResult().size(), 2);
+ BOOST_CHECK_EQUAL(cm->getMatchResult()[0].toUri(), string("a"));
+ BOOST_CHECK_EQUAL(cm->getMatchResult()[1].toUri(), string("b"));
+
+ backRef = make_shared<RegexBackrefManager>();
+ cm = make_shared<RegexRepeatMatcher>("<.*>*", backRef, 4);
+ res = cm->match(Name("/a/b/c/d/e/f/"), 0, 6);
+ BOOST_CHECK_EQUAL(res, true);
+ BOOST_CHECK_EQUAL(cm->getMatchResult().size(), 6);
+ BOOST_CHECK_EQUAL(cm->getMatchResult()[0].toUri(), string("a"));
+ BOOST_CHECK_EQUAL(cm->getMatchResult()[1].toUri(), string("b"));
+ BOOST_CHECK_EQUAL(cm->getMatchResult()[2].toUri(), string("c"));
+ BOOST_CHECK_EQUAL(cm->getMatchResult()[3].toUri(), string("d"));
+ BOOST_CHECK_EQUAL(cm->getMatchResult()[4].toUri(), string("e"));
+ BOOST_CHECK_EQUAL(cm->getMatchResult()[5].toUri(), string("f"));
+
+ backRef = make_shared<RegexBackrefManager>();
+ cm = make_shared<RegexRepeatMatcher>("<>*", backRef, 2);
+ res = cm->match(Name("/a/b/c/d/e/f/"), 0, 6);
+ BOOST_CHECK_EQUAL(res, true);
+ BOOST_CHECK_EQUAL(cm->getMatchResult().size(), 6);
+ BOOST_CHECK_EQUAL(cm->getMatchResult()[0].toUri(), string("a"));
+ BOOST_CHECK_EQUAL(cm->getMatchResult()[1].toUri(), string("b"));
+ BOOST_CHECK_EQUAL(cm->getMatchResult()[2].toUri(), string("c"));
+ BOOST_CHECK_EQUAL(cm->getMatchResult()[3].toUri(), string("d"));
+ BOOST_CHECK_EQUAL(cm->getMatchResult()[4].toUri(), string("e"));
+ BOOST_CHECK_EQUAL(cm->getMatchResult()[5].toUri(), string("f"));
+
+ backRef = make_shared<RegexBackrefManager>();
+ cm = make_shared<RegexRepeatMatcher>("<a>?", backRef, 3);
+ res = cm->match(Name("/a/b/c"), 0, 0);
+ BOOST_CHECK_EQUAL(res, true);
+ BOOST_CHECK_EQUAL(cm->getMatchResult().size(), 0);
+
+ cm = make_shared<RegexRepeatMatcher>("<a>?", backRef, 3);
+ res = cm->match(Name("/a/b/c"), 0, 1);
+ BOOST_CHECK_EQUAL(res, true);
+ BOOST_CHECK_EQUAL(cm->getMatchResult().size(), 1);
+ BOOST_CHECK_EQUAL(cm->getMatchResult()[0].toUri(), string("a"));
+
+ cm = make_shared<RegexRepeatMatcher>("<a>?", backRef, 3);
+ res = cm->match(Name("/a/b/c"), 0, 2);
+ BOOST_CHECK_EQUAL(res, false);
+ BOOST_CHECK_EQUAL(cm->getMatchResult().size(), 0);
+
+ backRef = make_shared<RegexBackrefManager>();
+ cm = make_shared<RegexRepeatMatcher>("[<a><b>]{3}", backRef, 8);
+ res = cm->match(Name("/a/b/a/d/"), 0, 2);
+ BOOST_CHECK_EQUAL(res, false);
+ BOOST_CHECK_EQUAL(cm->getMatchResult().size(), 0);
+
+ res = cm->match(Name("/a/b/a/d/"), 0, 3);
+ BOOST_CHECK_EQUAL(res, true);
+ BOOST_CHECK_EQUAL(cm->getMatchResult().size(), 3);
+ BOOST_CHECK_EQUAL(cm->getMatchResult()[0].toUri(), string("a"));
+ BOOST_CHECK_EQUAL(cm->getMatchResult()[1].toUri(), string("b"));
+ BOOST_CHECK_EQUAL(cm->getMatchResult()[2].toUri(), string("a"));
+
+ res = cm->match(Name("/a/b/a/d/"), 0, 4);
+ BOOST_CHECK_EQUAL(res, false);
+ BOOST_CHECK_EQUAL(cm->getMatchResult().size(), 0);
+
+ backRef = make_shared<RegexBackrefManager>();
+ cm = make_shared<RegexRepeatMatcher>("[<a><b>]{2,3}", backRef, 8);
+ res = cm->match(Name("/a/b/a/d/e/"), 0, 2);
+ BOOST_CHECK_EQUAL(res, true);
+ BOOST_CHECK_EQUAL(cm->getMatchResult().size(), 2);
+ BOOST_CHECK_EQUAL(cm->getMatchResult()[0].toUri(), string("a"));
+ BOOST_CHECK_EQUAL(cm->getMatchResult()[1].toUri(), string("b"));
+
+ res = cm->match(Name("/a/b/a/d/e/"), 0, 3);
+ BOOST_CHECK_EQUAL(res, true);
+ BOOST_CHECK_EQUAL(cm->getMatchResult().size(), 3);
+ BOOST_CHECK_EQUAL(cm->getMatchResult()[0].toUri(), string("a"));
+ BOOST_CHECK_EQUAL(cm->getMatchResult()[1].toUri(), string("b"));
+ BOOST_CHECK_EQUAL(cm->getMatchResult()[2].toUri(), string("a"));
+
+ res = cm->match(Name("/a/b/a/b/e/"), 0, 4);
+ BOOST_CHECK_EQUAL(res, false);
+ BOOST_CHECK_EQUAL(cm->getMatchResult().size(), 0);
+
+ res = cm->match(Name("/a/b/a/d/e/"), 0, 1);
+ BOOST_CHECK_EQUAL(res, false);
+ BOOST_CHECK_EQUAL(cm->getMatchResult().size(), 0);
+
+ backRef = make_shared<RegexBackrefManager>();
+ cm = make_shared<RegexRepeatMatcher>("[<a><b>]{2,}", backRef, 8);
+ res = cm->match(Name("/a/b/a/d/e/"), 0, 2);
+ BOOST_CHECK_EQUAL(res, true);
+ BOOST_CHECK_EQUAL(cm->getMatchResult().size(), 2);
+ BOOST_CHECK_EQUAL(cm->getMatchResult()[0].toUri(), string("a"));
+ BOOST_CHECK_EQUAL(cm->getMatchResult()[1].toUri(), string("b"));
+
+ res = cm->match(Name("/a/b/a/b/e/"), 0, 4);
+ BOOST_CHECK_EQUAL(res, true);
+ BOOST_CHECK_EQUAL(cm->getMatchResult().size(), 4);
+ BOOST_CHECK_EQUAL(cm->getMatchResult()[0].toUri(), string("a"));
+ BOOST_CHECK_EQUAL(cm->getMatchResult()[1].toUri(), string("b"));
+ BOOST_CHECK_EQUAL(cm->getMatchResult()[2].toUri(), string("a"));
+ BOOST_CHECK_EQUAL(cm->getMatchResult()[3].toUri(), string("b"));
+
+ res = cm->match(Name("/a/b/a/d/e/"), 0, 1);
+ BOOST_CHECK_EQUAL(res, false);
+ BOOST_CHECK_EQUAL(cm->getMatchResult().size(), 0);
+
+ backRef = make_shared<RegexBackrefManager>();
+ cm = make_shared<RegexRepeatMatcher>("[<a><b>]{,2}", backRef, 8);
+ res = cm->match(Name("/a/b/a/b/e/"), 0, 3);
+ BOOST_CHECK_EQUAL(res, false);
+ BOOST_CHECK_EQUAL(cm->getMatchResult().size(), 0);
+
+ res = cm->match(Name("/a/b/a/b/e/"), 0, 2);
+ BOOST_CHECK_EQUAL(res, true);
+ BOOST_CHECK_EQUAL(cm->getMatchResult().size(), 2);
+ BOOST_CHECK_EQUAL(cm->getMatchResult()[0].toUri(), string("a"));
+ BOOST_CHECK_EQUAL(cm->getMatchResult()[1].toUri(), string("b"));
+
+ res = cm->match(Name("/a/b/a/d/e/"), 0, 1);
+ BOOST_CHECK_EQUAL(res, true);
+ BOOST_CHECK_EQUAL(cm->getMatchResult().size(), 1);
+ BOOST_CHECK_EQUAL(cm->getMatchResult()[0].toUri(), string("a"));
+
+ res = cm->match(Name("/a/b/a/d/e/"), 0, 0);
+ BOOST_CHECK_EQUAL(res, true);
+ BOOST_CHECK_EQUAL(cm->getMatchResult().size(), 0);
+}
+
+BOOST_AUTO_TEST_CASE(BackRefMatcher)
+{
+ shared_ptr<RegexBackrefManager> backRef = make_shared<RegexBackrefManager>();
+ shared_ptr<RegexBackrefMatcher> cm = make_shared<RegexBackrefMatcher>("(<a><b>)", backRef);
+ backRef->pushRef(static_pointer_cast<RegexMatcher>(cm));
+ cm->lateCompile();
+ bool res = cm->match(Name("/a/b/c"), 0, 2);
+ BOOST_CHECK_EQUAL(res, true);
+ BOOST_CHECK_EQUAL(cm->getMatchResult().size(), 2);
+ BOOST_CHECK_EQUAL(cm->getMatchResult()[0].toUri(), string("a"));
+ BOOST_CHECK_EQUAL(cm->getMatchResult()[1].toUri(), string("b"));
+ BOOST_CHECK_EQUAL(backRef->size(), 1);
+
+ backRef = make_shared<RegexBackrefManager>();
+ cm = make_shared<RegexBackrefMatcher>("(<a>(<b>))", backRef);
+ backRef->pushRef(cm);
+ cm->lateCompile();
+ res = cm->match(Name("/a/b/c"), 0, 2);
+ BOOST_CHECK_EQUAL(res, true);
+ BOOST_CHECK_EQUAL(cm->getMatchResult().size(), 2);
+ BOOST_CHECK_EQUAL(cm->getMatchResult()[0].toUri(), string("a"));
+ BOOST_CHECK_EQUAL(cm->getMatchResult()[1].toUri(), string("b"));
+ BOOST_CHECK_EQUAL(backRef->size(), 2);
+ BOOST_CHECK_EQUAL(backRef->getBackref(0)->getMatchResult()[0].toUri(), string("a"));
+ BOOST_CHECK_EQUAL(backRef->getBackref(0)->getMatchResult()[1].toUri(), string("b"));
+ BOOST_CHECK_EQUAL(backRef->getBackref(1)->getMatchResult()[0].toUri(), string("b"));
+}
+
+BOOST_AUTO_TEST_CASE(BackRefMatcherAdvanced)
+{
+ shared_ptr<RegexBackrefManager> backRef = make_shared<RegexBackrefManager>();
+ shared_ptr<RegexRepeatMatcher> cm = make_shared<RegexRepeatMatcher>("([<a><b>])+", backRef, 10);
+ bool res = cm->match(Name("/a/b/c"), 0, 2);
+ BOOST_CHECK_EQUAL(res, true);
+ BOOST_CHECK_EQUAL(cm->getMatchResult().size(), 2);
+ BOOST_CHECK_EQUAL(cm->getMatchResult()[0].toUri(), string("a"));
+ BOOST_CHECK_EQUAL(cm->getMatchResult()[1].toUri(), string("b"));
+ BOOST_CHECK_EQUAL(backRef->size(), 1);
+ BOOST_CHECK_EQUAL(backRef->getBackref(0)->getMatchResult()[0].toUri(), string("b"));
+}
+
+BOOST_AUTO_TEST_CASE(BackRefMatcherAdvanced2)
+{
+ shared_ptr<RegexBackrefManager> backRef = make_shared<RegexBackrefManager>();
+ shared_ptr<RegexPatternListMatcher> cm = make_shared<RegexPatternListMatcher>("(<a>(<b>))<c>", backRef);
+ bool res = cm->match(Name("/a/b/c"), 0, 3);
+ BOOST_CHECK_EQUAL(res, true);
+ BOOST_CHECK_EQUAL(cm->getMatchResult().size(), 3);
+ BOOST_CHECK_EQUAL(cm->getMatchResult()[0].toUri(), string("a"));
+ BOOST_CHECK_EQUAL(cm->getMatchResult()[1].toUri(), string("b"));
+ BOOST_CHECK_EQUAL(cm->getMatchResult()[2].toUri(), string("c"));
+ BOOST_CHECK_EQUAL(backRef->size(), 2);
+ BOOST_CHECK_EQUAL(backRef->getBackref(0)->getMatchResult()[0].toUri(), string("a"));
+ BOOST_CHECK_EQUAL(backRef->getBackref(0)->getMatchResult()[1].toUri(), string("b"));
+ BOOST_CHECK_EQUAL(backRef->getBackref(1)->getMatchResult()[0].toUri(), string("b"));
+}
+
+BOOST_AUTO_TEST_CASE(PatternListMatcher)
+{
+ shared_ptr<RegexBackrefManager> backRef = make_shared<RegexBackrefManager>();
+ shared_ptr<RegexPatternListMatcher> cm = make_shared<RegexPatternListMatcher>("<a>[<a><b>]", backRef);
+ bool res = cm->match(Name("/a/b/c"), 0, 2);
+ BOOST_CHECK_EQUAL(res, true);
+ BOOST_CHECK_EQUAL(cm->getMatchResult().size(), 2);
+ BOOST_CHECK_EQUAL(cm->getMatchResult()[0].toUri(), string("a"));
+ BOOST_CHECK_EQUAL(cm->getMatchResult()[1].toUri(), string("b"));
+
+ backRef = make_shared<RegexBackrefManager>();
+ cm = make_shared<RegexPatternListMatcher>("<>*<a>", backRef);
+ res = cm->match(Name("/a/b/c"), 0, 1);
+ BOOST_CHECK_EQUAL(res, true);
+ BOOST_CHECK_EQUAL(cm->getMatchResult().size(), 1);
+ BOOST_CHECK_EQUAL(cm->getMatchResult()[0].toUri(), string("a"));
+
+ backRef = make_shared<RegexBackrefManager>();
+ cm = make_shared<RegexPatternListMatcher>("<>*<a>", backRef);
+ res = cm->match(Name("/a/b/c"), 0, 2);
+ BOOST_CHECK_EQUAL(res, false);
+ BOOST_CHECK_EQUAL(cm->getMatchResult().size(), 0);
+
+ backRef = make_shared<RegexBackrefManager>();
+ cm = make_shared<RegexPatternListMatcher>("<>*<a><>*", backRef);
+ res = cm->match(Name("/a/b/c"), 0, 3);
+ BOOST_CHECK_EQUAL(res, true);
+ BOOST_CHECK_EQUAL(cm->getMatchResult().size(), 3);
+ BOOST_CHECK_EQUAL(cm->getMatchResult()[0].toUri(), string("a"));
+ BOOST_CHECK_EQUAL(cm->getMatchResult()[1].toUri(), string("b"));
+ BOOST_CHECK_EQUAL(cm->getMatchResult()[2].toUri(), string("c"));
+}
+
+BOOST_AUTO_TEST_CASE(TopMatcher)
+{
+ shared_ptr<RegexTopMatcher> cm = make_shared<RegexTopMatcher>("^<a><b><c>");
+ bool res = cm->match(Name("/a/b/c/d"));
+ BOOST_CHECK_EQUAL(res, true);
+ BOOST_CHECK_EQUAL(cm->getMatchResult().size(), 4);
+ BOOST_CHECK_EQUAL(cm->getMatchResult()[0].toUri(), string("a"));
+ BOOST_CHECK_EQUAL(cm->getMatchResult()[1].toUri(), string("b"));
+ BOOST_CHECK_EQUAL(cm->getMatchResult()[2].toUri(), string("c"));
+ BOOST_CHECK_EQUAL(cm->getMatchResult()[3].toUri(), string("d"));
+
+ cm = make_shared<RegexTopMatcher>("<b><c><d>$");
+ res = cm->match(Name("/a/b/c/d"));
+ BOOST_CHECK_EQUAL(res, true);
+ BOOST_CHECK_EQUAL(cm->getMatchResult().size(), 4);
+ BOOST_CHECK_EQUAL(cm->getMatchResult()[0].toUri(), string("a"));
+ BOOST_CHECK_EQUAL(cm->getMatchResult()[1].toUri(), string("b"));
+ BOOST_CHECK_EQUAL(cm->getMatchResult()[2].toUri(), string("c"));
+ BOOST_CHECK_EQUAL(cm->getMatchResult()[3].toUri(), string("d"));
+
+ cm = make_shared<RegexTopMatcher>("^<a><b><c><d>$");
+ res = cm->match(Name("/a/b/c/d"));
+ BOOST_CHECK_EQUAL(res, true);
+ BOOST_CHECK_EQUAL(cm->getMatchResult().size(), 4);
+ BOOST_CHECK_EQUAL(cm->getMatchResult()[0].toUri(), string("a"));
+ BOOST_CHECK_EQUAL(cm->getMatchResult()[1].toUri(), string("b"));
+ BOOST_CHECK_EQUAL(cm->getMatchResult()[2].toUri(), string("c"));
+ BOOST_CHECK_EQUAL(cm->getMatchResult()[3].toUri(), string("d"));
+
+ res = cm->match(Name("/a/b/c/d/e"));
+ BOOST_CHECK_EQUAL(res, false);
+ BOOST_CHECK_EQUAL(cm->getMatchResult().size(), 0);
+
+ cm = make_shared<RegexTopMatcher>("<a><b><c><d>");
+ res = cm->match(Name("/a/b/c/d"));
+ BOOST_CHECK_EQUAL(res, true);
+ BOOST_CHECK_EQUAL(cm->getMatchResult().size(), 4);
+ BOOST_CHECK_EQUAL(cm->getMatchResult()[0].toUri(), string("a"));
+ BOOST_CHECK_EQUAL(cm->getMatchResult()[1].toUri(), string("b"));
+ BOOST_CHECK_EQUAL(cm->getMatchResult()[2].toUri(), string("c"));
+ BOOST_CHECK_EQUAL(cm->getMatchResult()[3].toUri(), string("d"));
+
+ cm = make_shared<RegexTopMatcher>("<b><c>");
+ res = cm->match(Name("/a/b/c/d"));
+ BOOST_CHECK_EQUAL(res, true);
+ BOOST_CHECK_EQUAL(cm->getMatchResult().size(), 4);
+ BOOST_CHECK_EQUAL(cm->getMatchResult()[0].toUri(), string("a"));
+ BOOST_CHECK_EQUAL(cm->getMatchResult()[1].toUri(), string("b"));
+ BOOST_CHECK_EQUAL(cm->getMatchResult()[2].toUri(), string("c"));
+ BOOST_CHECK_EQUAL(cm->getMatchResult()[3].toUri(), string("d"));
+}
+
+BOOST_AUTO_TEST_CASE(TopMatcherAdvanced)
+{
+ shared_ptr<Regex> cm = make_shared<Regex>("^(<.*>*)<.*>");
+ bool res = cm->match(Name("/n/a/b/c"));
+ BOOST_CHECK_EQUAL(res, true);
+ BOOST_CHECK_EQUAL(cm->getMatchResult().size(), 4);
+ BOOST_CHECK_EQUAL(cm->expand("\\1"), Name("/n/a/b/"));
+
+ cm = make_shared<Regex>("^(<.*>*)<.*><c>(<.*>)<.*>");
+ res = cm->match(Name("/n/a/b/c/d/e/"));
+ BOOST_CHECK_EQUAL(res, true);
+ BOOST_CHECK_EQUAL(cm->getMatchResult().size(), 6);
+ BOOST_CHECK_EQUAL(cm->expand("\\1\\2"), Name("/n/a/d/"));
+
+ cm = make_shared<Regex>("(<.*>*)<.*>$");
+ res = cm->match(Name("/n/a/b/c/"));
+ BOOST_CHECK_EQUAL(res, true);
+ BOOST_CHECK_EQUAL(cm->getMatchResult().size(), 4);
+ BOOST_CHECK_EQUAL(cm->expand("\\1"), Name("/n/a/b/"));
+
+ cm = make_shared<Regex>("<.*>(<.*>*)<.*>$");
+ res = cm->match(Name("/n/a/b/c/"));
+ BOOST_CHECK_EQUAL(res, true);
+ BOOST_CHECK_EQUAL(cm->getMatchResult().size(), 4);
+ BOOST_CHECK_EQUAL(cm->expand("\\1"), Name("/a/b/"));
+
+ cm = make_shared<Regex>("<a>(<>*)<>$");
+ res = cm->match(Name("/n/a/b/c/"));
+ BOOST_CHECK_EQUAL(res, true);
+ BOOST_CHECK_EQUAL(cm->getMatchResult().size(), 4);
+ BOOST_CHECK_EQUAL(cm->expand("\\1"), Name("/b/"));
+
+ cm = make_shared<Regex>("^<ndn><(.*)\\.(.*)><DNS>(<>*)<>");
+ res = cm->match(Name("/ndn/ucla.edu/DNS/yingdi/mac/ksk-1/"));
+ BOOST_CHECK_EQUAL(res, true);
+ BOOST_CHECK_EQUAL(cm->getMatchResult().size(), 6);
+ BOOST_CHECK_EQUAL(cm->expand("<ndn>\\2\\1\\3"), Name("/ndn/edu/ucla/yingdi/mac/"));
+
+ cm = make_shared<Regex>("^<ndn><(.*)\\.(.*)><DNS>(<>*)<>", "<ndn>\\2\\1\\3");
+ res = cm->match(Name("/ndn/ucla.edu/DNS/yingdi/mac/ksk-1/"));
+ BOOST_CHECK_EQUAL(res, true);
+ BOOST_CHECK_EQUAL(cm->getMatchResult().size(), 6);
+ BOOST_CHECK_EQUAL(cm->expand(), Name("/ndn/edu/ucla/yingdi/mac/"));
+}
+
+BOOST_AUTO_TEST_CASE(RegexBackrefManagerMemoryLeak)
+{
+ auto re = make_unique<Regex>("^(<>)$");
+
+ weak_ptr<RegexPatternListMatcher> m1(re->m_primaryMatcher);
+ weak_ptr<RegexPatternListMatcher> m2(re->m_secondaryMatcher);
+ weak_ptr<RegexBackrefManager> b1(re->m_primaryBackrefManager);
+ weak_ptr<RegexBackrefManager> b2(re->m_secondaryBackrefManager);
+
+ re.reset();
+
+ BOOST_CHECK_EQUAL(m1.use_count(), 0);
+ BOOST_CHECK_EQUAL(m2.use_count(), 0);
+ BOOST_CHECK_EQUAL(b1.use_count(), 0);
+ BOOST_CHECK_EQUAL(b2.use_count(), 0);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestRegex
+BOOST_AUTO_TEST_SUITE_END() // Util
+
+} // namespace tests
+} // namespace ndn
diff --git a/tests/unit/util/scheduler.t.cpp b/tests/unit/util/scheduler.t.cpp
new file mode 100644
index 0000000..05380f3
--- /dev/null
+++ b/tests/unit/util/scheduler.t.cpp
@@ -0,0 +1,417 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library 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 Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "util/scheduler.hpp"
+#include "util/scheduler-scoped-event-id.hpp"
+
+#include "boost-test.hpp"
+#include "../unit-test-time-fixture.hpp"
+
+#include <boost/lexical_cast.hpp>
+
+namespace ndn {
+namespace util {
+namespace scheduler {
+namespace tests {
+
+using namespace ndn::tests;
+
+class SchedulerFixture : public UnitTestTimeFixture
+{
+public:
+ SchedulerFixture()
+ : scheduler(io)
+ {
+ }
+
+public:
+ Scheduler scheduler;
+};
+
+BOOST_AUTO_TEST_SUITE(Util)
+BOOST_FIXTURE_TEST_SUITE(TestScheduler, SchedulerFixture)
+
+BOOST_AUTO_TEST_SUITE(General)
+
+BOOST_AUTO_TEST_CASE(Events)
+{
+ size_t count1 = 0;
+ size_t count2 = 0;
+
+ scheduler.scheduleEvent(500_ms, [&] {
+ ++count1;
+ BOOST_CHECK_EQUAL(count2, 1);
+ });
+
+ EventId i = scheduler.scheduleEvent(1_s, [&] {
+ BOOST_ERROR("This event should not have been fired");
+ });
+ scheduler.cancelEvent(i);
+
+ scheduler.scheduleEvent(250_ms, [&] {
+ BOOST_CHECK_EQUAL(count1, 0);
+ ++count2;
+ });
+
+ i = scheduler.scheduleEvent(50_ms, [&] {
+ BOOST_ERROR("This event should not have been fired");
+ });
+ scheduler.cancelEvent(i);
+
+ advanceClocks(25_ms, 1000_ms);
+ BOOST_CHECK_EQUAL(count1, 1);
+ BOOST_CHECK_EQUAL(count2, 1);
+}
+
+BOOST_AUTO_TEST_CASE(CallbackException)
+{
+ class MyException : public std::exception
+ {
+ };
+ scheduler.scheduleEvent(10_ms, [] { BOOST_THROW_EXCEPTION(MyException()); });
+
+ bool isCallbackInvoked = false;
+ scheduler.scheduleEvent(20_ms, [&isCallbackInvoked] { isCallbackInvoked = true; });
+
+ BOOST_CHECK_THROW(this->advanceClocks(6_ms, 2), MyException);
+ this->advanceClocks(6_ms, 2);
+ BOOST_CHECK(isCallbackInvoked);
+}
+
+BOOST_AUTO_TEST_CASE(CancelEmptyEvent)
+{
+ EventId i;
+ scheduler.cancelEvent(i);
+
+ // avoid "test case [...] did not check any assertions" message from Boost.Test
+ BOOST_CHECK(true);
+}
+
+BOOST_AUTO_TEST_CASE(SelfCancel)
+{
+ EventId selfEventId;
+ selfEventId = scheduler.scheduleEvent(100_ms, [&] {
+ scheduler.cancelEvent(selfEventId);
+ });
+
+ BOOST_REQUIRE_NO_THROW(advanceClocks(100_ms, 10));
+}
+
+class SelfRescheduleFixture : public SchedulerFixture
+{
+public:
+ SelfRescheduleFixture()
+ : count(0)
+ {
+ }
+
+ void
+ reschedule()
+ {
+ EventId eventId = scheduler.scheduleEvent(100_ms,
+ bind(&SelfRescheduleFixture::reschedule, this));
+ scheduler.cancelEvent(selfEventId);
+ selfEventId = eventId;
+
+ if (count < 5)
+ count++;
+ else
+ scheduler.cancelEvent(selfEventId);
+ }
+
+ void
+ reschedule2()
+ {
+ scheduler.cancelEvent(selfEventId);
+
+ if (count < 5) {
+ selfEventId = scheduler.scheduleEvent(100_ms,
+ bind(&SelfRescheduleFixture::reschedule2, this));
+ count++;
+ }
+ }
+
+ void
+ reschedule3()
+ {
+ scheduler.cancelEvent(selfEventId);
+
+ scheduler.scheduleEvent(100_ms, [&] { ++count; });
+ scheduler.scheduleEvent(100_ms, [&] { ++count; });
+ scheduler.scheduleEvent(100_ms, [&] { ++count; });
+ scheduler.scheduleEvent(100_ms, [&] { ++count; });
+ scheduler.scheduleEvent(100_ms, [&] { ++count; });
+ scheduler.scheduleEvent(100_ms, [&] { ++count; });
+ }
+
+public:
+ EventId selfEventId;
+ size_t count;
+};
+
+BOOST_FIXTURE_TEST_CASE(Reschedule, SelfRescheduleFixture)
+{
+ selfEventId = scheduler.scheduleEvent(0_s,
+ bind(&SelfRescheduleFixture::reschedule, this));
+
+ BOOST_REQUIRE_NO_THROW(advanceClocks(50_ms, 1000_ms));
+
+ BOOST_CHECK_EQUAL(count, 5);
+}
+
+BOOST_FIXTURE_TEST_CASE(Reschedule2, SelfRescheduleFixture)
+{
+ selfEventId = scheduler.scheduleEvent(0_s,
+ bind(&SelfRescheduleFixture::reschedule2, this));
+
+ BOOST_REQUIRE_NO_THROW(advanceClocks(50_ms, 1000_ms));
+
+ BOOST_CHECK_EQUAL(count, 5);
+}
+
+BOOST_FIXTURE_TEST_CASE(Reschedule3, SelfRescheduleFixture)
+{
+ selfEventId = scheduler.scheduleEvent(0_s,
+ bind(&SelfRescheduleFixture::reschedule3, this));
+
+ BOOST_REQUIRE_NO_THROW(advanceClocks(50_ms, 1000_ms));
+
+ BOOST_CHECK_EQUAL(count, 6);
+}
+
+class CancelAllFixture : public SchedulerFixture
+{
+public:
+ CancelAllFixture()
+ : count(0)
+ {
+ }
+
+ void
+ event()
+ {
+ ++count;
+
+ scheduler.scheduleEvent(1_s, [&] { event(); });
+ }
+
+public:
+ uint32_t count;
+};
+
+BOOST_FIXTURE_TEST_CASE(CancelAll, CancelAllFixture)
+{
+ scheduler.scheduleEvent(500_ms, [&] { scheduler.cancelAllEvents(); });
+
+ scheduler.scheduleEvent(1_s, [&] { event(); });
+
+ scheduler.scheduleEvent(3_s, [] {
+ BOOST_ERROR("This event should have been cancelled" );
+ });
+
+ advanceClocks(100_ms, 100);
+
+ BOOST_CHECK_EQUAL(count, 0);
+}
+
+BOOST_AUTO_TEST_CASE(CancelAllWithScopedEventId) // Bug 3691
+{
+ Scheduler sched(io);
+ ScopedEventId eid(sched);
+ eid = sched.scheduleEvent(10_ms, []{});
+ sched.cancelAllEvents();
+ eid.cancel(); // should not crash
+
+ // avoid "test case [...] did not check any assertions" message from Boost.Test
+ BOOST_CHECK(true);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // General
+
+BOOST_AUTO_TEST_SUITE(EventId)
+
+using scheduler::EventId;
+
+BOOST_AUTO_TEST_CASE(ConstructEmpty)
+{
+ EventId eid;
+ eid = nullptr;
+ EventId eid2(nullptr);
+
+ BOOST_CHECK(!eid && !eid2);
+}
+
+BOOST_AUTO_TEST_CASE(Compare)
+{
+ EventId eid, eid2;
+ BOOST_CHECK_EQUAL(eid == eid2, true);
+ BOOST_CHECK_EQUAL(eid != eid2, false);
+ BOOST_CHECK_EQUAL(eid == nullptr, true);
+ BOOST_CHECK_EQUAL(eid != nullptr, false);
+
+ eid = scheduler.scheduleEvent(10_ms, []{});
+ BOOST_CHECK_EQUAL(eid == eid2, false);
+ BOOST_CHECK_EQUAL(eid != eid2, true);
+ BOOST_CHECK_EQUAL(eid == nullptr, false);
+ BOOST_CHECK_EQUAL(eid != nullptr, true);
+
+ eid2 = eid;
+ BOOST_CHECK_EQUAL(eid, eid2);
+ BOOST_CHECK_EQUAL(eid != eid2, false);
+
+ eid2 = scheduler.scheduleEvent(10_ms, []{});
+ BOOST_CHECK_EQUAL(eid == eid2, false);
+ BOOST_CHECK_NE(eid, eid2);
+}
+
+BOOST_AUTO_TEST_CASE(Valid)
+{
+ EventId eid;
+ BOOST_CHECK_EQUAL(static_cast<bool>(eid), false);
+ BOOST_CHECK_EQUAL(!eid, true);
+
+ eid = scheduler.scheduleEvent(10_ms, []{});
+ BOOST_CHECK_EQUAL(static_cast<bool>(eid), true);
+ BOOST_CHECK_EQUAL(!eid, false);
+
+ EventId eid2 = eid;
+ scheduler.cancelEvent(eid2);
+ BOOST_CHECK(!eid);
+ BOOST_CHECK(!eid2);
+}
+
+BOOST_AUTO_TEST_CASE(DuringCallback)
+{
+ EventId eid;
+ EventId eid2 = scheduler.scheduleEvent(20_ms, []{});
+
+ bool isCallbackInvoked = false;
+ eid = scheduler.scheduleEvent(10_ms, [this, &eid, &eid2, &isCallbackInvoked] {
+ isCallbackInvoked = true;
+
+ // eid is "expired" during callback execution
+ BOOST_CHECK(!eid);
+ BOOST_CHECK(eid == nullptr);
+ BOOST_CHECK_NE(eid, eid2);
+
+ scheduler.cancelEvent(eid2);
+ BOOST_CHECK_EQUAL(eid, eid2);
+ });
+
+ this->advanceClocks(6_ms, 2);
+ BOOST_CHECK(isCallbackInvoked);
+}
+
+BOOST_AUTO_TEST_CASE(Reset)
+{
+ bool isCallbackInvoked = false;
+ EventId eid = scheduler.scheduleEvent(10_ms, [&isCallbackInvoked] { isCallbackInvoked = true; });
+ eid.reset();
+ BOOST_CHECK(!eid);
+ BOOST_CHECK(eid == nullptr);
+
+ this->advanceClocks(6_ms, 2);
+ BOOST_CHECK(isCallbackInvoked);
+}
+
+BOOST_AUTO_TEST_CASE(ToString)
+{
+ std::string nullString = boost::lexical_cast<std::string>(shared_ptr<int>());
+
+ EventId eid;
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(eid), nullString);
+
+ eid = scheduler.scheduleEvent(10_ms, []{});
+ BOOST_TEST_MESSAGE("eid=" << eid);
+ BOOST_CHECK_NE(boost::lexical_cast<std::string>(eid), nullString);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // EventId
+
+BOOST_AUTO_TEST_SUITE(ScopedEventId)
+
+using scheduler::ScopedEventId;
+
+BOOST_AUTO_TEST_CASE(Destruct)
+{
+ int hit = 0;
+ {
+ ScopedEventId se(scheduler);
+ se = scheduler.scheduleEvent(10_ms, [&] { ++hit; });
+ } // se goes out of scope
+ this->advanceClocks(1_ms, 15);
+ BOOST_CHECK_EQUAL(hit, 0);
+}
+
+BOOST_AUTO_TEST_CASE(Assign)
+{
+ int hit1 = 0, hit2 = 0;
+ ScopedEventId se1(scheduler);
+ se1 = scheduler.scheduleEvent(10_ms, [&] { ++hit1; });
+ se1 = scheduler.scheduleEvent(10_ms, [&] { ++hit2; });
+ this->advanceClocks(1_ms, 15);
+ BOOST_CHECK_EQUAL(hit1, 0);
+ BOOST_CHECK_EQUAL(hit2, 1);
+}
+
+BOOST_AUTO_TEST_CASE(Release)
+{
+ int hit = 0;
+ {
+ ScopedEventId se(scheduler);
+ se = scheduler.scheduleEvent(10_ms, [&] { ++hit; });
+ se.release();
+ } // se goes out of scope
+ this->advanceClocks(1_ms, 15);
+ BOOST_CHECK_EQUAL(hit, 1);
+}
+
+BOOST_AUTO_TEST_CASE(Move)
+{
+ int hit = 0;
+ unique_ptr<ScopedEventId> se2;
+ {
+ ScopedEventId se(scheduler);
+ se = scheduler.scheduleEvent(10_ms, [&] { ++hit; });
+ se2 = make_unique<ScopedEventId>(std::move(se)); // move constructor
+ } // se goes out of scope
+ this->advanceClocks(1_ms, 15);
+ BOOST_CHECK_EQUAL(hit, 1);
+
+ ScopedEventId se3(scheduler);
+ {
+ ScopedEventId se(scheduler);
+ se = scheduler.scheduleEvent(10_ms, [&] { ++hit; });
+ se3 = std::move(se); // move assignment
+ } // se goes out of scope
+ this->advanceClocks(1_ms, 15);
+ BOOST_CHECK_EQUAL(hit, 2);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // ScopedEventId
+
+BOOST_AUTO_TEST_SUITE_END() // TestScheduler
+BOOST_AUTO_TEST_SUITE_END() // Util
+
+} // namespace tests
+} // namespace scheduler
+} // namespace util
+} // namespace ndn
diff --git a/tests/unit/util/segment-fetcher.t.cpp b/tests/unit/util/segment-fetcher.t.cpp
new file mode 100644
index 0000000..d52fa08
--- /dev/null
+++ b/tests/unit/util/segment-fetcher.t.cpp
@@ -0,0 +1,863 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library 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 Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "util/segment-fetcher.hpp"
+
+#include "data.hpp"
+#include "lp/nack.hpp"
+#include "util/dummy-client-face.hpp"
+
+#include "boost-test.hpp"
+#include "dummy-validator.hpp"
+#include "make-interest-data.hpp"
+#include "../identity-management-time-fixture.hpp"
+
+#include <set>
+
+namespace ndn {
+namespace util {
+namespace tests {
+
+using namespace ndn::tests;
+
+class Fixture : public IdentityManagementTimeFixture
+{
+public:
+ Fixture()
+ : face(io, m_keyChain)
+ {
+ }
+
+ static shared_ptr<Data>
+ makeDataSegment(const Name& baseName, uint64_t segment, bool isFinal)
+ {
+ const uint8_t buffer[] = "Hello, world!";
+
+ auto data = make_shared<Data>(Name(baseName).appendSegment(segment));
+ data->setContent(buffer, sizeof(buffer));
+ if (isFinal) {
+ data->setFinalBlock(data->getName()[-1]);
+ }
+
+ return signData(data);
+ }
+
+ void
+ onError(uint32_t errorCode)
+ {
+ ++nErrors;
+ lastError = errorCode;
+ }
+
+ void
+ onComplete(ConstBufferPtr data)
+ {
+ ++nCompletions;
+ dataSize = data->size();
+ dataBuf = data;
+ }
+
+ void
+ nackLastInterest(lp::NackReason nackReason)
+ {
+ const Interest& lastInterest = face.sentInterests.back();
+ lp::Nack nack = makeNack(lastInterest, nackReason);
+ face.receive(nack);
+ advanceClocks(10_ms);
+ }
+
+ void
+ connectSignals(const shared_ptr<SegmentFetcher>& fetcher)
+ {
+ fetcher->onComplete.connect(bind(&Fixture::onComplete, this, _1));
+ fetcher->onError.connect(bind(&Fixture::onError, this, _1));
+ }
+
+ void
+ onInterest(const Interest& interest)
+ {
+ if (interest.getName().get(-1).isSegment()) {
+ if (segmentsToDropOrNack.size() > 0 &&
+ interest.getName().get(-1).toSegment() == segmentsToDropOrNack.front()) {
+ segmentsToDropOrNack.pop();
+ if (sendNackInsteadOfDropping) {
+ lp::Nack nack = makeNack(interest, nackReason);
+ face.receive(nack);
+ }
+ return;
+ }
+
+ auto data = makeDataSegment("/hello/world/version0",
+ interest.getName().get(-1).toSegment(),
+ interest.getName().get(-1).toSegment() == nSegments - 1);
+ face.receive(*data);
+
+ uniqSegmentsSent.insert(interest.getName().get(-1).toSegment());
+ if (uniqSegmentsSent.size() == nSegments) {
+ io.stop();
+ }
+ }
+ else {
+ if (segmentsToDropOrNack.size() > 0 &&
+ segmentsToDropOrNack.front() == 0) {
+ segmentsToDropOrNack.pop();
+ if (sendNackInsteadOfDropping) {
+ lp::Nack nack = makeNack(interest, nackReason);
+ face.receive(nack);
+ }
+ return;
+ }
+
+ auto data = makeDataSegment("/hello/world/version0", defaultSegmentToSend, nSegments == 1);
+ face.receive(*data);
+ uniqSegmentsSent.insert(defaultSegmentToSend);
+ }
+ }
+
+public:
+ DummyClientFace face;
+ std::set<uint64_t> uniqSegmentsSent;
+
+ int nErrors = 0;
+ uint32_t lastError = 0;
+ int nCompletions = 0;
+ size_t dataSize = 0;
+ ConstBufferPtr dataBuf;
+
+ // number of segments in fetched object
+ uint64_t nSegments = 0;
+ std::queue<uint64_t> segmentsToDropOrNack;
+ bool sendNackInsteadOfDropping = false;
+ lp::NackReason nackReason = lp::NackReason::NONE;
+ // segment that is sent in response to an Interest w/o a segment component in its name
+ uint64_t defaultSegmentToSend = 0;
+};
+
+BOOST_AUTO_TEST_SUITE(Util)
+BOOST_FIXTURE_TEST_SUITE(TestSegmentFetcher, Fixture)
+
+BOOST_AUTO_TEST_CASE(InvalidOptions)
+{
+ SegmentFetcher::Options options;
+ options.mdCoef = 1.5;
+ DummyValidator acceptValidator;
+ BOOST_CHECK_THROW(SegmentFetcher::start(face, Interest("/hello/world"), acceptValidator, options),
+ std::invalid_argument);
+}
+
+BOOST_AUTO_TEST_CASE(ExceedMaxTimeout)
+{
+ DummyValidator acceptValidator;
+ size_t nAfterSegmentTimedOut = 0;
+ SegmentFetcher::Options options;
+ options.maxTimeout = 100_ms;
+ shared_ptr<SegmentFetcher> fetcher = SegmentFetcher::start(face, Interest("/hello/world"),
+ acceptValidator, options);
+ connectSignals(fetcher);
+ fetcher->afterSegmentTimedOut.connect(bind([&nAfterSegmentTimedOut] { ++nAfterSegmentTimedOut; }));
+
+ advanceClocks(1_ms);
+ BOOST_REQUIRE_EQUAL(face.sentInterests.size(), 1);
+
+ const Interest& interest = face.sentInterests[0];
+ BOOST_CHECK_EQUAL(interest.getName(), "/hello/world");
+ BOOST_CHECK_EQUAL(interest.getMustBeFresh(), true);
+ BOOST_CHECK_EQUAL(interest.getCanBePrefix(), true);
+
+ advanceClocks(98_ms);
+ BOOST_CHECK_EQUAL(nErrors, 0);
+ BOOST_CHECK_EQUAL(nCompletions, 0);
+ BOOST_CHECK_EQUAL(face.sentData.size(), 0);
+ BOOST_CHECK_EQUAL(nAfterSegmentTimedOut, 0);
+
+ advanceClocks(1_ms, 2);
+ BOOST_CHECK_EQUAL(nErrors, 1);
+ BOOST_CHECK_EQUAL(lastError, static_cast<uint32_t>(SegmentFetcher::INTEREST_TIMEOUT));
+ BOOST_REQUIRE_EQUAL(face.sentInterests.size(), 1);
+ BOOST_CHECK_EQUAL(face.sentData.size(), 0);
+ BOOST_CHECK_EQUAL(nAfterSegmentTimedOut, 1);
+}
+
+BOOST_AUTO_TEST_CASE(BasicSingleSegment)
+{
+ DummyValidator acceptValidator;
+ size_t nAfterSegmentReceived = 0;
+ size_t nAfterSegmentValidated = 0;
+ size_t nAfterSegmentNacked = 0;
+ size_t nAfterSegmentTimedOut = 0;
+ shared_ptr<SegmentFetcher> fetcher = SegmentFetcher::start(face, Interest("/hello/world"),
+ acceptValidator);
+ connectSignals(fetcher);
+ fetcher->afterSegmentReceived.connect(bind([&nAfterSegmentReceived] { ++nAfterSegmentReceived; }));
+ fetcher->afterSegmentValidated.connect(bind([&nAfterSegmentValidated] { ++nAfterSegmentValidated; }));
+ fetcher->afterSegmentNacked.connect(bind([&nAfterSegmentNacked] { ++nAfterSegmentNacked; }));
+ fetcher->afterSegmentTimedOut.connect(bind([&nAfterSegmentTimedOut] { ++nAfterSegmentTimedOut; }));
+
+ advanceClocks(10_ms);
+
+ face.receive(*makeDataSegment("/hello/world/version0", 0, true));
+
+ advanceClocks(10_ms);
+
+ BOOST_CHECK_EQUAL(nErrors, 0);
+ BOOST_CHECK_EQUAL(nCompletions, 1);
+ BOOST_CHECK_EQUAL(nAfterSegmentReceived, 1);
+ BOOST_CHECK_EQUAL(nAfterSegmentValidated, 1);
+ BOOST_CHECK_EQUAL(nAfterSegmentNacked, 0);
+ BOOST_CHECK_EQUAL(nAfterSegmentTimedOut, 0);
+}
+
+BOOST_AUTO_TEST_CASE(ConstantCwnd)
+{
+ SegmentFetcher::Options options;
+ options.useConstantCwnd = true;
+ DummyValidator acceptValidator;
+ size_t nAfterSegmentReceived = 0;
+ size_t nAfterSegmentValidated = 0;
+ size_t nAfterSegmentNacked = 0;
+ size_t nAfterSegmentTimedOut = 0;
+ shared_ptr<SegmentFetcher> fetcher = SegmentFetcher::start(face, Interest("/hello/world"),
+ acceptValidator, options);
+ connectSignals(fetcher);
+ fetcher->afterSegmentReceived.connect(bind([&nAfterSegmentReceived] { ++nAfterSegmentReceived; }));
+ fetcher->afterSegmentValidated.connect(bind([&nAfterSegmentValidated] { ++nAfterSegmentValidated; }));
+ fetcher->afterSegmentNacked.connect(bind([&nAfterSegmentNacked] { ++nAfterSegmentNacked; }));
+ fetcher->afterSegmentTimedOut.connect(bind([&nAfterSegmentTimedOut] { ++nAfterSegmentTimedOut; }));
+
+ advanceClocks(10_ms);
+
+ BOOST_CHECK_EQUAL(fetcher->m_cwnd, 1.0);
+ BOOST_CHECK_EQUAL(fetcher->m_nSegmentsInFlight, 1);
+
+ face.receive(*makeDataSegment("/hello/world/version0", 0, false));
+
+ advanceClocks(10_ms);
+
+ BOOST_CHECK_EQUAL(fetcher->m_cwnd, 1.0);
+ BOOST_CHECK_EQUAL(fetcher->m_nSegmentsInFlight, 1);
+ BOOST_REQUIRE_EQUAL(face.sentInterests.size(), 2);
+ BOOST_CHECK_EQUAL(face.sentInterests.back().getName().get(-1).toSegment(), 1);
+ face.receive(*makeDataSegment("/hello/world/version0", 1, false));
+
+ advanceClocks(10_ms);
+
+ BOOST_CHECK_EQUAL(fetcher->m_cwnd, 1.0);
+ BOOST_CHECK_EQUAL(fetcher->m_nSegmentsInFlight, 1);
+ BOOST_CHECK_EQUAL(face.sentInterests.size(), 3);
+ BOOST_CHECK_EQUAL(face.sentInterests.back().getName().get(-1).toSegment(), 2);
+ face.receive(*makeDataSegment("/hello/world/version0", 2, false));
+
+ advanceClocks(10_ms);
+
+ BOOST_CHECK_EQUAL(fetcher->m_cwnd, 1.0);
+ BOOST_CHECK_EQUAL(fetcher->m_nSegmentsInFlight, 1);
+ BOOST_CHECK_EQUAL(face.sentInterests.size(), 4);
+ BOOST_CHECK_EQUAL(face.sentInterests.back().getName().get(-1).toSegment(), 3);
+ face.receive(*makeDataSegment("/hello/world/version0", 3, false));
+
+ advanceClocks(10_ms);
+
+ BOOST_CHECK_EQUAL(fetcher->m_cwnd, 1.0);
+ BOOST_CHECK_EQUAL(fetcher->m_nSegmentsInFlight, 1);
+ BOOST_CHECK_EQUAL(face.sentInterests.size(), 5);
+ BOOST_CHECK_EQUAL(face.sentInterests.back().getName().get(-1).toSegment(), 4);
+ nackLastInterest(lp::NackReason::CONGESTION);
+
+ advanceClocks(10_ms);
+
+ BOOST_CHECK_EQUAL(fetcher->m_cwnd, 1.0);
+ BOOST_CHECK_EQUAL(fetcher->m_nSegmentsInFlight, 1);
+ BOOST_CHECK_EQUAL(face.sentInterests.size(), 6);
+ BOOST_CHECK_EQUAL(face.sentInterests.back().getName().get(-1).toSegment(), 4);
+ face.receive(*makeDataSegment("/hello/world/version0", 4, true));
+
+ advanceClocks(10_ms);
+
+ BOOST_CHECK_EQUAL(nErrors, 0);
+ BOOST_CHECK_EQUAL(nCompletions, 1);
+ BOOST_CHECK_EQUAL(nAfterSegmentReceived, 5);
+ BOOST_CHECK_EQUAL(nAfterSegmentValidated, 5);
+ BOOST_CHECK_EQUAL(nAfterSegmentNacked, 1);
+ BOOST_CHECK_EQUAL(nAfterSegmentTimedOut, 0);
+ BOOST_CHECK_EQUAL(fetcher->m_cwnd, 1.0);
+}
+
+BOOST_AUTO_TEST_CASE(BasicMultipleSegments)
+{
+ DummyValidator acceptValidator;
+ size_t nAfterSegmentReceived = 0;
+ size_t nAfterSegmentValidated = 0;
+ size_t nAfterSegmentNacked = 0;
+ size_t nAfterSegmentTimedOut = 0;
+ nSegments = 401;
+ sendNackInsteadOfDropping = false;
+ face.onSendInterest.connect(bind(&Fixture::onInterest, this, _1));
+
+ shared_ptr<SegmentFetcher> fetcher = SegmentFetcher::start(face, Interest("/hello/world"),
+ acceptValidator);
+ connectSignals(fetcher);
+ fetcher->afterSegmentReceived.connect(bind([&nAfterSegmentReceived] { ++nAfterSegmentReceived; }));
+ fetcher->afterSegmentValidated.connect(bind([&nAfterSegmentValidated] { ++nAfterSegmentValidated; }));
+ fetcher->afterSegmentNacked.connect(bind([&nAfterSegmentNacked] { ++nAfterSegmentNacked; }));
+ fetcher->afterSegmentTimedOut.connect(bind([&nAfterSegmentTimedOut] { ++nAfterSegmentTimedOut; }));
+
+ face.processEvents(1_s);
+
+ BOOST_CHECK_EQUAL(nErrors, 0);
+ BOOST_CHECK_EQUAL(nCompletions, 1);
+ BOOST_CHECK_EQUAL(dataSize, 14 * 401);
+ BOOST_CHECK_EQUAL(nAfterSegmentReceived, 401);
+ BOOST_CHECK_EQUAL(nAfterSegmentValidated, 401);
+ BOOST_CHECK_EQUAL(nAfterSegmentNacked, 0);
+ BOOST_CHECK_EQUAL(nAfterSegmentTimedOut, 0);
+}
+
+BOOST_AUTO_TEST_CASE(FirstSegmentNotZero)
+{
+ DummyValidator acceptValidator;
+ size_t nAfterSegmentReceived = 0;
+ size_t nAfterSegmentValidated = 0;
+ size_t nAfterSegmentNacked = 0;
+ size_t nAfterSegmentTimedOut = 0;
+ nSegments = 401;
+ sendNackInsteadOfDropping = false;
+ defaultSegmentToSend = 47;
+ face.onSendInterest.connect(bind(&Fixture::onInterest, this, _1));
+
+ shared_ptr<SegmentFetcher> fetcher = SegmentFetcher::start(face, Interest("/hello/world"),
+ acceptValidator);
+ connectSignals(fetcher);
+ fetcher->afterSegmentReceived.connect(bind([&nAfterSegmentReceived] { ++nAfterSegmentReceived; }));
+ fetcher->afterSegmentValidated.connect(bind([&nAfterSegmentValidated] { ++nAfterSegmentValidated; }));
+ fetcher->afterSegmentNacked.connect(bind([&nAfterSegmentNacked] { ++nAfterSegmentNacked; }));
+ fetcher->afterSegmentTimedOut.connect(bind([&nAfterSegmentTimedOut] { ++nAfterSegmentTimedOut; }));
+
+ face.processEvents(1_s);
+
+ BOOST_CHECK_EQUAL(nErrors, 0);
+ BOOST_CHECK_EQUAL(nCompletions, 1);
+ BOOST_CHECK_EQUAL(dataSize, 14 * 401);
+ BOOST_CHECK_EQUAL(nAfterSegmentReceived, 401);
+ BOOST_CHECK_EQUAL(nAfterSegmentValidated, 401);
+ BOOST_CHECK_EQUAL(nAfterSegmentNacked, 0);
+ BOOST_CHECK_EQUAL(nAfterSegmentTimedOut, 0);
+}
+
+BOOST_AUTO_TEST_CASE(WindowSize)
+{
+ DummyValidator acceptValidator;
+ size_t nAfterSegmentReceived = 0;
+ size_t nAfterSegmentValidated = 0;
+ shared_ptr<SegmentFetcher> fetcher = SegmentFetcher::start(face, Interest("/hello/world"),
+ acceptValidator);
+ connectSignals(fetcher);
+ fetcher->afterSegmentReceived.connect(bind([&nAfterSegmentReceived] { ++nAfterSegmentReceived; }));
+ fetcher->afterSegmentValidated.connect(bind([&nAfterSegmentValidated] { ++nAfterSegmentValidated; }));
+
+ advanceClocks(10_ms); // T+10ms
+
+ BOOST_CHECK_EQUAL(fetcher->m_timeLastSegmentReceived, time::steady_clock::now() - 10_ms);
+ BOOST_CHECK_EQUAL(fetcher->m_retxQueue.size(), 0);
+ BOOST_CHECK_EQUAL(fetcher->m_nextSegmentNum, 0);
+ BOOST_CHECK_EQUAL(fetcher->m_cwnd, 1.0);
+ BOOST_CHECK_EQUAL(fetcher->m_ssthresh, std::numeric_limits<double>::max());
+ BOOST_CHECK_EQUAL(fetcher->m_nSegmentsInFlight, 1);
+ BOOST_CHECK_EQUAL(fetcher->m_nSegments, 0);
+ BOOST_CHECK_EQUAL(fetcher->m_nBytesReceived, 0);
+ BOOST_CHECK_EQUAL(fetcher->m_highInterest, 0);
+ BOOST_CHECK_EQUAL(fetcher->m_highData, 0);
+ BOOST_CHECK_EQUAL(fetcher->m_recPoint, 0);
+ BOOST_CHECK_EQUAL(fetcher->m_nReceived, 0);
+ BOOST_CHECK_EQUAL(fetcher->m_receivedSegments.size(), 0);
+ BOOST_CHECK_EQUAL(fetcher->m_pendingSegments.size(), 1);
+ BOOST_CHECK_EQUAL(face.sentInterests.size(), 1);
+
+ double oldCwnd = fetcher->m_cwnd;
+ double oldSsthresh = fetcher->m_ssthresh;
+ uint64_t oldNextSegmentNum = fetcher->m_nextSegmentNum;
+
+ face.receive(*makeDataSegment("/hello/world/version0", 0, false));
+
+ advanceClocks(10_ms); //T+20ms
+
+ BOOST_CHECK_EQUAL(fetcher->m_timeLastSegmentReceived, time::steady_clock::now() - 10_ms);
+ BOOST_CHECK_EQUAL(fetcher->m_retxQueue.size(), 0);
+ BOOST_CHECK_EQUAL(fetcher->m_versionedDataName, "/hello/world/version0");
+ // +2 below because m_nextSegmentNum will be incremented in the receive callback if segment 0 is
+ // the first received
+ BOOST_CHECK_EQUAL(fetcher->m_nextSegmentNum, oldNextSegmentNum + fetcher->m_options.aiStep + 2);
+ BOOST_CHECK_EQUAL(fetcher->m_cwnd, oldCwnd + fetcher->m_options.aiStep);
+ BOOST_CHECK_EQUAL(fetcher->m_ssthresh, oldSsthresh);
+ BOOST_CHECK_EQUAL(fetcher->m_nSegmentsInFlight, oldCwnd + fetcher->m_options.aiStep);
+ BOOST_CHECK_EQUAL(fetcher->m_nSegments, 0);
+ BOOST_CHECK_EQUAL(fetcher->m_nBytesReceived, 14);
+ BOOST_CHECK_EQUAL(fetcher->m_highInterest, fetcher->m_nextSegmentNum - 1);
+ BOOST_CHECK_EQUAL(fetcher->m_highData, 0);
+ BOOST_CHECK_EQUAL(fetcher->m_recPoint, 0);
+ BOOST_CHECK_EQUAL(fetcher->m_receivedSegments.size(), 1);
+ BOOST_CHECK_EQUAL(fetcher->m_pendingSegments.size(), fetcher->m_cwnd);
+ BOOST_CHECK_EQUAL(face.sentInterests.size(), 1 + fetcher->m_cwnd);
+
+ oldCwnd = fetcher->m_cwnd;
+ oldNextSegmentNum = fetcher->m_nextSegmentNum;
+
+ face.receive(*makeDataSegment("/hello/world/version0", 2, false));
+
+ advanceClocks(10_ms); //T+30ms
+
+ BOOST_CHECK_EQUAL(fetcher->m_timeLastSegmentReceived, time::steady_clock::now() - 10_ms);
+ BOOST_CHECK_EQUAL(fetcher->m_retxQueue.size(), 0);
+ BOOST_CHECK_EQUAL(fetcher->m_versionedDataName, "/hello/world/version0");
+ BOOST_CHECK_EQUAL(fetcher->m_nextSegmentNum, oldNextSegmentNum + fetcher->m_options.aiStep + 1);
+ BOOST_CHECK_EQUAL(fetcher->m_cwnd, oldCwnd + fetcher->m_options.aiStep);
+ BOOST_CHECK_EQUAL(fetcher->m_ssthresh, oldSsthresh);
+ BOOST_CHECK_EQUAL(fetcher->m_nSegmentsInFlight, fetcher->m_cwnd);
+ BOOST_CHECK_EQUAL(fetcher->m_nSegments, 0);
+ BOOST_CHECK_EQUAL(fetcher->m_nBytesReceived, 28);
+ BOOST_CHECK_EQUAL(fetcher->m_highInterest, fetcher->m_nextSegmentNum - 1);
+ BOOST_CHECK_EQUAL(fetcher->m_highData, 2);
+ BOOST_CHECK_EQUAL(fetcher->m_recPoint, 0);
+ BOOST_CHECK_EQUAL(fetcher->m_receivedSegments.size(), 2);
+ BOOST_CHECK_EQUAL(fetcher->m_pendingSegments.size(), fetcher->m_cwnd);
+ BOOST_CHECK_EQUAL(face.sentInterests.size(), 2 + fetcher->m_cwnd);
+
+ oldCwnd = fetcher->m_cwnd;
+ oldNextSegmentNum = fetcher->m_nextSegmentNum;
+
+ face.receive(*makeDataSegment("/hello/world/version0", 1, false));
+
+ advanceClocks(10_ms); //T+40ms
+
+ BOOST_CHECK_EQUAL(fetcher->m_timeLastSegmentReceived, time::steady_clock::now() - 10_ms);
+ BOOST_CHECK_EQUAL(fetcher->m_retxQueue.size(), 0);
+ BOOST_CHECK_EQUAL(fetcher->m_versionedDataName, "/hello/world/version0");
+ BOOST_CHECK_EQUAL(fetcher->m_nextSegmentNum, oldNextSegmentNum + fetcher->m_options.aiStep + 1);
+ BOOST_CHECK_EQUAL(fetcher->m_cwnd, oldCwnd + fetcher->m_options.aiStep);
+ BOOST_CHECK_EQUAL(fetcher->m_ssthresh, oldSsthresh);
+ BOOST_CHECK_EQUAL(fetcher->m_nSegmentsInFlight, fetcher->m_cwnd);
+ BOOST_CHECK_EQUAL(fetcher->m_nSegments, 0);
+ BOOST_CHECK_EQUAL(fetcher->m_nBytesReceived, 42);
+ BOOST_CHECK_EQUAL(fetcher->m_highInterest, fetcher->m_nextSegmentNum - 1);
+ BOOST_CHECK_EQUAL(fetcher->m_highData, 2);
+ BOOST_CHECK_EQUAL(fetcher->m_recPoint, 0);
+ BOOST_CHECK_EQUAL(fetcher->m_receivedSegments.size(), 3);
+ BOOST_CHECK_EQUAL(fetcher->m_pendingSegments.size(), fetcher->m_cwnd);
+ BOOST_CHECK_EQUAL(face.sentInterests.size(), 3 + fetcher->m_cwnd);
+
+ oldCwnd = fetcher->m_cwnd;
+ oldSsthresh = fetcher->m_ssthresh;
+ oldNextSegmentNum = fetcher->m_nextSegmentNum;
+ size_t oldSentInterestsSize = face.sentInterests.size();
+
+ nackLastInterest(lp::NackReason::CONGESTION); //T+50ms
+
+ BOOST_CHECK_EQUAL(fetcher->m_timeLastSegmentReceived, time::steady_clock::now() - 20_ms);
+ BOOST_CHECK_EQUAL(fetcher->m_retxQueue.size(), 1);
+ BOOST_CHECK_EQUAL(fetcher->m_versionedDataName, "/hello/world/version0");
+ BOOST_CHECK_EQUAL(fetcher->m_nextSegmentNum, oldNextSegmentNum);
+ BOOST_CHECK_EQUAL(fetcher->m_cwnd, oldCwnd / 2.0);
+ BOOST_CHECK_EQUAL(fetcher->m_ssthresh, oldCwnd / 2.0);
+ BOOST_CHECK_EQUAL(fetcher->m_nSegmentsInFlight, oldCwnd - 1);
+ BOOST_CHECK_EQUAL(fetcher->m_nSegments, 0);
+ BOOST_CHECK_EQUAL(fetcher->m_nBytesReceived, 42);
+ BOOST_CHECK_EQUAL(fetcher->m_highInterest, fetcher->m_nextSegmentNum - 1);
+ BOOST_CHECK_EQUAL(fetcher->m_highData, 2);
+ BOOST_CHECK_EQUAL(fetcher->m_recPoint, fetcher->m_nextSegmentNum - 1);
+ BOOST_CHECK_EQUAL(fetcher->m_receivedSegments.size(), 3);
+ // The Nacked segment will remain in pendingSegments, so the size of the structure doesn't change
+ BOOST_CHECK_EQUAL(fetcher->m_pendingSegments.size(), oldCwnd);
+ BOOST_CHECK_EQUAL(face.sentInterests.size(), oldSentInterestsSize);
+
+ advanceClocks(10_ms); //T+60ms
+
+ BOOST_CHECK_EQUAL(fetcher->m_timeLastSegmentReceived, time::steady_clock::now() - 30_ms);
+ BOOST_CHECK_EQUAL(fetcher->m_retxQueue.size(), 1);
+ BOOST_CHECK_EQUAL(fetcher->m_versionedDataName, "/hello/world/version0");
+ BOOST_CHECK_EQUAL(fetcher->m_nextSegmentNum, oldNextSegmentNum);
+ BOOST_CHECK_EQUAL(fetcher->m_cwnd, oldCwnd / 2.0);
+ BOOST_CHECK_EQUAL(fetcher->m_ssthresh, oldCwnd / 2.0);
+ BOOST_CHECK_EQUAL(fetcher->m_nSegmentsInFlight, oldCwnd - 1);
+ BOOST_CHECK_EQUAL(fetcher->m_nSegments, 0);
+ BOOST_CHECK_EQUAL(fetcher->m_nBytesReceived, 42);
+ BOOST_CHECK_EQUAL(fetcher->m_highInterest, fetcher->m_nextSegmentNum - 1);
+ BOOST_CHECK_EQUAL(fetcher->m_highData, 2);
+ BOOST_CHECK_EQUAL(fetcher->m_recPoint, fetcher->m_nextSegmentNum - 1);
+ BOOST_CHECK_EQUAL(fetcher->m_receivedSegments.size(), 3);
+ BOOST_CHECK_EQUAL(fetcher->m_pendingSegments.size(), oldCwnd);
+ BOOST_CHECK_EQUAL(face.sentInterests.size(), oldSentInterestsSize);
+
+ // Properly end test case
+ lp::Nack nack = makeNack(face.sentInterests[face.sentInterests.size() - 2], lp::NackReason::NO_ROUTE);
+ face.receive(nack);
+ advanceClocks(10_ms); //T+70ms
+
+ BOOST_CHECK_EQUAL(nErrors, 1);
+ BOOST_CHECK_EQUAL(lastError, static_cast<uint32_t>(SegmentFetcher::NACK_ERROR));
+ BOOST_CHECK_EQUAL(nCompletions, 0);
+}
+
+BOOST_AUTO_TEST_CASE(MissingSegmentNum)
+{
+ DummyValidator acceptValidator;
+ shared_ptr<SegmentFetcher> fetcher = SegmentFetcher::start(face, Interest("/hello/world"),
+ acceptValidator);
+ connectSignals(fetcher);
+
+ advanceClocks(10_ms);
+
+ const uint8_t buffer[] = "Hello, world!";
+ auto data = makeData("/hello/world/version0/no-segment");
+ data->setContent(buffer, sizeof(buffer));
+
+ face.receive(*data);
+ advanceClocks(10_ms);
+
+ BOOST_CHECK_EQUAL(nErrors, 1);
+ BOOST_CHECK_EQUAL(lastError, static_cast<uint32_t>(SegmentFetcher::DATA_HAS_NO_SEGMENT));
+ BOOST_CHECK_EQUAL(nCompletions, 0);
+}
+
+BOOST_AUTO_TEST_CASE(MoreSegmentsThanNSegments)
+{
+ DummyValidator acceptValidator;
+ size_t nAfterSegmentReceived = 0;
+ size_t nAfterSegmentValidated = 0;
+ size_t nAfterSegmentNacked = 0;
+ size_t nAfterSegmentTimedOut = 0;
+ shared_ptr<SegmentFetcher> fetcher = SegmentFetcher::start(face, Interest("/hello/world"),
+ acceptValidator);
+ connectSignals(fetcher);
+ fetcher->afterSegmentReceived.connect(bind([&nAfterSegmentReceived] { ++nAfterSegmentReceived; }));
+ fetcher->afterSegmentValidated.connect(bind([&nAfterSegmentValidated] { ++nAfterSegmentValidated; }));
+ fetcher->afterSegmentNacked.connect(bind([&nAfterSegmentNacked] { ++nAfterSegmentNacked; }));
+ fetcher->afterSegmentTimedOut.connect(bind([&nAfterSegmentTimedOut] { ++nAfterSegmentTimedOut; }));
+
+ advanceClocks(10_ms);
+
+ face.receive(*makeDataSegment("/hello/world/version0", 0, false));
+ advanceClocks(10_ms);
+
+ BOOST_CHECK_EQUAL(nErrors, 0);
+ BOOST_CHECK_EQUAL(nCompletions, 0);
+
+ face.receive(*makeDataSegment("/hello/world/version0", 1, false));
+ advanceClocks(10_ms);
+
+ BOOST_CHECK_EQUAL(nErrors, 0);
+ BOOST_CHECK_EQUAL(nCompletions, 0);
+
+ face.receive(*makeDataSegment("/hello/world/version0", 2, false));
+ advanceClocks(10_ms);
+
+ BOOST_CHECK_EQUAL(nErrors, 0);
+ BOOST_CHECK_EQUAL(nCompletions, 0);
+
+ face.receive(*makeDataSegment("/hello/world/version0", 3, false));
+ advanceClocks(10_ms);
+
+ BOOST_CHECK_EQUAL(nErrors, 0);
+ BOOST_CHECK_EQUAL(nCompletions, 0);
+
+ auto data4 = makeDataSegment("/hello/world/version0", 4, false);
+ data4->setFinalBlock(name::Component::fromSegment(2));
+ face.receive(*data4);
+ advanceClocks(10_ms);
+
+ BOOST_CHECK_EQUAL(nErrors, 0);
+ BOOST_CHECK_EQUAL(nCompletions, 1);
+ BOOST_CHECK_EQUAL(dataSize, 14 * 3);
+ BOOST_CHECK_EQUAL(nAfterSegmentReceived, 5);
+ BOOST_CHECK_EQUAL(nAfterSegmentValidated, 5);
+ BOOST_CHECK_EQUAL(nAfterSegmentNacked, 0);
+ BOOST_CHECK_EQUAL(nAfterSegmentTimedOut, 0);
+}
+
+BOOST_AUTO_TEST_CASE(DuplicateNack)
+{
+ DummyValidator acceptValidator;
+ size_t nAfterSegmentReceived = 0;
+ size_t nAfterSegmentValidated = 0;
+ size_t nAfterSegmentNacked = 0;
+ size_t nAfterSegmentTimedOut = 0;
+ nSegments = 401;
+ segmentsToDropOrNack.push(0);
+ segmentsToDropOrNack.push(200);
+ sendNackInsteadOfDropping = true;
+ nackReason = lp::NackReason::DUPLICATE;
+ face.onSendInterest.connect(bind(&Fixture::onInterest, this, _1));
+
+ shared_ptr<SegmentFetcher> fetcher = SegmentFetcher::start(face, Interest("/hello/world"),
+ acceptValidator);
+ connectSignals(fetcher);
+ fetcher->afterSegmentReceived.connect(bind([&nAfterSegmentReceived] { ++nAfterSegmentReceived; }));
+ fetcher->afterSegmentValidated.connect(bind([&nAfterSegmentValidated] { ++nAfterSegmentValidated; }));
+ fetcher->afterSegmentNacked.connect(bind([&nAfterSegmentNacked] { ++nAfterSegmentNacked; }));
+ fetcher->afterSegmentTimedOut.connect(bind([&nAfterSegmentTimedOut] { ++nAfterSegmentTimedOut; }));
+
+ face.processEvents(1_s);
+
+ BOOST_CHECK_EQUAL(nErrors, 0);
+ BOOST_CHECK_EQUAL(nCompletions, 1);
+ BOOST_CHECK_EQUAL(dataSize, 14 * 401);
+ BOOST_CHECK_EQUAL(nAfterSegmentReceived, 401);
+ BOOST_CHECK_EQUAL(nAfterSegmentValidated, 401);
+ BOOST_CHECK_EQUAL(nAfterSegmentNacked, 2);
+ BOOST_CHECK_EQUAL(nAfterSegmentTimedOut, 0);
+}
+
+BOOST_AUTO_TEST_CASE(CongestionNack)
+{
+ DummyValidator acceptValidator;
+ size_t nAfterSegmentReceived = 0;
+ size_t nAfterSegmentValidated = 0;
+ size_t nAfterSegmentNacked = 0;
+ size_t nAfterSegmentTimedOut = 0;
+ nSegments = 401;
+ segmentsToDropOrNack.push(0);
+ segmentsToDropOrNack.push(200);
+ sendNackInsteadOfDropping = true;
+ nackReason = lp::NackReason::CONGESTION;
+ face.onSendInterest.connect(bind(&Fixture::onInterest, this, _1));
+
+ shared_ptr<SegmentFetcher> fetcher = SegmentFetcher::start(face, Interest("/hello/world"),
+ acceptValidator);
+ connectSignals(fetcher);
+ fetcher->afterSegmentReceived.connect(bind([&nAfterSegmentReceived] { ++nAfterSegmentReceived; }));
+ fetcher->afterSegmentValidated.connect(bind([&nAfterSegmentValidated] { ++nAfterSegmentValidated; }));
+ fetcher->afterSegmentNacked.connect(bind([&nAfterSegmentNacked] { ++nAfterSegmentNacked; }));
+ fetcher->afterSegmentTimedOut.connect(bind([&nAfterSegmentTimedOut] { ++nAfterSegmentTimedOut; }));
+
+ face.processEvents(1_s);
+
+ BOOST_CHECK_EQUAL(nErrors, 0);
+ BOOST_CHECK_EQUAL(nCompletions, 1);
+ BOOST_CHECK_EQUAL(dataSize, 14 * 401);
+ BOOST_CHECK_EQUAL(nAfterSegmentReceived, 401);
+ BOOST_CHECK_EQUAL(nAfterSegmentValidated, 401);
+ BOOST_CHECK_EQUAL(nAfterSegmentNacked, 2);
+ BOOST_CHECK_EQUAL(nAfterSegmentTimedOut, 0);
+}
+
+BOOST_AUTO_TEST_CASE(OtherNackReason)
+{
+ DummyValidator acceptValidator;
+ size_t nAfterSegmentReceived = 0;
+ size_t nAfterSegmentValidated = 0;
+ size_t nAfterSegmentNacked = 0;
+ size_t nAfterSegmentTimedOut = 0;
+ segmentsToDropOrNack.push(0);
+ sendNackInsteadOfDropping = true;
+ nackReason = lp::NackReason::NO_ROUTE;
+ face.onSendInterest.connect(bind(&Fixture::onInterest, this, _1));
+
+ shared_ptr<SegmentFetcher> fetcher = SegmentFetcher::start(face, Interest("/hello/world"),
+ acceptValidator);
+ connectSignals(fetcher);
+ fetcher->afterSegmentReceived.connect(bind([&nAfterSegmentReceived] { ++nAfterSegmentReceived; }));
+ fetcher->afterSegmentValidated.connect(bind([&nAfterSegmentValidated] { ++nAfterSegmentValidated; }));
+ fetcher->afterSegmentNacked.connect(bind([&nAfterSegmentNacked] { ++nAfterSegmentNacked; }));
+ fetcher->afterSegmentTimedOut.connect(bind([&nAfterSegmentTimedOut] { ++nAfterSegmentTimedOut; }));
+
+ face.processEvents(1_s);
+
+ BOOST_CHECK_EQUAL(nErrors, 1);
+ BOOST_CHECK_EQUAL(nCompletions, 0);
+ BOOST_CHECK_EQUAL(nAfterSegmentReceived, 0);
+ BOOST_CHECK_EQUAL(nAfterSegmentValidated, 0);
+ BOOST_CHECK_EQUAL(nAfterSegmentNacked, 1);
+ BOOST_CHECK_EQUAL(nAfterSegmentTimedOut, 0);
+ BOOST_CHECK_EQUAL(face.sentInterests.size(), 1);
+}
+
+BOOST_AUTO_TEST_CASE(ValidationFailure)
+{
+ DummyValidator validator;
+ validator.getPolicy().setResultCallback([] (const Name& name) {
+ return name.at(-1).toSegment() % 2 == 0;
+ });
+ shared_ptr<SegmentFetcher> fetcher = SegmentFetcher::start(face, Interest("/hello/world"),
+ validator);
+ connectSignals(fetcher);
+
+ auto data1 = makeDataSegment("/hello/world", 0, false);
+ auto data2 = makeDataSegment("/hello/world", 1, true);
+
+ size_t nRecvSegments = 0;
+ fetcher->afterSegmentReceived.connect([&nRecvSegments] (const Data& receivedSegment) {
+ ++nRecvSegments;
+ });
+
+ size_t nValidatedSegments = 0;
+ fetcher->afterSegmentValidated.connect([&nValidatedSegments] (const Data& validatedSegment) {
+ ++nValidatedSegments;
+ });
+
+ advanceClocks(10_ms, 10);
+
+ BOOST_CHECK_EQUAL(fetcher->m_timeLastSegmentReceived, time::steady_clock::now() - 100_ms);
+
+ face.receive(*data1);
+
+ advanceClocks(10_ms, 10);
+
+ BOOST_CHECK_EQUAL(fetcher->m_timeLastSegmentReceived, time::steady_clock::now() - 100_ms);
+
+ face.receive(*data2);
+
+ advanceClocks(10_ms, 10);
+
+ BOOST_CHECK_EQUAL(fetcher->m_timeLastSegmentReceived, time::steady_clock::now() - 200_ms);
+ BOOST_CHECK_EQUAL(nRecvSegments, 2);
+ BOOST_CHECK_EQUAL(nValidatedSegments, 1);
+ BOOST_CHECK_EQUAL(nErrors, 1);
+}
+
+BOOST_AUTO_TEST_CASE(Stop)
+{
+ DummyValidator acceptValidator;
+
+ auto fetcher = SegmentFetcher::start(face, Interest("/hello/world"), acceptValidator);
+ connectSignals(fetcher);
+ BOOST_CHECK_EQUAL(fetcher.use_count(), 2);
+
+ fetcher->stop();
+ advanceClocks(10_ms);
+ BOOST_CHECK_EQUAL(fetcher.use_count(), 1);
+
+ face.receive(*makeDataSegment("/hello/world/version0", 0, true));
+ advanceClocks(10_ms);
+ BOOST_CHECK_EQUAL(nErrors, 0);
+ BOOST_CHECK_EQUAL(nCompletions, 0);
+
+ fetcher.reset();
+ BOOST_CHECK_EQUAL(fetcher.use_count(), 0);
+
+ // Make sure we can re-assign w/o any complains from ASan
+ fetcher = SegmentFetcher::start(face, Interest("/hello/world"), acceptValidator);
+ connectSignals(fetcher);
+ BOOST_CHECK_EQUAL(fetcher.use_count(), 2);
+
+ advanceClocks(10_ms);
+
+ face.receive(*makeDataSegment("/hello/world/version0", 0, true));
+
+ advanceClocks(10_ms);
+ BOOST_CHECK_EQUAL(nErrors, 0);
+ BOOST_CHECK_EQUAL(nCompletions, 1);
+ BOOST_CHECK_EQUAL(fetcher.use_count(), 1);
+
+ // Stop from callback
+ bool fetcherStopped = false;
+
+ fetcher = SegmentFetcher::start(face, Interest("/hello/world"), acceptValidator);
+ fetcher->afterSegmentReceived.connect([&fetcher, &fetcherStopped] (const Data& data) {
+ fetcherStopped = true;
+ fetcher->stop();
+ });
+ BOOST_CHECK_EQUAL(fetcher.use_count(), 2);
+
+ advanceClocks(10_ms);
+
+ face.receive(*makeDataSegment("/hello/world/version0", 0, true));
+
+ advanceClocks(10_ms);
+ BOOST_CHECK(fetcherStopped);
+ BOOST_CHECK_EQUAL(fetcher.use_count(), 1);
+}
+
+BOOST_AUTO_TEST_CASE(Lifetime)
+{
+ // BasicSingleSegment, but with scoped fetcher
+
+ DummyValidator acceptValidator;
+ size_t nAfterSegmentReceived = 0;
+ size_t nAfterSegmentValidated = 0;
+ size_t nAfterSegmentNacked = 0;
+ size_t nAfterSegmentTimedOut = 0;
+
+ weak_ptr<SegmentFetcher> weakFetcher;
+ {
+ auto fetcher = SegmentFetcher::start(face, Interest("/hello/world"), acceptValidator);
+ weakFetcher = fetcher;
+ connectSignals(fetcher);
+
+ fetcher->afterSegmentReceived.connect(bind([&nAfterSegmentReceived] { ++nAfterSegmentReceived; }));
+ fetcher->afterSegmentValidated.connect(bind([&nAfterSegmentValidated] { ++nAfterSegmentValidated; }));
+ fetcher->afterSegmentNacked.connect(bind([&nAfterSegmentNacked] { ++nAfterSegmentNacked; }));
+ fetcher->afterSegmentTimedOut.connect(bind([&nAfterSegmentTimedOut] { ++nAfterSegmentTimedOut; }));
+ }
+
+ advanceClocks(10_ms);
+ BOOST_CHECK_EQUAL(weakFetcher.expired(), false);
+
+ face.receive(*makeDataSegment("/hello/world/version0", 0, true));
+
+ advanceClocks(10_ms);
+
+ BOOST_CHECK_EQUAL(nErrors, 0);
+ BOOST_CHECK_EQUAL(nCompletions, 1);
+ BOOST_CHECK_EQUAL(nAfterSegmentReceived, 1);
+ BOOST_CHECK_EQUAL(nAfterSegmentValidated, 1);
+ BOOST_CHECK_EQUAL(nAfterSegmentNacked, 0);
+ BOOST_CHECK_EQUAL(nAfterSegmentTimedOut, 0);
+ BOOST_CHECK_EQUAL(weakFetcher.expired(), true);
+}
+
+BOOST_AUTO_TEST_CASE(OutOfScopeTimeout)
+{
+ DummyValidator acceptValidator;
+ SegmentFetcher::Options options;
+ options.maxTimeout = 3000_ms;
+
+ size_t nAfterSegmentReceived = 0;
+ size_t nAfterSegmentValidated = 0;
+ size_t nAfterSegmentNacked = 0;
+ size_t nAfterSegmentTimedOut = 0;
+
+ weak_ptr<SegmentFetcher> weakFetcher;
+ {
+ auto fetcher = SegmentFetcher::start(face, Interest("/localhost/nfd/faces/list"),
+ acceptValidator, options);
+ weakFetcher = fetcher;
+ connectSignals(fetcher);
+ fetcher->afterSegmentReceived.connect(bind([&nAfterSegmentReceived] { ++nAfterSegmentReceived; }));
+ fetcher->afterSegmentValidated.connect(bind([&nAfterSegmentValidated] { ++nAfterSegmentValidated; }));
+ fetcher->afterSegmentNacked.connect(bind([&nAfterSegmentNacked] { ++nAfterSegmentNacked; }));
+ fetcher->afterSegmentTimedOut.connect(bind([&nAfterSegmentTimedOut] { ++nAfterSegmentTimedOut; }));
+ }
+
+ advanceClocks(500_ms, 7);
+ BOOST_CHECK_EQUAL(weakFetcher.expired(), true);
+
+ BOOST_CHECK_EQUAL(nErrors, 1);
+ BOOST_CHECK_EQUAL(nCompletions, 0);
+ BOOST_CHECK_EQUAL(nAfterSegmentReceived, 0);
+ BOOST_CHECK_EQUAL(nAfterSegmentValidated, 0);
+ BOOST_CHECK_EQUAL(nAfterSegmentNacked, 0);
+ BOOST_CHECK_EQUAL(nAfterSegmentTimedOut, 2);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestSegmentFetcher
+BOOST_AUTO_TEST_SUITE_END() // Util
+
+} // namespace tests
+} // namespace util
+} // namespace ndn
diff --git a/tests/unit/util/sha256.t.cpp b/tests/unit/util/sha256.t.cpp
new file mode 100644
index 0000000..094f5f6
--- /dev/null
+++ b/tests/unit/util/sha256.t.cpp
@@ -0,0 +1,214 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library 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 Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "util/sha256.hpp"
+#include "util/string-helper.hpp"
+
+#include "boost-test.hpp"
+
+#include <boost/endian/conversion.hpp>
+#include <sstream>
+
+namespace ndn {
+namespace util {
+namespace test {
+
+BOOST_AUTO_TEST_SUITE(Util)
+BOOST_AUTO_TEST_SUITE(TestSha256)
+
+BOOST_AUTO_TEST_CASE(Basic)
+{
+ const uint8_t input[] = {0x01, 0x02, 0x03, 0x04};
+ auto expected = fromHex("9f64a747e1b97f131fabb6b447296c9b6f0201e79fb3c5356e6c77e89b6a806a");
+
+ Sha256 statefulSha256;
+ BOOST_CHECK_EQUAL(statefulSha256.empty(), true);
+
+ statefulSha256.update(input, 1);
+ statefulSha256.update(input + 1, 1);
+ statefulSha256.update(input + 2, 1);
+ statefulSha256.update(input + 3, 1);
+ ConstBufferPtr digest = statefulSha256.computeDigest();
+ BOOST_CHECK_EQUAL(digest->size(), Sha256::DIGEST_SIZE);
+ BOOST_CHECK_EQUAL_COLLECTIONS(expected->data(), expected->data() + expected->size(),
+ digest->data(), digest->data() + digest->size());
+}
+
+BOOST_AUTO_TEST_CASE(ConstructFromStream)
+{
+ const std::string input = "Hello, world!";
+ auto expected = fromHex("315f5bdb76d078c43b8ac0064e4a0164612b1fce77c869345bfc94c75894edd3");
+
+ std::istringstream is(input);
+ Sha256 sha(is);
+ BOOST_CHECK_EQUAL(sha.empty(), false);
+ BOOST_CHECK_EQUAL(sha.toString(), "315F5BDB76D078C43B8AC0064E4A0164612B1FCE77C869345BFC94C75894EDD3");
+
+ ConstBufferPtr digest = sha.computeDigest();
+ BOOST_CHECK_EQUAL_COLLECTIONS(expected->data(), expected->data() + expected->size(),
+ digest->data(), digest->data() + digest->size());
+}
+
+BOOST_AUTO_TEST_CASE(Compare)
+{
+ const uint8_t origin[] = {0x01, 0x02, 0x03, 0x04};
+
+ Sha256 digest1;
+ digest1.update(origin, sizeof(origin));
+ digest1.computeDigest();
+
+ Sha256 digest2;
+ digest2.update(origin, 1);
+ digest2.update(origin + 1, 1);
+ digest2.update(origin + 2, 1);
+ digest2.update(origin + 3, 1);
+ digest2.computeDigest();
+
+ BOOST_CHECK_EQUAL(digest1 == digest2, true);
+ BOOST_CHECK_EQUAL(digest1 != digest2, false);
+}
+
+BOOST_AUTO_TEST_CASE(InsertionOperatorSha256)
+{
+ auto expected = fromHex("d7bd34bfe44a18d2aa755a344fe3e6b06ed0473772e6dfce16ac71ba0b0a241c");
+
+ Sha256 innerDigest;
+ innerDigest << "TEST";
+
+ Sha256 statefulSha256;
+ statefulSha256 << innerDigest;
+ ConstBufferPtr digest = statefulSha256.computeDigest();
+
+ BOOST_CHECK_EQUAL(statefulSha256.empty(), false);
+ BOOST_CHECK_EQUAL_COLLECTIONS(expected->data(), expected->data() + expected->size(),
+ digest->data(), digest->data() + digest->size());
+}
+
+BOOST_AUTO_TEST_CASE(InsertionOperatorString)
+{
+ const std::string input = "Hello, world!";
+ auto expected = fromHex("315f5bdb76d078c43b8ac0064e4a0164612b1fce77c869345bfc94c75894edd3");
+
+ Sha256 statefulSha256;
+ statefulSha256 << input;
+ ConstBufferPtr digest = statefulSha256.computeDigest();
+
+ BOOST_CHECK_EQUAL(statefulSha256.empty(), false);
+ BOOST_CHECK_EQUAL_COLLECTIONS(expected->data(), expected->data() + expected->size(),
+ digest->data(), digest->data() + digest->size());
+}
+
+BOOST_AUTO_TEST_CASE(InsertionOperatorBlock)
+{
+ const uint8_t input[] = {
+ 0x16, 0x1b, // SignatureInfo
+ 0x1b, 0x01, // SignatureType
+ 0x01, // Sha256WithRsa
+ 0x1c, 0x16, // KeyLocator
+ 0x07, 0x14, // Name
+ 0x08, 0x04,
+ 0x74, 0x65, 0x73, 0x74,
+ 0x08, 0x03,
+ 0x6b, 0x65, 0x79,
+ 0x08, 0x07,
+ 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x6f, 0x72
+ };
+ auto expected = fromHex("b372edfd4d6a4db2cfeaeead6c34fdee9b9e759f7b8d799cf8067e39e7f2886c");
+
+ Sha256 statefulSha256;
+ statefulSha256 << Block{input, sizeof(input)};
+ ConstBufferPtr digest = statefulSha256.computeDigest();
+
+ BOOST_CHECK_EQUAL(statefulSha256.empty(), false);
+ BOOST_CHECK_EQUAL_COLLECTIONS(expected->data(), expected->data() + expected->size(),
+ digest->data(), digest->data() + digest->size());
+}
+
+BOOST_AUTO_TEST_CASE(InsertionOperatorUint64t)
+{
+ const uint64_t input[] = {1, 2, 3, 4};
+ auto expected = fromHex("7236c00c170036c6de133a878210ddd58567aa1d0619a0f70f69e38ae6f916e9");
+
+ Sha256 statefulSha256;
+ for (size_t i = 0; i < sizeof(input) / sizeof(uint64_t); ++i) {
+ statefulSha256 << boost::endian::native_to_big(input[i]);
+ }
+ ConstBufferPtr digest = statefulSha256.computeDigest();
+
+ BOOST_CHECK_EQUAL(statefulSha256.empty(), false);
+ BOOST_CHECK_EQUAL_COLLECTIONS(expected->data(), expected->data() + expected->size(),
+ digest->data(), digest->data() + digest->size());
+}
+
+BOOST_AUTO_TEST_CASE(Reset)
+{
+ Sha256 sha;
+ BOOST_CHECK_EQUAL(sha.empty(), true);
+
+ sha << 42;
+ BOOST_CHECK_EQUAL(sha.empty(), false);
+
+ sha.computeDigest(); // finalize
+ sha.reset();
+ BOOST_CHECK_EQUAL(sha.empty(), true);
+ BOOST_CHECK_NO_THROW(sha << 42);
+}
+
+BOOST_AUTO_TEST_CASE(Error)
+{
+ Sha256 sha;
+ sha << 42;
+ sha.computeDigest(); // finalize
+ BOOST_CHECK_THROW(sha << 42, Sha256::Error);
+}
+
+BOOST_AUTO_TEST_CASE(StaticComputeDigest)
+{
+ const uint8_t input[] = {0x01, 0x02, 0x03, 0x04};
+ auto expected = fromHex("9f64a747e1b97f131fabb6b447296c9b6f0201e79fb3c5356e6c77e89b6a806a");
+
+ ConstBufferPtr digest = Sha256::computeDigest(input, sizeof(input));
+ BOOST_CHECK_EQUAL_COLLECTIONS(expected->data(), expected->data() + expected->size(),
+ digest->data(), digest->data() + digest->size());
+}
+
+BOOST_AUTO_TEST_CASE(Print)
+{
+ const uint8_t origin[] = {0x94, 0xEE, 0x05, 0x93, 0x35, 0xE5, 0x87, 0xE5,
+ 0x01, 0xCC, 0x4B, 0xF9, 0x06, 0x13, 0xE0, 0x81,
+ 0x4F, 0x00, 0xA7, 0xB0, 0x8B, 0xC7, 0xC6, 0x48,
+ 0xFD, 0x86, 0x5A, 0x2A, 0xF6, 0xA2, 0x2C, 0xC2};
+ std::string expected = toHex(origin, sizeof(origin));
+
+ Sha256 digest;
+ digest << "TEST";
+ std::ostringstream os;
+ os << digest;
+ BOOST_CHECK_EQUAL(os.str(), expected);
+ BOOST_CHECK_EQUAL(digest.toString(), expected);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestSha256
+BOOST_AUTO_TEST_SUITE_END() // Util
+
+} // namespace test
+} // namespace util
+} // namespace ndn
diff --git a/tests/unit/util/signal.t.cpp b/tests/unit/util/signal.t.cpp
new file mode 100644
index 0000000..c319f64
--- /dev/null
+++ b/tests/unit/util/signal.t.cpp
@@ -0,0 +1,450 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library 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 Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "util/signal.hpp"
+
+#include "boost-test.hpp"
+
+namespace ndn {
+namespace util {
+namespace signal {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Util)
+BOOST_AUTO_TEST_SUITE(TestSignal)
+
+class SignalOwner0
+{
+public:
+ Signal<SignalOwner0> sig;
+
+public:
+ DECLARE_SIGNAL_EMIT(sig)
+
+ bool
+ isSigEmpty()
+ {
+ return sig.isEmpty();
+ }
+};
+
+BOOST_AUTO_TEST_CASE(ZeroSlot)
+{
+ SignalOwner0 so;
+ BOOST_CHECK_NO_THROW(so.emitSignal(sig));
+}
+
+BOOST_AUTO_TEST_CASE(TwoListeners)
+{
+ SignalOwner0 so;
+
+ int hit1 = 0, hit2 = 0;
+ so.sig.connect([&hit1] { ++hit1; });
+ so.sig.connect([&hit2] { ++hit2; });
+
+ so.emitSignal(sig);
+
+ BOOST_CHECK_EQUAL(hit1, 1);
+ BOOST_CHECK_EQUAL(hit2, 1);
+}
+
+class SignalOwner1
+{
+public:
+ Signal<SignalOwner1, int> sig;
+
+protected:
+ DECLARE_SIGNAL_EMIT(sig)
+};
+
+class SignalEmitter1 : public SignalOwner1
+{
+public:
+ void
+ emitTestSignal()
+ {
+ this->emitSignal(sig, 8106);
+ }
+};
+
+BOOST_AUTO_TEST_CASE(OneArgument)
+{
+ SignalEmitter1 se;
+
+ int hit = 0;
+ se.sig.connect([&hit] (int a) {
+ ++hit;
+ BOOST_CHECK_EQUAL(a, 8106);
+ });
+ se.emitTestSignal();
+
+ BOOST_CHECK_EQUAL(hit, 1);
+}
+
+BOOST_AUTO_TEST_CASE(TwoArguments)
+{
+ Signal<std::remove_pointer_t<decltype(this)>, int, int> sig;
+
+ int hit = 0;
+ sig.connect([&hit] (int a, int b) {
+ ++hit;
+ BOOST_CHECK_EQUAL(a, 21);
+ BOOST_CHECK_EQUAL(b, 22);
+ });
+ sig(21, 22);
+
+ BOOST_CHECK_EQUAL(hit, 1);
+}
+
+class RefObject
+{
+public:
+ RefObject()
+ {
+ }
+
+ RefObject(const RefObject& other)
+ {
+ ++s_copyCount;
+ }
+
+public:
+ static int s_copyCount;
+};
+int RefObject::s_copyCount = 0;
+
+// Signal passes arguments by reference,
+// but it also allows a handler that accept arguments by value
+BOOST_AUTO_TEST_CASE(HandlerByVal)
+{
+ RefObject refObject;
+ RefObject::s_copyCount = 0;
+
+ Signal<std::remove_pointer_t<decltype(this)>, RefObject> sig;
+ sig.connect([] (RefObject) {});
+ sig(refObject);
+
+ BOOST_CHECK_EQUAL(RefObject::s_copyCount, 1);
+}
+
+// Signal passes arguments by reference, and no copying
+// is necessary when handler accepts arguments by reference
+BOOST_AUTO_TEST_CASE(HandlerByRef)
+{
+ RefObject refObject;
+ RefObject::s_copyCount = 0;
+
+ Signal<std::remove_pointer_t<decltype(this)>, RefObject> sig;
+ sig.connect([] (const RefObject&) {});
+ sig(refObject);
+
+ BOOST_CHECK_EQUAL(RefObject::s_copyCount, 0);
+}
+
+BOOST_AUTO_TEST_CASE(ManualDisconnect)
+{
+ SignalOwner0 so;
+
+ int hit = 0;
+ Connection c1 = so.sig.connect([&hit] { ++hit; });
+ BOOST_CHECK_EQUAL(c1.isConnected(), true);
+
+ so.emitSignal(sig);
+ BOOST_CHECK_EQUAL(hit, 1); // handler called
+
+ Connection c2 = c1; // make a copy
+ BOOST_CHECK_EQUAL(c2.isConnected(), true);
+ BOOST_CHECK_EQUAL(c1.isConnected(), true);
+ c2.disconnect();
+ BOOST_CHECK_EQUAL(c2.isConnected(), false);
+ BOOST_CHECK_EQUAL(c1.isConnected(), false);
+ so.emitSignal(sig);
+ BOOST_CHECK_EQUAL(hit, 1); // handler not called
+
+ BOOST_CHECK_NO_THROW(c2.disconnect());
+ BOOST_CHECK_NO_THROW(c1.disconnect());
+}
+
+BOOST_AUTO_TEST_CASE(ManualDisconnectDestructed)
+{
+ auto so = make_unique<SignalOwner0>();
+
+ int hit = 0;
+ Connection connection = so->sig.connect([&hit] { ++hit; });
+
+ so->emitSignal(sig);
+ BOOST_CHECK_EQUAL(hit, 1); // handler called
+
+ BOOST_CHECK_EQUAL(connection.isConnected(), true);
+ so.reset(); // destruct Signal
+ BOOST_CHECK_EQUAL(connection.isConnected(), false);
+ BOOST_CHECK_NO_THROW(connection.disconnect());
+}
+
+BOOST_AUTO_TEST_CASE(AutoDisconnect)
+{
+ SignalOwner0 so;
+
+ int hit = 0;
+ {
+ ScopedConnection sc = so.sig.connect([&hit] { ++hit; });
+
+ BOOST_CHECK_EQUAL(sc.isConnected(), true);
+ so.emitSignal(sig);
+ BOOST_CHECK_EQUAL(hit, 1); // handler called
+
+ // sc goes out of scope, disconnecting
+ }
+
+ so.emitSignal(sig);
+ BOOST_CHECK_EQUAL(hit, 1); // handler not called
+}
+
+BOOST_AUTO_TEST_CASE(AutoDisconnectAssign)
+{
+ SignalOwner0 so;
+
+ int hit1 = 0, hit2 = 0;
+ ScopedConnection sc = so.sig.connect([&hit1] { ++hit1; });
+ BOOST_CHECK_EQUAL(sc.isConnected(), true);
+
+ so.emitSignal(sig);
+ BOOST_CHECK_EQUAL(hit1, 1); // handler1 called
+
+ sc = so.sig.connect([&hit2] { ++hit2; }); // handler1 is disconnected
+ BOOST_CHECK_EQUAL(sc.isConnected(), true);
+
+ so.emitSignal(sig);
+ BOOST_CHECK_EQUAL(hit1, 1); // handler1 not called
+ BOOST_CHECK_EQUAL(hit2, 1); // handler2 called
+}
+
+BOOST_AUTO_TEST_CASE(AutoDisconnectAssignSame)
+{
+ SignalOwner0 so;
+
+ int hit = 0;
+ Connection c1 = so.sig.connect([&hit] { ++hit; });
+
+ ScopedConnection sc(c1);
+ so.emitSignal(sig);
+ BOOST_CHECK_EQUAL(hit, 1); // handler called
+ BOOST_CHECK_EQUAL(c1.isConnected(), true);
+ BOOST_CHECK_EQUAL(sc.isConnected(), true);
+
+ sc = c1; // assign same connection
+ so.emitSignal(sig);
+ BOOST_CHECK_EQUAL(hit, 2); // handler called
+ BOOST_CHECK_EQUAL(c1.isConnected(), true);
+ BOOST_CHECK_EQUAL(sc.isConnected(), true);
+
+ Connection c2 = c1;
+ sc = c2; // assign a copy of same connection
+ so.emitSignal(sig);
+ BOOST_CHECK_EQUAL(hit, 3); // handler called
+ BOOST_CHECK_EQUAL(c1.isConnected(), true);
+ BOOST_CHECK_EQUAL(c2.isConnected(), true);
+ BOOST_CHECK_EQUAL(sc.isConnected(), true);
+}
+
+BOOST_AUTO_TEST_CASE(AutoDisconnectRelease)
+{
+ SignalOwner0 so;
+
+ int hit = 0;
+ {
+ ScopedConnection sc = so.sig.connect([&hit] { ++hit; });
+
+ so.emitSignal(sig);
+ BOOST_CHECK_EQUAL(hit, 1); // handler called
+ BOOST_CHECK_EQUAL(sc.isConnected(), true);
+
+ sc.release();
+ BOOST_CHECK_EQUAL(sc.isConnected(), false);
+ // sc goes out of scope, but not disconnecting
+ }
+
+ so.emitSignal(sig);
+ BOOST_CHECK_EQUAL(hit, 2); // handler called
+}
+
+BOOST_AUTO_TEST_CASE(AutoDisconnectMove)
+{
+ SignalOwner0 so;
+ int hit = 0;
+
+ unique_ptr<ScopedConnection> sc2;
+ {
+ ScopedConnection sc = so.sig.connect([&hit] { ++hit; });
+
+ so.emitSignal(sig);
+ BOOST_CHECK_EQUAL(hit, 1); // handler called
+ BOOST_CHECK_EQUAL(sc.isConnected(), true);
+
+ sc2 = make_unique<ScopedConnection>(std::move(sc)); // move constructor
+ BOOST_CHECK_EQUAL(sc.isConnected(), false);
+ BOOST_CHECK_EQUAL(sc2->isConnected(), true);
+
+ // sc goes out of scope, but without disconnecting
+ }
+
+ so.emitSignal(sig);
+ BOOST_CHECK_EQUAL(hit, 2); // handler called
+ sc2.reset();
+
+ ScopedConnection sc3;
+ {
+ ScopedConnection sc = so.sig.connect([&hit] { ++hit; });
+
+ so.emitSignal(sig);
+ BOOST_CHECK_EQUAL(hit, 3); // handler called
+ BOOST_CHECK_EQUAL(sc.isConnected(), true);
+ BOOST_CHECK_EQUAL(sc3.isConnected(), false);
+
+ sc3 = std::move(sc); // move assignment
+ BOOST_CHECK_EQUAL(sc.isConnected(), false);
+ BOOST_CHECK_EQUAL(sc3.isConnected(), true);
+
+ // sc goes out of scope, but without disconnecting
+ }
+
+ so.emitSignal(sig);
+ BOOST_CHECK_EQUAL(hit, 4); // handler called
+}
+
+BOOST_AUTO_TEST_CASE(ConnectSingleShot)
+{
+ SignalOwner0 so;
+
+ int hit = 0;
+ so.sig.connectSingleShot([&hit] { ++hit; });
+
+ so.emitSignal(sig);
+ BOOST_CHECK_EQUAL(hit, 1); // handler called
+
+ so.emitSignal(sig);
+ BOOST_CHECK_EQUAL(hit, 1); // handler not called
+}
+
+BOOST_AUTO_TEST_CASE(ConnectSingleShotDisconnected)
+{
+ SignalOwner0 so;
+
+ int hit = 0;
+ Connection conn = so.sig.connectSingleShot([&hit] { ++hit; });
+ BOOST_CHECK_EQUAL(conn.isConnected(), true);
+ conn.disconnect();
+ BOOST_CHECK_EQUAL(conn.isConnected(), false);
+
+ so.emitSignal(sig);
+ BOOST_CHECK_EQUAL(hit, 0); // handler not called
+}
+
+BOOST_AUTO_TEST_CASE(ConnectSingleShot1)
+{
+ SignalEmitter1 se;
+
+ int hit = 0;
+ se.sig.connectSingleShot([&hit] (int) { ++hit; });
+
+ se.emitTestSignal();
+ BOOST_CHECK_EQUAL(hit, 1); // handler called
+
+ se.emitTestSignal();
+ BOOST_CHECK_EQUAL(hit, 1); // handler not called
+}
+
+BOOST_AUTO_TEST_CASE(ConnectInHandler)
+{
+ SignalOwner0 so;
+
+ int hit1 = 0, hit2 = 0; bool hasHandler2 = false;
+ so.sig.connect([&] {
+ ++hit1;
+ if (!hasHandler2) {
+ so.sig.connect([&] { ++hit2; });
+ hasHandler2 = true;
+ }
+ });
+
+ so.emitSignal(sig);
+ BOOST_CHECK_EQUAL(hit1, 1); // handler1 called
+ BOOST_CHECK_EQUAL(hit2, 0); // handler2 not called
+
+ // new subscription takes effect
+ so.emitSignal(sig);
+ BOOST_CHECK_EQUAL(hit1, 2); // handler1 called
+ BOOST_CHECK_EQUAL(hit2, 1); // handler2 called
+}
+
+BOOST_AUTO_TEST_CASE(DisconnectSelfInHandler)
+{
+ SignalOwner0 so;
+
+ int hit = 0;
+ Connection connection;
+ BOOST_CHECK_EQUAL(connection.isConnected(), false);
+ connection = so.sig.connect([&so, &connection, &hit] {
+ ++hit;
+ BOOST_CHECK_EQUAL(connection.isConnected(), true);
+ connection.disconnect();
+ BOOST_CHECK_EQUAL(connection.isConnected(), false);
+ BOOST_CHECK_EQUAL(so.isSigEmpty(), false); // disconnecting hasn't taken effect
+ });
+
+ so.emitSignal(sig);
+ BOOST_CHECK_EQUAL(hit, 1); // handler called
+ BOOST_CHECK_EQUAL(connection.isConnected(), false);
+
+ // disconnecting takes effect
+ BOOST_CHECK_EQUAL(so.isSigEmpty(), true);
+ so.emitSignal(sig);
+ BOOST_CHECK_EQUAL(hit, 1); // handler not called
+}
+
+BOOST_AUTO_TEST_CASE(ThrowInHandler)
+{
+ SignalOwner0 so;
+
+ struct HandlerError : public std::exception
+ {
+ };
+
+ int hit = 0;
+ so.sig.connect([&] {
+ ++hit;
+ BOOST_THROW_EXCEPTION(HandlerError());
+ });
+
+ BOOST_CHECK_THROW(so.emitSignal(sig), HandlerError);
+ BOOST_CHECK_EQUAL(hit, 1); // handler called
+
+ BOOST_CHECK_THROW(so.emitSignal(sig), HandlerError);
+ BOOST_CHECK_EQUAL(hit, 2); // handler called
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestSignal
+BOOST_AUTO_TEST_SUITE_END() // Util
+
+} // namespace tests
+} // namespace signal
+} // namespace util
+} // namespace ndn
diff --git a/tests/unit/util/simple-notification.hpp b/tests/unit/util/simple-notification.hpp
new file mode 100644
index 0000000..9e0d4ed
--- /dev/null
+++ b/tests/unit/util/simple-notification.hpp
@@ -0,0 +1,96 @@
+/* -*- 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 ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library 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 Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#ifndef NDN_TESTS_UNIT_TESTS_UTIL_SIMPLE_NOTIFICATION_HPP
+#define NDN_TESTS_UNIT_TESTS_UTIL_SIMPLE_NOTIFICATION_HPP
+
+#include "common.hpp"
+
+#include "encoding/encoding-buffer.hpp"
+
+namespace ndn {
+namespace util {
+namespace tests {
+
+class SimpleNotification
+{
+public:
+ SimpleNotification() = default;
+
+ explicit
+ SimpleNotification(const Block& block)
+ {
+ wireDecode(block);
+ }
+
+ SimpleNotification(const std::string& message)
+ : m_message(message)
+ {
+ }
+
+ Block
+ wireEncode() const
+ {
+ ndn::EncodingBuffer buffer;
+ buffer.prependByteArrayBlock(0x8888,
+ reinterpret_cast<const uint8_t*>(m_message.c_str()),
+ m_message.size());
+ return buffer.block();
+ }
+
+ void
+ wireDecode(const Block& block)
+ {
+ m_message.assign(reinterpret_cast<const char*>(block.value()),
+ block.value_size());
+
+ // error for testing
+ if (!m_message.empty() && m_message[0] == '\x07')
+ BOOST_THROW_EXCEPTION(tlv::Error("0x07 error"));
+ }
+
+ const std::string&
+ getMessage() const
+ {
+ return m_message;
+ }
+
+ void
+ setMessage(const std::string& message)
+ {
+ m_message = message;
+ }
+
+private:
+ std::string m_message;
+};
+
+} // namespace tests
+} // namespace util
+} // namespace ndn
+
+#endif // NDN_TESTS_UNIT_TESTS_UTIL_SIMPLE_NOTIFICATION_HPP
diff --git a/tests/unit/util/sqlite3-statement.t.cpp b/tests/unit/util/sqlite3-statement.t.cpp
new file mode 100644
index 0000000..13171c5
--- /dev/null
+++ b/tests/unit/util/sqlite3-statement.t.cpp
@@ -0,0 +1,158 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library 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 Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "util/sqlite3-statement.hpp"
+
+#include "boost-test.hpp"
+
+#include <boost/filesystem.hpp>
+#include <cstring>
+#include <sqlite3.h>
+
+namespace ndn {
+namespace util {
+namespace tests {
+
+class Sqlite3StatementTestFixture
+{
+public:
+ Sqlite3StatementTestFixture()
+ : m_path(boost::filesystem::path(UNIT_TEST_CONFIG_PATH))
+ {
+ boost::filesystem::create_directories(m_path);
+ int result = sqlite3_open_v2((m_path / "sqlite3-statement.db").string().c_str(), &db,
+ SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE,
+#ifdef NDN_CXX_DISABLE_SQLITE3_FS_LOCKING
+ "unix-dotfile"
+#else
+ nullptr
+#endif
+ );
+
+ if (result != SQLITE_OK) {
+ BOOST_FAIL("Sqlite3 database cannot be opened/created: " + m_path.string());
+ }
+ }
+
+ ~Sqlite3StatementTestFixture()
+ {
+ sqlite3_close(db);
+ boost::filesystem::remove_all(m_path);
+ }
+
+private:
+ boost::filesystem::path m_path;
+
+public:
+ sqlite3* db;
+};
+
+BOOST_AUTO_TEST_SUITE(Util)
+BOOST_FIXTURE_TEST_SUITE(TestSqlite3Statement, Sqlite3StatementTestFixture)
+
+BOOST_AUTO_TEST_CASE(Basic)
+{
+ // create table
+ BOOST_CHECK_NO_THROW(Sqlite3Statement(db, "CREATE TABLE test (t1 int, t2 text)").step());
+
+ // insert data into table
+ BOOST_CHECK_NO_THROW(Sqlite3Statement(db, "INSERT INTO test VALUES (1, 'test1')").step());
+
+ {
+ Sqlite3Statement stmt(db, "INSERT INTO test VALUES (2, ?)");
+ stmt.bind(1, "test2", std::strlen("test2"), SQLITE_STATIC);
+ stmt.step();
+ }
+
+ {
+ Sqlite3Statement stmt(db, "INSERT INTO test VALUES (3, ?)");
+ stmt.bind(1, "test3", SQLITE_TRANSIENT);
+ stmt.step();
+ }
+
+ Block block(100);
+ block.encode();
+ {
+ Sqlite3Statement stmt(db, "INSERT INTO test VALUES (4, ?)");
+ stmt.bind(1, block, SQLITE_STATIC);
+ stmt.step();
+ }
+
+ {
+ Sqlite3Statement stmt(db, "INSERT INTO test VALUES (5, ?)");
+ stmt.bind(1, reinterpret_cast<const void*>(block.wire()), block.size(), SQLITE_STATIC);
+ stmt.step();
+ }
+
+ {
+ Sqlite3Statement stmt(db, "INSERT INTO test VALUES (?, ?)");
+ stmt.bind(1, 6);
+ stmt.bind(2, "test", SQLITE_TRANSIENT);
+ stmt.step();
+ }
+
+ // check content of the table
+
+ {
+ Sqlite3Statement stmt(db, "SELECT count(*) FROM test");
+ BOOST_CHECK_EQUAL(stmt.step(), SQLITE_ROW);
+ BOOST_CHECK_EQUAL(stmt.getInt(0), 6);
+ BOOST_CHECK_EQUAL(stmt.step(), SQLITE_DONE);
+ }
+
+ {
+ Sqlite3Statement stmt(db, "SELECT t1, t2 FROM test ORDER BY t1");
+ BOOST_CHECK_EQUAL(stmt.step(), SQLITE_ROW);
+ BOOST_CHECK_EQUAL(stmt.getInt(0), 1);
+ BOOST_CHECK_EQUAL(stmt.getString(1), "test1");
+
+ BOOST_CHECK_EQUAL(stmt.step(), SQLITE_ROW);
+ BOOST_CHECK_EQUAL(stmt.getInt(0), 2);
+ BOOST_CHECK_EQUAL(stmt.getString(1), "test2");
+
+ BOOST_CHECK_EQUAL(stmt.step(), SQLITE_ROW);
+ BOOST_CHECK_EQUAL(stmt.getInt(0), 3);
+ BOOST_CHECK_EQUAL(stmt.getString(1), "test3");
+
+ BOOST_CHECK_EQUAL(stmt.step(), SQLITE_ROW);
+ BOOST_CHECK_EQUAL(stmt.getInt(0), 4);
+
+ Block newBlock = stmt.getBlock(1);
+ BOOST_CHECK_EQUAL(newBlock.type(), 100);
+ BOOST_CHECK_EQUAL(newBlock, block);
+
+ BOOST_CHECK_EQUAL(stmt.step(), SQLITE_ROW);
+ BOOST_CHECK_EQUAL(stmt.getInt(0), 5);
+ BOOST_CHECK_EQUAL(stmt.getSize(1), block.size());
+ BOOST_CHECK_EQUAL_COLLECTIONS(block.begin(), block.end(),
+ stmt.getBlob(1), stmt.getBlob(1) + stmt.getSize(1));
+
+ BOOST_CHECK_EQUAL(stmt.step(), SQLITE_ROW);
+ BOOST_CHECK_EQUAL(stmt.step(), SQLITE_DONE);
+ }
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestSqlite3Statement
+BOOST_AUTO_TEST_SUITE_END() // Util
+
+} // namespace tests
+} // namespace util
+} // namespace ndn
diff --git a/tests/unit/util/string-helper.t.cpp b/tests/unit/util/string-helper.t.cpp
new file mode 100644
index 0000000..0456a35
--- /dev/null
+++ b/tests/unit/util/string-helper.t.cpp
@@ -0,0 +1,208 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library 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 Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "util/string-helper.hpp"
+#include "encoding/buffer.hpp"
+
+#include "boost-test.hpp"
+
+#include <cctype>
+#include <cstring>
+
+namespace ndn {
+namespace util {
+namespace test {
+
+using boost::test_tools::output_test_stream;
+
+BOOST_AUTO_TEST_SUITE(Util)
+BOOST_AUTO_TEST_SUITE(TestStringHelper)
+
+BOOST_AUTO_TEST_CASE(PrintHex)
+{
+ output_test_stream os;
+
+ printHex(os, 0);
+ BOOST_CHECK(os.is_equal("0x0"));
+
+ printHex(os, 42);
+ BOOST_CHECK(os.is_equal("0x2a"));
+
+ printHex(os, 2748, true);
+ BOOST_CHECK(os.is_equal("0xABC"));
+
+ printHex(os, static_cast<uint64_t>(-1));
+ BOOST_CHECK(os.is_equal("0xffffffffffffffff"));
+
+ printHex(os, ~0U, true);
+ BOOST_CHECK(os.is_equal("0xFFFFFFFF"));
+
+ printHex(os, ~0ULL, true);
+ BOOST_CHECK(os.is_equal("0xFFFFFFFFFFFFFFFF"));
+}
+
+BOOST_AUTO_TEST_CASE(AsHex)
+{
+ using ndn::AsHex;
+ output_test_stream os;
+
+ os << AsHex{0};
+ BOOST_CHECK(os.is_equal("0x0"));
+
+ os << AsHex{42};
+ BOOST_CHECK(os.is_equal("0x2a"));
+
+ os << std::uppercase << AsHex{~0U};
+ BOOST_CHECK(os.is_equal("0xFFFFFFFF"));
+
+ os << std::nouppercase << AsHex{~0U};
+ BOOST_CHECK(os.is_equal("0xffffffff"));
+}
+
+BOOST_AUTO_TEST_CASE(ToHex)
+{
+ std::string test = "Hello, world!";
+ BOOST_CHECK_EQUAL(toHex(reinterpret_cast<const uint8_t*>(test.data()), test.size()),
+ "48656C6C6F2C20776F726C6421");
+ BOOST_CHECK_EQUAL(toHex(reinterpret_cast<const uint8_t*>(test.data()), test.size(), false),
+ "48656c6c6f2c20776f726c6421");
+ BOOST_CHECK_EQUAL(toHex(nullptr, 0), "");
+
+ Buffer buffer(test.data(), test.size());
+ BOOST_CHECK_EQUAL(toHex(buffer, false), "48656c6c6f2c20776f726c6421");
+ BOOST_CHECK_EQUAL(toHex(Buffer{}), "");
+}
+
+BOOST_AUTO_TEST_CASE(FromHex)
+{
+ BOOST_CHECK(*fromHex("") == Buffer{});
+ BOOST_CHECK(*fromHex("48656c6c6f2c20776f726c6421") ==
+ (std::vector<uint8_t>{0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x2c, 0x20,
+ 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x21}));
+ BOOST_CHECK(*fromHex("012a3Bc4defAB5CdEF") ==
+ (std::vector<uint8_t>{0x01, 0x2a, 0x3b, 0xc4, 0xde,
+ 0xfa, 0xb5, 0xcd, 0xef}));
+
+ BOOST_CHECK_THROW(fromHex("1"), StringHelperError);
+ BOOST_CHECK_THROW(fromHex("zz"), StringHelperError);
+ BOOST_CHECK_THROW(fromHex("00az"), StringHelperError);
+ BOOST_CHECK_THROW(fromHex("1234z"), StringHelperError);
+}
+
+BOOST_AUTO_TEST_CASE(ToHexChar)
+{
+ static const std::vector<std::pair<unsigned int, char>> hexMap{
+ {0, '0'}, {1, '1'}, {2, '2'}, {3, '3'}, {4, '4'}, {5, '5'}, {6, '6'}, {7, '7'},
+ {8, '8'}, {9, '9'}, {10, 'A'}, {11, 'B'}, {12, 'C'}, {13, 'D'}, {14, 'E'}, {15, 'F'}
+ };
+
+ for (const auto& i : hexMap) {
+ BOOST_CHECK_EQUAL(toHexChar(i.first), i.second);
+ BOOST_CHECK_EQUAL(toHexChar(i.first + 16), i.second);
+ BOOST_CHECK_EQUAL(toHexChar(i.first + 32), i.second);
+ BOOST_CHECK_EQUAL(toHexChar(i.first + 240), i.second);
+ BOOST_CHECK_EQUAL(toHexChar(i.first, false), std::tolower(static_cast<unsigned char>(i.second)));
+ }
+}
+
+BOOST_AUTO_TEST_CASE(FromHexChar)
+{
+ // for (int ch = 0; ch <= std::numeric_limits<uint8_t>::max(); ++ch) {
+ // std::cout << "{0x" << std::hex << ch << ", "
+ // << std::dec << fromHexChar(static_cast<char>(ch)) << "}, ";
+ // if (ch % 8 == 7)
+ // std::cout << std::endl;
+ // }
+ static const std::vector<std::pair<char, int>> hexMap{
+ {0x0, -1}, {0x1, -1}, {0x2, -1}, {0x3, -1}, {0x4, -1}, {0x5, -1}, {0x6, -1}, {0x7, -1},
+ {0x8, -1}, {0x9, -1}, {0xa, -1}, {0xb, -1}, {0xc, -1}, {0xd, -1}, {0xe, -1}, {0xf, -1},
+ {0x10, -1}, {0x11, -1}, {0x12, -1}, {0x13, -1}, {0x14, -1}, {0x15, -1}, {0x16, -1}, {0x17, -1},
+ {0x18, -1}, {0x19, -1}, {0x1a, -1}, {0x1b, -1}, {0x1c, -1}, {0x1d, -1}, {0x1e, -1}, {0x1f, -1},
+ {0x20, -1}, {0x21, -1}, {0x22, -1}, {0x23, -1}, {0x24, -1}, {0x25, -1}, {0x26, -1}, {0x27, -1},
+ {0x28, -1}, {0x29, -1}, {0x2a, -1}, {0x2b, -1}, {0x2c, -1}, {0x2d, -1}, {0x2e, -1}, {0x2f, -1},
+ {0x30, 0}, {0x31, 1}, {0x32, 2}, {0x33, 3}, {0x34, 4}, {0x35, 5}, {0x36, 6}, {0x37, 7},
+ {0x38, 8}, {0x39, 9}, {0x3a, -1}, {0x3b, -1}, {0x3c, -1}, {0x3d, -1}, {0x3e, -1}, {0x3f, -1},
+ {0x40, -1}, {0x41, 10}, {0x42, 11}, {0x43, 12}, {0x44, 13}, {0x45, 14}, {0x46, 15}, {0x47, -1},
+ {0x48, -1}, {0x49, -1}, {0x4a, -1}, {0x4b, -1}, {0x4c, -1}, {0x4d, -1}, {0x4e, -1}, {0x4f, -1},
+ {0x50, -1}, {0x51, -1}, {0x52, -1}, {0x53, -1}, {0x54, -1}, {0x55, -1}, {0x56, -1}, {0x57, -1},
+ {0x58, -1}, {0x59, -1}, {0x5a, -1}, {0x5b, -1}, {0x5c, -1}, {0x5d, -1}, {0x5e, -1}, {0x5f, -1},
+ {0x60, -1}, {0x61, 10}, {0x62, 11}, {0x63, 12}, {0x64, 13}, {0x65, 14}, {0x66, 15}, {0x67, -1},
+ {0x68, -1}, {0x69, -1}, {0x6a, -1}, {0x6b, -1}, {0x6c, -1}, {0x6d, -1}, {0x6e, -1}, {0x6f, -1},
+ {0x70, -1}, {0x71, -1}, {0x72, -1}, {0x73, -1}, {0x74, -1}, {0x75, -1}, {0x76, -1}, {0x77, -1},
+ {0x78, -1}, {0x79, -1}, {0x7a, -1}, {0x7b, -1}, {0x7c, -1}, {0x7d, -1}, {0x7e, -1}, {0x7f, -1},
+ {0x80, -1}, {0x81, -1}, {0x82, -1}, {0x83, -1}, {0x84, -1}, {0x85, -1}, {0x86, -1}, {0x87, -1},
+ {0x88, -1}, {0x89, -1}, {0x8a, -1}, {0x8b, -1}, {0x8c, -1}, {0x8d, -1}, {0x8e, -1}, {0x8f, -1},
+ {0x90, -1}, {0x91, -1}, {0x92, -1}, {0x93, -1}, {0x94, -1}, {0x95, -1}, {0x96, -1}, {0x97, -1},
+ {0x98, -1}, {0x99, -1}, {0x9a, -1}, {0x9b, -1}, {0x9c, -1}, {0x9d, -1}, {0x9e, -1}, {0x9f, -1},
+ {0xa0, -1}, {0xa1, -1}, {0xa2, -1}, {0xa3, -1}, {0xa4, -1}, {0xa5, -1}, {0xa6, -1}, {0xa7, -1},
+ {0xa8, -1}, {0xa9, -1}, {0xaa, -1}, {0xab, -1}, {0xac, -1}, {0xad, -1}, {0xae, -1}, {0xaf, -1},
+ {0xb0, -1}, {0xb1, -1}, {0xb2, -1}, {0xb3, -1}, {0xb4, -1}, {0xb5, -1}, {0xb6, -1}, {0xb7, -1},
+ {0xb8, -1}, {0xb9, -1}, {0xba, -1}, {0xbb, -1}, {0xbc, -1}, {0xbd, -1}, {0xbe, -1}, {0xbf, -1},
+ {0xc0, -1}, {0xc1, -1}, {0xc2, -1}, {0xc3, -1}, {0xc4, -1}, {0xc5, -1}, {0xc6, -1}, {0xc7, -1},
+ {0xc8, -1}, {0xc9, -1}, {0xca, -1}, {0xcb, -1}, {0xcc, -1}, {0xcd, -1}, {0xce, -1}, {0xcf, -1},
+ {0xd0, -1}, {0xd1, -1}, {0xd2, -1}, {0xd3, -1}, {0xd4, -1}, {0xd5, -1}, {0xd6, -1}, {0xd7, -1},
+ {0xd8, -1}, {0xd9, -1}, {0xda, -1}, {0xdb, -1}, {0xdc, -1}, {0xdd, -1}, {0xde, -1}, {0xdf, -1},
+ {0xe0, -1}, {0xe1, -1}, {0xe2, -1}, {0xe3, -1}, {0xe4, -1}, {0xe5, -1}, {0xe6, -1}, {0xe7, -1},
+ {0xe8, -1}, {0xe9, -1}, {0xea, -1}, {0xeb, -1}, {0xec, -1}, {0xed, -1}, {0xee, -1}, {0xef, -1},
+ {0xf0, -1}, {0xf1, -1}, {0xf2, -1}, {0xf3, -1}, {0xf4, -1}, {0xf5, -1}, {0xf6, -1}, {0xf7, -1},
+ {0xf8, -1}, {0xf9, -1}, {0xfa, -1}, {0xfb, -1}, {0xfc, -1}, {0xfd, -1}, {0xfe, -1}, {0xff, -1}
+ };
+
+ for (const auto& item : hexMap) {
+ BOOST_CHECK_EQUAL(fromHexChar(item.first), item.second);
+ }
+}
+
+BOOST_AUTO_TEST_CASE(Escape)
+{
+ BOOST_CHECK_EQUAL(escape(""), "");
+ BOOST_CHECK_EQUAL(escape("foo42"), "foo42");
+ BOOST_CHECK_EQUAL(escape("foo%bar"), "foo%25bar");
+ BOOST_CHECK_EQUAL(escape("lower UPPER"), "lower%20UPPER");
+ BOOST_CHECK_EQUAL(escape("-._~"), "-._~");
+ BOOST_CHECK_EQUAL(escape(":/?#[]@"), "%3A%2F%3F%23%5B%5D%40");
+
+ output_test_stream os;
+ const char str[] = "\x01\x2a\x3b\xc4\xde\xfa\xb5\xcd\xef";
+ escape(os, str, std::strlen(str));
+ BOOST_CHECK(os.is_equal("%01%2A%3B%C4%DE%FA%B5%CD%EF"));
+}
+
+BOOST_AUTO_TEST_CASE(Unescape)
+{
+ BOOST_CHECK_EQUAL(unescape(""), "");
+ BOOST_CHECK_EQUAL(unescape("Hello%01, world!%AA "), "Hello\x01, world!\xAA ");
+ BOOST_CHECK_EQUAL(unescape("Bad %ZZ (not a hex value)"), "Bad %ZZ (not a hex value)");
+ BOOST_CHECK_EQUAL(unescape("Bad %a (should be two hex chars)"), "Bad %a (should be two hex chars)");
+ BOOST_CHECK_EQUAL(unescape("Bad %a"), "Bad %a");
+
+ output_test_stream os;
+ const char str[] = "%01%2a%3B%c4%de%fA%B5%Cd%EF";
+ unescape(os, str, std::strlen(str));
+ BOOST_CHECK(os.is_equal("\x01\x2a\x3b\xc4\xde\xfa\xb5\xcd\xef"));
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestStringHelper
+BOOST_AUTO_TEST_SUITE_END() // Util
+
+} // namespace test
+} // namespace util
+} // namespace ndn
diff --git a/tests/unit/util/time-unit-test-clock.t.cpp b/tests/unit/util/time-unit-test-clock.t.cpp
new file mode 100644
index 0000000..dd8346a
--- /dev/null
+++ b/tests/unit/util/time-unit-test-clock.t.cpp
@@ -0,0 +1,138 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library 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 Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "util/time-unit-test-clock.hpp"
+#include "util/scheduler.hpp"
+
+#include "boost-test.hpp"
+#include "../unit-test-time-fixture.hpp"
+
+#include <boost/lexical_cast.hpp>
+#include <thread>
+
+namespace ndn {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Util)
+BOOST_FIXTURE_TEST_SUITE(TestTimeUnitTestClock, UnitTestTimeFixture)
+
+BOOST_AUTO_TEST_CASE(SystemClock)
+{
+ BOOST_CHECK_EQUAL(time::system_clock::now().time_since_epoch(),
+ time::UnitTestClockTraits<time::system_clock>::getDefaultStartTime());
+ std::this_thread::sleep_for(std::chrono::milliseconds(100));
+
+ BOOST_CHECK_EQUAL(time::system_clock::now().time_since_epoch(),
+ time::UnitTestClockTraits<time::system_clock>::getDefaultStartTime());
+
+ steadyClock->advance(1_day);
+ BOOST_CHECK_EQUAL(time::system_clock::now().time_since_epoch(),
+ time::UnitTestClockTraits<time::system_clock>::getDefaultStartTime());
+
+ systemClock->advance(1_day);
+ BOOST_CHECK_GT(time::system_clock::now().time_since_epoch(),
+ time::UnitTestClockTraits<time::system_clock>::getDefaultStartTime());
+
+ time::system_clock::TimePoint referenceTime =
+ time::fromUnixTimestamp(time::milliseconds(1390966967032LL));
+ BOOST_CHECK_GT(time::system_clock::now(), referenceTime);
+
+ systemClock->setNow(referenceTime.time_since_epoch());
+ BOOST_CHECK_EQUAL(time::system_clock::now(), referenceTime);
+
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(time::system_clock::now()),
+ "1390966967032000000 nanoseconds since unit test beginning");
+
+ BOOST_CHECK_EQUAL(time::toIsoString(referenceTime), "20140129T034247.032000");
+ BOOST_CHECK_EQUAL(time::toString(referenceTime), "2014-01-29 03:42:47");
+ BOOST_CHECK_EQUAL(time::toString(referenceTime), "2014-01-29 03:42:47");
+
+ // Unfortunately, not all systems has lv_LV locale installed :(
+ // BOOST_CHECK_EQUAL(time::toString(referenceTime, "%Y. gada %d. %B",
+ // std::locale("lv_LV.UTF-8")),
+ // "2014. gada 29. Janvāris");
+
+ BOOST_CHECK_EQUAL(time::toString(referenceTime, "%Y -- %d -- %B",
+ std::locale("C")),
+ "2014 -- 29 -- January");
+
+ BOOST_CHECK_EQUAL(time::fromIsoString("20140129T034247.032000"), referenceTime);
+ BOOST_CHECK_EQUAL(time::fromIsoString("20140129T034247.032000Z"), referenceTime);
+ BOOST_CHECK_EQUAL(time::fromString("2014-01-29 03:42:47"),
+ time::fromUnixTimestamp(1390966967_s));
+
+ // Unfortunately, not all systems has lv_LV locale installed :(
+ // BOOST_CHECK_EQUAL(time::fromString("2014. gada 29. Janvāris", "%Y. gada %d. %B",
+ // std::locale("lv_LV.UTF-8")),
+ // time::fromUnixTimestamp(1390953600_s));
+
+ BOOST_CHECK_EQUAL(time::fromString("2014 -- 29 -- January", "%Y -- %d -- %B",
+ std::locale("C")),
+ time::fromUnixTimestamp(1390953600_s));
+}
+
+BOOST_AUTO_TEST_CASE(SteadyClock)
+{
+ BOOST_CHECK_EQUAL(time::steady_clock::now().time_since_epoch(),
+ time::steady_clock::duration::zero());
+
+ std::this_thread::sleep_for(std::chrono::milliseconds(100));
+ BOOST_CHECK_EQUAL(time::steady_clock::now().time_since_epoch(),
+ time::steady_clock::duration::zero());
+
+ systemClock->advance(36500_days);
+ BOOST_CHECK_EQUAL(time::steady_clock::now().time_since_epoch(),
+ time::steady_clock::duration::zero());
+
+ steadyClock->advance(100_ns);
+ BOOST_CHECK_EQUAL(time::steady_clock::now().time_since_epoch(), 100_ns);
+
+ steadyClock->advance(100_us);
+ BOOST_CHECK_EQUAL(time::steady_clock::now().time_since_epoch(), 100100_ns);
+
+ steadyClock->setNow(1_ms);
+ BOOST_CHECK_EQUAL(time::steady_clock::now().time_since_epoch(), 1000000_ns);
+
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(time::steady_clock::now()),
+ "1000000 nanoseconds since unit test beginning");
+}
+
+BOOST_AUTO_TEST_CASE(Scheduler)
+{
+ ndn::Scheduler scheduler(io);
+
+ bool hasFired = false;
+ scheduler.scheduleEvent(100_s, [&] { hasFired = true; });
+
+ io.poll();
+ BOOST_CHECK_EQUAL(hasFired, false);
+
+ steadyClock->advance(100_s);
+
+ io.poll();
+ BOOST_CHECK_EQUAL(hasFired, true);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestTimeUnitTestClock
+BOOST_AUTO_TEST_SUITE_END() // Util
+
+} // namespace tests
+} // namespace ndn
diff --git a/tests/unit/util/time.t.cpp b/tests/unit/util/time.t.cpp
new file mode 100644
index 0000000..a60e2d9
--- /dev/null
+++ b/tests/unit/util/time.t.cpp
@@ -0,0 +1,138 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library 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 Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#include "util/time.hpp"
+
+#include "boost-test.hpp"
+
+#include <thread>
+
+namespace ndn {
+namespace time {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Util)
+BOOST_AUTO_TEST_SUITE(TestTime)
+
+BOOST_AUTO_TEST_CASE(SystemClock)
+{
+ system_clock::TimePoint value = system_clock::now();
+ system_clock::TimePoint referenceTime = fromUnixTimestamp(milliseconds(1390966967032LL));
+
+ BOOST_CHECK_GT(value, referenceTime);
+
+ BOOST_CHECK_EQUAL(toIsoString(referenceTime), "20140129T034247.032000");
+ BOOST_CHECK_EQUAL(toString(referenceTime), "2014-01-29 03:42:47");
+ BOOST_CHECK_EQUAL(toString(referenceTime), "2014-01-29 03:42:47");
+
+ // Unfortunately, not all systems has lv_LV locale installed :(
+ // BOOST_CHECK_EQUAL(toString(referenceTime, "%Y. gada %d. %B", std::locale("lv_LV.UTF-8")),
+ // "2014. gada 29. Janvāris");
+
+ BOOST_CHECK_EQUAL(toString(referenceTime, "%Y -- %d -- %B", std::locale("C")),
+ "2014 -- 29 -- January");
+
+ BOOST_CHECK_EQUAL(fromIsoString("20140129T034247.032000"), referenceTime);
+ BOOST_CHECK_EQUAL(fromIsoString("20140129T034247.032000Z"), referenceTime);
+ BOOST_CHECK_EQUAL(fromString("2014-01-29 03:42:47"), fromUnixTimestamp(1390966967_s));
+
+ // Unfortunately, not all systems has lv_LV locale installed :(
+ // BOOST_CHECK_EQUAL(fromString("2014. gada 29. Janvāris", "%Y. gada %d. %B", std::locale("lv_LV.UTF-8")),
+ // fromUnixTimestamp(1390953600_s));
+
+ BOOST_CHECK_EQUAL(fromString("2014 -- 29 -- January", "%Y -- %d -- %B", std::locale("C")),
+ fromUnixTimestamp(1390953600_s));
+}
+
+BOOST_AUTO_TEST_CASE(SteadyClock)
+{
+ steady_clock::TimePoint oldValue = steady_clock::now();
+ std::this_thread::sleep_for(std::chrono::milliseconds(1));
+ steady_clock::TimePoint newValue = steady_clock::now();
+ BOOST_CHECK_GT(newValue, oldValue);
+}
+
+BOOST_AUTO_TEST_CASE(Abs)
+{
+ BOOST_CHECK_EQUAL(abs(nanoseconds(24422)), nanoseconds(24422));
+ BOOST_CHECK_EQUAL(abs(microseconds(0)), microseconds(0));
+ BOOST_CHECK_EQUAL(abs(milliseconds(-15583)), milliseconds(15583));
+}
+
+BOOST_AUTO_TEST_CASE(LargeDates)
+{
+ auto value = fromUnixTimestamp(1390966967032_ms);
+ BOOST_CHECK_EQUAL(toIsoString(value), "20140129T034247.032000");
+ BOOST_CHECK_EQUAL(fromIsoString("20140129T034247.032000"), value);
+ BOOST_CHECK_EQUAL(toString(value, "%Y-%m-%d %H:%M:%S%F"), "2014-01-29 03:42:47.032000");
+ BOOST_CHECK_EQUAL(fromString("2014-01-29 03:42:47.032000", "%Y-%m-%d %H:%M:%S%F"), value);
+
+ value += 36524_days;
+ BOOST_CHECK_EQUAL(toIsoString(value), "21140129T034247.032000");
+ BOOST_CHECK_EQUAL(fromIsoString("21140129T034247.032000"), value);
+ BOOST_CHECK_EQUAL(toString(value, "%Y-%m-%d %H:%M:%S%F"), "2114-01-29 03:42:47.032000");
+ BOOST_CHECK_EQUAL(fromString("2114-01-29 03:42:47.03200", "%Y-%m-%d %H:%M:%S%F"), value);
+}
+
+BOOST_AUTO_TEST_CASE(Literals)
+{
+ BOOST_CHECK_EQUAL(42_s, seconds(42));
+
+ BOOST_CHECK_EQUAL(1_day, 24_h);
+ BOOST_CHECK_EQUAL(2_days, 48_h);
+ BOOST_CHECK_EQUAL(0.5_day, 12_h);
+ BOOST_CHECK_EQUAL(.5_days, 12_h);
+
+ BOOST_CHECK_EQUAL(1_h, 60_min);
+ BOOST_CHECK_EQUAL(0.5_h, 30_min);
+
+ BOOST_CHECK_EQUAL(1_min, 60_s);
+ BOOST_CHECK_EQUAL(0.5_min, 30_s);
+
+ BOOST_CHECK_EQUAL(1_s, 1000_ms);
+ BOOST_CHECK_EQUAL(0.5_s, 500_ms);
+
+ BOOST_CHECK_EQUAL(1_ms, 1000_us);
+ BOOST_CHECK_EQUAL(0.5_ms, 500_us);
+
+ BOOST_CHECK_EQUAL(1_us, 1000_ns);
+ BOOST_CHECK_EQUAL(0.5_us, 500_ns);
+
+ BOOST_CHECK_EQUAL(1_ns, nanoseconds(1));
+ BOOST_CHECK_EQUAL(5.5_ns, 0.0055_us);
+}
+
+BOOST_AUTO_TEST_CASE(Year2038)
+{
+ auto year2042 = fromIsoString("20420101T000001.042000");
+ auto year2010 = fromIsoString("20100101T000001.042000");
+
+ BOOST_CHECK_EQUAL(to_string(year2010), "1262304001042000000 nanoseconds since Jan 1, 1970");
+ BOOST_CHECK_EQUAL(to_string(year2042), "2272147201042000000 nanoseconds since Jan 1, 1970");
+ BOOST_CHECK_GT(year2042, year2010);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestTime
+BOOST_AUTO_TEST_SUITE_END() // Util
+
+} // namespace tests
+} // namespace time
+} // namespace ndn