Rename 'tests/unit-tests' directory to 'tests/unit'
Change-Id: I78ea29938259fac288781bed12fb2399ac7eba26
diff --git a/tests/unit/data.t.cpp b/tests/unit/data.t.cpp
new file mode 100644
index 0000000..0d91005
--- /dev/null
+++ b/tests/unit/data.t.cpp
@@ -0,0 +1,442 @@
+/* -*- 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 "data.hpp"
+#include "encoding/buffer-stream.hpp"
+#include "security/signature-sha256-with-rsa.hpp"
+#include "security/transform/private-key.hpp"
+#include "security/transform/public-key.hpp"
+#include "security/transform/signer-filter.hpp"
+#include "security/transform/step-source.hpp"
+#include "security/transform/stream-sink.hpp"
+#include "security/verification-helpers.hpp"
+#include "util/sha256.hpp"
+
+#include "boost-test.hpp"
+#include "identity-management-fixture.hpp"
+#include <boost/lexical_cast.hpp>
+
+namespace ndn {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(TestData)
+
+const uint8_t CONTENT1[] = {0x53, 0x55, 0x43, 0x43, 0x45, 0x53, 0x53, 0x21};
+
+const uint8_t DATA1[] = {
+0x06, 0xc5, // Data
+ 0x07, 0x14, // Name
+ 0x08, 0x05,
+ 0x6c, 0x6f, 0x63, 0x61, 0x6c,
+ 0x08, 0x03,
+ 0x6e, 0x64, 0x6e,
+ 0x08, 0x06,
+ 0x70, 0x72, 0x65, 0x66, 0x69, 0x78,
+ 0x14, 0x04, // MetaInfo
+ 0x19, 0x02, // FreshnessPeriod
+ 0x27, 0x10,
+ 0x15, 0x08, // Content
+ 0x53, 0x55, 0x43, 0x43, 0x45, 0x53, 0x53, 0x21,
+ 0x16, 0x1b, // SignatureInfo
+ 0x1b, 0x01, // SignatureType
+ 0x01,
+ 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,
+ 0x17, 0x80, // SignatureValue
+ 0x2f, 0xd6, 0xf1, 0x6e, 0x80, 0x6f, 0x10, 0xbe, 0xb1, 0x6f, 0x3e, 0x31, 0xec,
+ 0xe3, 0xb9, 0xea, 0x83, 0x30, 0x40, 0x03, 0xfc, 0xa0, 0x13, 0xd9, 0xb3, 0xc6,
+ 0x25, 0x16, 0x2d, 0xa6, 0x58, 0x41, 0x69, 0x62, 0x56, 0xd8, 0xb3, 0x6a, 0x38,
+ 0x76, 0x56, 0xea, 0x61, 0xb2, 0x32, 0x70, 0x1c, 0xb6, 0x4d, 0x10, 0x1d, 0xdc,
+ 0x92, 0x8e, 0x52, 0xa5, 0x8a, 0x1d, 0xd9, 0x96, 0x5e, 0xc0, 0x62, 0x0b, 0xcf,
+ 0x3a, 0x9d, 0x7f, 0xca, 0xbe, 0xa1, 0x41, 0x71, 0x85, 0x7a, 0x8b, 0x5d, 0xa9,
+ 0x64, 0xd6, 0x66, 0xb4, 0xe9, 0x8d, 0x0c, 0x28, 0x43, 0xee, 0xa6, 0x64, 0xe8,
+ 0x55, 0xf6, 0x1c, 0x19, 0x0b, 0xef, 0x99, 0x25, 0x1e, 0xdc, 0x78, 0xb3, 0xa7,
+ 0xaa, 0x0d, 0x14, 0x58, 0x30, 0xe5, 0x37, 0x6a, 0x6d, 0xdb, 0x56, 0xac, 0xa3,
+ 0xfc, 0x90, 0x7a, 0xb8, 0x66, 0x9c, 0x0e, 0xf6, 0xb7, 0x64, 0xd1
+};
+
+// ---- constructor, encode, decode ----
+
+BOOST_AUTO_TEST_CASE(DefaultConstructor)
+{
+ Data d;
+ BOOST_CHECK_EQUAL(d.hasWire(), false);
+ BOOST_CHECK_EQUAL(d.getName(), "/");
+ BOOST_CHECK_EQUAL(d.getContentType(), tlv::ContentType_Blob);
+ BOOST_CHECK_EQUAL(d.getFreshnessPeriod(), DEFAULT_FRESHNESS_PERIOD);
+ BOOST_CHECK(!d.getFinalBlock());
+ BOOST_CHECK_EQUAL(d.getContent().type(), tlv::Content);
+ BOOST_CHECK_EQUAL(d.getContent().value_size(), 0);
+ BOOST_CHECK(!d.getSignature());
+}
+
+class DataSigningKeyFixture
+{
+protected:
+ DataSigningKeyFixture()
+ {
+ m_privKey.loadPkcs1(PRIVATE_KEY_DER, sizeof(PRIVATE_KEY_DER));
+ auto buf = m_privKey.derivePublicKey();
+ m_pubKey.loadPkcs8(buf->data(), buf->size());
+ }
+
+protected:
+ security::transform::PrivateKey m_privKey;
+ security::transform::PublicKey m_pubKey;
+
+private:
+ static const uint8_t PRIVATE_KEY_DER[632];
+};
+
+const uint8_t DataSigningKeyFixture::PRIVATE_KEY_DER[] = {
+ 0x30, 0x82, 0x02, 0x74, 0x02, 0x01, 0x00, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
+ 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x04, 0x82, 0x02, 0x5e, 0x30, 0x82, 0x02, 0x5a, 0x02, 0x01,
+ 0x00, 0x02, 0x81, 0x81, 0x00, 0x9e, 0x06, 0x3e, 0x47, 0x85, 0xb2, 0x34, 0x37, 0xaa, 0x85, 0x47,
+ 0xac, 0x03, 0x24, 0x83, 0xb5, 0x9c, 0xa8, 0x05, 0x3a, 0x24, 0x1e, 0xeb, 0x89, 0x01, 0xbb, 0xe9,
+ 0x9b, 0xb2, 0xc3, 0x22, 0xac, 0x68, 0xe3, 0xf0, 0x6c, 0x02, 0xce, 0x68, 0xa6, 0xc4, 0xd0, 0xa7,
+ 0x06, 0x90, 0x9c, 0xaa, 0x1b, 0x08, 0x1d, 0x8b, 0x43, 0x9a, 0x33, 0x67, 0x44, 0x6d, 0x21, 0xa3,
+ 0x1b, 0x88, 0x9a, 0x97, 0x5e, 0x59, 0xc4, 0x15, 0x0b, 0xd9, 0x2c, 0xbd, 0x51, 0x07, 0x61, 0x82,
+ 0xad, 0xc1, 0xb8, 0xd7, 0xbf, 0x9b, 0xcf, 0x7d, 0x24, 0xc2, 0x63, 0xf3, 0x97, 0x17, 0xeb, 0xfe,
+ 0x62, 0x25, 0xba, 0x5b, 0x4d, 0x8a, 0xc2, 0x7a, 0xbd, 0x43, 0x8a, 0x8f, 0xb8, 0xf2, 0xf1, 0xc5,
+ 0x6a, 0x30, 0xd3, 0x50, 0x8c, 0xc8, 0x9a, 0xdf, 0xef, 0xed, 0x35, 0xe7, 0x7a, 0x62, 0xea, 0x76,
+ 0x7c, 0xbb, 0x08, 0x26, 0xc7, 0x02, 0x01, 0x11, 0x02, 0x81, 0x80, 0x04, 0xa5, 0xd4, 0xa7, 0xc0,
+ 0x2a, 0xe3, 0x6b, 0x0c, 0x8b, 0x73, 0x0c, 0x96, 0xae, 0x40, 0x1b, 0xee, 0x04, 0xf1, 0x18, 0x4c,
+ 0x5b, 0x43, 0x29, 0xad, 0x3a, 0x3b, 0x93, 0xa3, 0x60, 0x17, 0x9b, 0xa8, 0xbb, 0x68, 0xf4, 0x1e,
+ 0x33, 0x3f, 0x50, 0x32, 0xf7, 0x13, 0xf8, 0xa9, 0xe6, 0x7d, 0x79, 0x44, 0x00, 0xde, 0x72, 0xed,
+ 0xf2, 0x73, 0xfa, 0x7b, 0xae, 0x2a, 0x71, 0xc0, 0x40, 0xc8, 0x37, 0x6f, 0x38, 0xb2, 0x69, 0x1f,
+ 0xa8, 0x83, 0x7b, 0x42, 0x00, 0x73, 0x46, 0xe6, 0x4c, 0x91, 0x7f, 0x13, 0x06, 0x69, 0x06, 0xd8,
+ 0x3f, 0x22, 0x15, 0x75, 0xf6, 0xde, 0xcd, 0xb0, 0xbc, 0x66, 0x61, 0x91, 0x08, 0x9b, 0x2b, 0xb2,
+ 0x00, 0xa9, 0x67, 0x05, 0x39, 0x40, 0xb9, 0x37, 0x85, 0x88, 0x4f, 0x76, 0x79, 0x63, 0xc0, 0x88,
+ 0x3c, 0x86, 0xa8, 0x12, 0x94, 0x5f, 0xe4, 0x36, 0x3d, 0xea, 0xb9, 0x02, 0x41, 0x00, 0xb6, 0x2e,
+ 0xbb, 0xcd, 0x2f, 0x3a, 0x99, 0xe0, 0xa1, 0xa5, 0x44, 0x77, 0xea, 0x0b, 0xbe, 0x16, 0x95, 0x0e,
+ 0x64, 0xa7, 0x68, 0xd7, 0x4b, 0x15, 0x15, 0x23, 0xe2, 0x1e, 0x4e, 0x00, 0x2c, 0x22, 0x97, 0xae,
+ 0xb0, 0x74, 0xa6, 0x99, 0xd0, 0x5d, 0xb7, 0x1b, 0x10, 0x34, 0x13, 0xd2, 0x5f, 0x6e, 0x56, 0xad,
+ 0x85, 0x4a, 0xdb, 0xf0, 0x78, 0xbd, 0xf4, 0x8c, 0xb7, 0x9a, 0x3e, 0x99, 0xef, 0xb9, 0x02, 0x41,
+ 0x00, 0xde, 0x0d, 0xa7, 0x48, 0x75, 0x90, 0xad, 0x11, 0xa1, 0xac, 0xee, 0xcb, 0x41, 0x81, 0xc6,
+ 0xc8, 0x7f, 0xe7, 0x25, 0x94, 0xa1, 0x2a, 0x21, 0xa8, 0x57, 0xfe, 0x84, 0xf2, 0x5e, 0xb4, 0x96,
+ 0x35, 0xaf, 0xef, 0x2e, 0x7a, 0xf8, 0xda, 0x3f, 0xac, 0x8a, 0x3c, 0x1c, 0x9c, 0xbd, 0x44, 0xd6,
+ 0x90, 0xb5, 0xce, 0x1b, 0x12, 0xf9, 0x3b, 0x8c, 0x69, 0xf6, 0xa9, 0x02, 0x93, 0x48, 0x35, 0x0a,
+ 0x7f, 0x02, 0x40, 0x6b, 0x2a, 0x8c, 0x96, 0xd0, 0x7c, 0xd2, 0xfc, 0x9b, 0x52, 0x28, 0x46, 0x89,
+ 0xac, 0x8d, 0xef, 0x2a, 0x80, 0xef, 0xea, 0x01, 0x6f, 0x95, 0x93, 0xee, 0x51, 0x57, 0xd5, 0x97,
+ 0x4b, 0x65, 0x41, 0x86, 0x66, 0xc2, 0x26, 0x80, 0x1e, 0x3e, 0x55, 0x3e, 0x88, 0x63, 0xe2, 0x66,
+ 0x03, 0x47, 0x31, 0xd8, 0xa2, 0x4e, 0x68, 0x45, 0x24, 0x0a, 0xca, 0x17, 0x61, 0xd5, 0x69, 0xca,
+ 0x78, 0xab, 0x21, 0x02, 0x41, 0x00, 0x8f, 0xae, 0x7b, 0x4d, 0x00, 0xc7, 0x06, 0x92, 0xf0, 0x24,
+ 0x9a, 0x83, 0x84, 0xbd, 0x62, 0x81, 0xbc, 0x2c, 0x27, 0x60, 0x2c, 0x0c, 0x33, 0xe5, 0x66, 0x1d,
+ 0x28, 0xd9, 0x10, 0x1a, 0x7f, 0x4f, 0xea, 0x4f, 0x78, 0x6d, 0xb0, 0x14, 0xbf, 0xc9, 0xff, 0x17,
+ 0xd6, 0x47, 0x4d, 0x4a, 0xa8, 0xf4, 0x39, 0x67, 0x3e, 0xb1, 0xec, 0x8f, 0xf1, 0x71, 0xbd, 0xb8,
+ 0xa7, 0x50, 0x3d, 0xc7, 0xf7, 0xbb, 0x02, 0x40, 0x0d, 0x85, 0x32, 0x73, 0x9f, 0x0a, 0x33, 0x2f,
+ 0x4b, 0xa2, 0xbd, 0xd1, 0xb1, 0x42, 0xf0, 0x72, 0xa8, 0x7a, 0xc8, 0x15, 0x37, 0x1b, 0xde, 0x76,
+ 0x70, 0xce, 0xfd, 0x69, 0x20, 0x00, 0x4d, 0xc9, 0x4f, 0x35, 0x6f, 0xd1, 0x35, 0xa1, 0x04, 0x95,
+ 0x30, 0xe8, 0x3b, 0xd5, 0x03, 0x5a, 0x50, 0x21, 0x6d, 0xa0, 0x84, 0x39, 0xe9, 0x2e, 0x1e, 0xfc,
+ 0xe4, 0x82, 0x43, 0x20, 0x46, 0x7d, 0x0a, 0xb6
+};
+
+BOOST_FIXTURE_TEST_CASE(Encode, DataSigningKeyFixture)
+{
+ // manual data packet creation for now
+
+ Data d(Name("/local/ndn/prefix"));
+ d.setContentType(tlv::ContentType_Blob);
+ d.setFreshnessPeriod(10_s);
+ d.setContent(CONTENT1, sizeof(CONTENT1));
+
+ Block signatureInfo(tlv::SignatureInfo);
+ // SignatureType
+ signatureInfo.push_back(makeNonNegativeIntegerBlock(tlv::SignatureType, tlv::SignatureSha256WithRsa));
+ // KeyLocator
+ {
+ KeyLocator keyLocator;
+ keyLocator.setName("/test/key/locator");
+ signatureInfo.push_back(keyLocator.wireEncode());
+ }
+ signatureInfo.encode();
+
+ // SignatureValue
+ OBufferStream os;
+ tlv::writeVarNumber(os, tlv::SignatureValue);
+
+ OBufferStream sig;
+ {
+ namespace tr = security::transform;
+
+ tr::StepSource input;
+ input >> tr::signerFilter(DigestAlgorithm::SHA256, m_privKey) >> tr::streamSink(sig);
+
+ input.write(d.getName(). wireEncode().wire(), d.getName(). wireEncode().size());
+ input.write(d.getMetaInfo().wireEncode().wire(), d.getMetaInfo().wireEncode().size());
+ input.write(d.getContent(). wire(), d.getContent(). size());
+ input.write(signatureInfo. wire(), signatureInfo. size());
+ input.end();
+ }
+ auto buf = sig.buf();
+ tlv::writeVarNumber(os, buf->size());
+ os.write(buf->get<char>(), buf->size());
+
+ Block signatureValue(os.buf());
+ Signature signature(signatureInfo, signatureValue);
+ d.setSignature(signature);
+
+ Block dataBlock(d.wireEncode());
+ BOOST_CHECK_EQUAL_COLLECTIONS(DATA1, DATA1 + sizeof(DATA1),
+ dataBlock.begin(), dataBlock.end());
+}
+
+BOOST_FIXTURE_TEST_CASE(Decode02, DataSigningKeyFixture)
+{
+ Block dataBlock(DATA1, sizeof(DATA1));
+ Data d(dataBlock);
+
+ BOOST_CHECK_EQUAL(d.getName().toUri(), "/local/ndn/prefix");
+ BOOST_CHECK_EQUAL(d.getContentType(), static_cast<uint32_t>(tlv::ContentType_Blob));
+ BOOST_CHECK_EQUAL(d.getFreshnessPeriod(), 10_s);
+ BOOST_CHECK_EQUAL(std::string(reinterpret_cast<const char*>(d.getContent().value()),
+ d.getContent().value_size()), "SUCCESS!");
+ BOOST_CHECK_EQUAL(d.getSignature().getType(), tlv::SignatureSha256WithRsa);
+
+ Block block = d.getSignature().getInfo();
+ block.parse();
+ KeyLocator keyLocator(block.get(tlv::KeyLocator));
+ BOOST_CHECK_EQUAL(keyLocator.getName().toUri(), "/test/key/locator");
+
+ BOOST_CHECK(security::verifySignature(d, m_pubKey));
+}
+
+class Decode03Fixture
+{
+protected:
+ Decode03Fixture()
+ {
+ // initialize all elements to non-empty, to verify wireDecode clears them
+ d.setName("/A");
+ d.setContentType(tlv::ContentType_Key);
+ d.setContent("1504C0C1C2C3"_block);
+ d.setSignature(Signature("160A 1B0101 1C050703080142"_block,
+ "1780 B48F1707A3BCA3CFC5F32DE51D9B46C32D7D262A21544EBDA88C3B415D637503"
+ "FC9BEF20F88202A56AF9831E0D30205FD4154B08502BCDEE860267A5C3E03D8E"
+ "A6CB74BE391C01E0A57B991B4404FC11B7D777F1B700A4B65F201118CF1840A8"
+ "30A2A7C17DB4B7A8777E58515121AF9E2498627F8475414CDFD9801B8152AD5B"_block));
+ }
+
+protected:
+ Data d;
+};
+
+BOOST_FIXTURE_TEST_SUITE(Decode03, Decode03Fixture)
+
+BOOST_AUTO_TEST_CASE(MinimalNoSigValue)
+{
+ d.wireDecode("0607 0700 16031B0100"_block);
+ BOOST_CHECK_EQUAL(d.getName(), "/"); // empty Name is allowed in Data
+ BOOST_CHECK_EQUAL(d.getMetaInfo(), MetaInfo());
+ BOOST_CHECK_EQUAL(d.getContent().value_size(), 0);
+ BOOST_CHECK_EQUAL(d.getSignature().getType(), tlv::DigestSha256);
+ BOOST_CHECK_EQUAL(d.getSignature().getValue().value_size(), 0);
+}
+
+BOOST_AUTO_TEST_CASE(Minimal)
+{
+ d.wireDecode("062C 0703080144 16031B0100 "
+ "1720612A79399E60304A9F701C1ECAC7956BF2F1B046E6C6F0D6C29B3FE3A29BAD76"_block);
+ BOOST_CHECK_EQUAL(d.getName(), "/D");
+ BOOST_CHECK_EQUAL(d.getMetaInfo(), MetaInfo());
+ BOOST_CHECK_EQUAL(d.getContent().value_size(), 0);
+ BOOST_CHECK_EQUAL(d.getSignature().getType(), tlv::DigestSha256);
+ BOOST_CHECK_EQUAL(d.getSignature().getValue().value_size(), 32);
+
+ // encode without modification: retain original wire encoding
+ BOOST_CHECK_EQUAL(d.wireEncode().value_size(), 44);
+
+ // modify then re-encode as v0.2 format
+ d.setName("/E");
+ BOOST_CHECK_EQUAL(d.wireEncode(),
+ "0630 0703080145 1400 1500 16031B0100 "
+ "1720612A79399E60304A9F701C1ECAC7956BF2F1B046E6C6F0D6C29B3FE3A29BAD76"_block);
+}
+
+BOOST_AUTO_TEST_CASE(Full)
+{
+ d.wireDecode("063A 0703080144 FC00 1400 FC00 1500 FC00 16031B0100 FC00 "
+ "1720612A79399E60304A9F701C1ECAC7956BF2F1B046E6C6F0D6C29B3FE3A29BAD76 FC00"_block);
+ BOOST_CHECK_EQUAL(d.getName(), "/D");
+ BOOST_CHECK_EQUAL(d.getMetaInfo(), MetaInfo());
+ BOOST_CHECK_EQUAL(d.getContent().value_size(), 0);
+ BOOST_CHECK_EQUAL(d.getSignature().getType(), tlv::DigestSha256);
+ BOOST_CHECK_EQUAL(d.getSignature().getValue().value_size(), 32);
+
+ // encode without modification: retain original wire encoding
+ BOOST_CHECK_EQUAL(d.wireEncode().value_size(), 58);
+
+ // modify then re-encode as v0.2 format
+ d.setName("/E");
+ BOOST_CHECK_EQUAL(d.wireEncode(),
+ "0630 0703080145 1400 1500 16031B0100 "
+ "1720612A79399E60304A9F701C1ECAC7956BF2F1B046E6C6F0D6C29B3FE3A29BAD76"_block);
+}
+
+BOOST_AUTO_TEST_CASE(CriticalElementOutOfOrder)
+{
+ BOOST_CHECK_THROW(d.wireDecode(
+ "0630 1400 0703080145 1500 16031B0100 "
+ "1720612A79399E60304A9F701C1ECAC7956BF2F1B046E6C6F0D6C29B3FE3A29BAD76"_block),
+ tlv::Error);
+ BOOST_CHECK_THROW(d.wireDecode(
+ "0630 0703080145 1500 1400 16031B0100 "
+ "1720612A79399E60304A9F701C1ECAC7956BF2F1B046E6C6F0D6C29B3FE3A29BAD76"_block),
+ tlv::Error);
+ BOOST_CHECK_THROW(d.wireDecode(
+ "0630 0703080145 1400 16031B0100 1500 "
+ "1720612A79399E60304A9F701C1ECAC7956BF2F1B046E6C6F0D6C29B3FE3A29BAD76"_block),
+ tlv::Error);
+ BOOST_CHECK_THROW(d.wireDecode(
+ "0630 0703080145 1400 1500 "
+ "1720612A79399E60304A9F701C1ECAC7956BF2F1B046E6C6F0D6C29B3FE3A29BAD76 16031B0100"_block),
+ tlv::Error);
+ BOOST_CHECK_THROW(d.wireDecode(
+ "0652 0703080145 1400 1500 16031B0100 "
+ "1720612A79399E60304A9F701C1ECAC7956BF2F1B046E6C6F0D6C29B3FE3A29BAD76"
+ "1720612A79399E60304A9F701C1ECAC7956BF2F1B046E6C6F0D6C29B3FE3A29BAD76"_block),
+ tlv::Error);
+}
+
+BOOST_AUTO_TEST_CASE(NameMissing)
+{
+ BOOST_CHECK_THROW(d.wireDecode("0605 16031B0100"_block), tlv::Error);
+}
+
+BOOST_AUTO_TEST_CASE(SigInfoMissing)
+{
+ BOOST_CHECK_THROW(d.wireDecode("0605 0703080144"_block), tlv::Error);
+}
+
+BOOST_AUTO_TEST_CASE(UnrecognizedNonCriticalElementBeforeName)
+{
+ BOOST_CHECK_THROW(d.wireDecode(
+ "062F FC00 0703080144 16031B0100 "
+ "1720612A79399E60304A9F701C1ECAC7956BF2F1B046E6C6F0D6C29B3FE3A29BAD76"_block),
+ tlv::Error);
+}
+
+BOOST_AUTO_TEST_CASE(UnrecognizedCriticalElement)
+{
+ BOOST_CHECK_THROW(d.wireDecode(
+ "0632 0703080145 FB00 1400 1500 16031B0100 "
+ "1720612A79399E60304A9F701C1ECAC7956BF2F1B046E6C6F0D6C29B3FE3A29BAD76"_block),
+ tlv::Error);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // Decode03
+
+BOOST_FIXTURE_TEST_CASE(FullName, IdentityManagementFixture)
+{
+ Data d(Name("/local/ndn/prefix"));
+ d.setContentType(tlv::ContentType_Blob);
+ d.setFreshnessPeriod(10_s);
+ d.setContent(CONTENT1, sizeof(CONTENT1));
+ BOOST_CHECK_THROW(d.getFullName(), Data::Error); // FullName is unavailable without signing
+
+ m_keyChain.sign(d);
+ BOOST_CHECK_EQUAL(d.hasWire(), true);
+ Name fullName = d.getFullName(); // FullName is available after signing
+
+ BOOST_CHECK_EQUAL(d.getName().size() + 1, fullName.size());
+ BOOST_CHECK_EQUAL_COLLECTIONS(d.getName().begin(), d.getName().end(),
+ fullName.begin(), fullName.end() - 1);
+ BOOST_CHECK_EQUAL(fullName.get(-1).value_size(), util::Sha256::DIGEST_SIZE);
+
+ // FullName should be cached, so value() pointer points to same memory location
+ BOOST_CHECK_EQUAL(fullName.get(-1).value(), d.getFullName().get(-1).value());
+
+ d.setFreshnessPeriod(100_s); // invalidates FullName
+ BOOST_CHECK_THROW(d.getFullName(), Data::Error);
+
+ Data d1(Block(DATA1, sizeof(DATA1)));
+ BOOST_CHECK_EQUAL(d1.getFullName(),
+ "/local/ndn/prefix/"
+ "sha256digest=28bad4b5275bd392dbb670c75cf0b66f13f7942b21e80f55c0e86b374753a548");
+}
+
+// ---- operators ----
+
+BOOST_AUTO_TEST_CASE(Equality)
+{
+ Data a;
+ Data b;
+ BOOST_CHECK_EQUAL(a == b, true);
+ BOOST_CHECK_EQUAL(a != b, false);
+
+ a.setName("ndn:/A");
+ BOOST_CHECK_EQUAL(a == b, false);
+ BOOST_CHECK_EQUAL(a != b, true);
+
+ b.setName("ndn:/B");
+ BOOST_CHECK_EQUAL(a == b, false);
+ BOOST_CHECK_EQUAL(a != b, true);
+
+ b.setName("ndn:/A");
+ BOOST_CHECK_EQUAL(a == b, true);
+ BOOST_CHECK_EQUAL(a != b, false);
+
+ a.setFreshnessPeriod(10_s);
+ BOOST_CHECK_EQUAL(a == b, false);
+ BOOST_CHECK_EQUAL(a != b, true);
+
+ b.setFreshnessPeriod(10_s);
+ BOOST_CHECK_EQUAL(a == b, true);
+ BOOST_CHECK_EQUAL(a != b, false);
+
+ static const uint8_t someData[] = "someData";
+ a.setContent(someData, sizeof(someData));
+ BOOST_CHECK_EQUAL(a == b, false);
+ BOOST_CHECK_EQUAL(a != b, true);
+
+ b.setContent(someData, sizeof(someData));
+ BOOST_CHECK_EQUAL(a == b, true);
+ BOOST_CHECK_EQUAL(a != b, false);
+
+ a.setSignature(SignatureSha256WithRsa());
+ BOOST_CHECK_EQUAL(a == b, false);
+ BOOST_CHECK_EQUAL(a != b, true);
+
+ b.setSignature(SignatureSha256WithRsa());
+ BOOST_CHECK_EQUAL(a == b, true);
+ BOOST_CHECK_EQUAL(a != b, false);
+}
+
+BOOST_AUTO_TEST_CASE(Print)
+{
+ Data d(Block(DATA1, sizeof(DATA1)));
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(d),
+ "Name: /local/ndn/prefix\n"
+ "MetaInfo: ContentType: 0, FreshnessPeriod: 10000 milliseconds\n"
+ "Content: (size: 8)\n"
+ "Signature: (type: SignatureSha256WithRsa, value_length: 128)\n");
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestData
+
+} // namespace tests
+} // namespace ndn
diff --git a/tests/unit/delegation-list.t.cpp b/tests/unit/delegation-list.t.cpp
new file mode 100644
index 0000000..e2aada8
--- /dev/null
+++ b/tests/unit/delegation-list.t.cpp
@@ -0,0 +1,370 @@
+/* -*- 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 "delegation-list.hpp"
+
+#include "boost-test.hpp"
+#include <boost/lexical_cast.hpp>
+
+namespace ndn {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(TestDelegationList)
+
+const uint8_t DEL1A[] = {
+ 0x1f, 0x08, // Delegation
+ 0x1e, 0x01, 0x01, // Preference=1
+ 0x07, 0x03, 0x08, 0x01, 0x41 // Name=/A
+};
+const uint8_t DEL1B[] = {
+ 0x1f, 0x08, // Delegation
+ 0x1e, 0x01, 0x01, // Preference=1
+ 0x07, 0x03, 0x08, 0x01, 0x42 // Name=/B
+};
+const uint8_t DEL2A[] = {
+ 0x1f, 0x08, // Delegation
+ 0x1e, 0x01, 0x02, // Preference=2
+ 0x07, 0x03, 0x08, 0x01, 0x41 // Name=/A
+};
+const uint8_t DEL2B[] = {
+ 0x1f, 0x08, // Delegation
+ 0x1e, 0x01, 0x02, // Preference=2
+ 0x07, 0x03, 0x08, 0x01, 0x42 // Name=/B
+};
+
+Block
+makeDelegationListBlock(uint32_t type, std::initializer_list<const uint8_t*> dels)
+{
+ Block block(type);
+ for (const uint8_t* del : dels) {
+ block.push_back(Block(del, 2 + del[1]));
+ }
+ block.encode();
+ return block;
+}
+
+BOOST_AUTO_TEST_SUITE(Decode)
+
+BOOST_AUTO_TEST_CASE(DecodeUnsorted)
+{
+ DelegationList dl(makeDelegationListBlock(tlv::ForwardingHint, {DEL2A, DEL2B, DEL1A}), false);
+ BOOST_CHECK_EQUAL(dl.size(), 3);
+ BOOST_CHECK_EQUAL(dl.at(0).preference, 2);
+ BOOST_CHECK_EQUAL(dl.at(0).name, "/A");
+ BOOST_CHECK_EQUAL(dl.at(1).preference, 2);
+ BOOST_CHECK_EQUAL(dl.at(1).name, "/B");
+ BOOST_CHECK_EQUAL(dl.at(2).preference, 1);
+ BOOST_CHECK_EQUAL(dl.at(2).name, "/A");
+}
+
+BOOST_AUTO_TEST_CASE(DecodeSorted)
+{
+ DelegationList dl(makeDelegationListBlock(tlv::Content, {DEL2A, DEL2B, DEL1A}));
+ BOOST_CHECK_EQUAL(dl.size(), 3);
+ BOOST_CHECK_EQUAL(dl.at(0).preference, 1);
+ BOOST_CHECK_EQUAL(dl.at(0).name, "/A");
+ BOOST_CHECK_EQUAL(dl.at(1).preference, 2);
+ BOOST_CHECK_EQUAL(dl.at(1).name, "/A");
+ BOOST_CHECK_EQUAL(dl.at(2).preference, 2);
+ BOOST_CHECK_EQUAL(dl.at(2).name, "/B");
+}
+
+BOOST_AUTO_TEST_CASE(DecodeEmpty)
+{
+ DelegationList dl;
+ Block block = makeDelegationListBlock(tlv::ForwardingHint, {});
+ BOOST_CHECK_THROW(dl.wireDecode(block), DelegationList::Error);
+}
+
+BOOST_AUTO_TEST_CASE(DecodeBadType)
+{
+ DelegationList dl;
+ Block block = makeDelegationListBlock(tlv::Selectors, {DEL1A, DEL2B});
+ BOOST_CHECK_THROW(dl.wireDecode(block), DelegationList::Error);
+}
+
+BOOST_AUTO_TEST_CASE(DecodeNotDelegation)
+{
+ const uint8_t BAD_DEL[] = {
+ 0x09, 0x00 // Selectors
+ };
+
+ DelegationList dl;
+ Block block = makeDelegationListBlock(tlv::ForwardingHint, {DEL1A, BAD_DEL});
+ BOOST_CHECK_THROW(dl.wireDecode(block), DelegationList::Error);
+}
+
+BOOST_AUTO_TEST_CASE(DecodeMissingPreference)
+{
+ const uint8_t BAD_DEL[] = {
+ 0x1f, 0x05, // Delegation
+ 0x07, 0x03, 0x08, 0x01, 0x42 // Name=/B
+ };
+
+ DelegationList dl;
+ Block block = makeDelegationListBlock(tlv::ForwardingHint, {DEL1A, BAD_DEL});
+ BOOST_CHECK_THROW(dl.wireDecode(block), DelegationList::Error);
+}
+
+BOOST_AUTO_TEST_CASE(DecodeMissingName)
+{
+ const uint8_t BAD_DEL[] = {
+ 0x1f, 0x03, // Delegation
+ 0x1e, 0x01, 0x02, // Preference=2
+ };
+
+ DelegationList dl;
+ Block block = makeDelegationListBlock(tlv::ForwardingHint, {DEL1A, BAD_DEL});
+ BOOST_CHECK_THROW(dl.wireDecode(block), DelegationList::Error);
+}
+
+BOOST_AUTO_TEST_CASE(DecodeUnknownField)
+{
+ const uint8_t BAD_DEL[] = {
+ 0x1f, 0x0a, // Delegation
+ 0x1e, 0x01, 0x02, // Preference=2
+ 0x09, 0x00, // Selectors
+ 0x07, 0x03, 0x08, 0x01, 0x42 // Name=/B
+ };
+
+ DelegationList dl;
+ Block block = makeDelegationListBlock(tlv::ForwardingHint, {DEL1A, BAD_DEL});
+ BOOST_CHECK_THROW(dl.wireDecode(block), DelegationList::Error);
+}
+
+BOOST_AUTO_TEST_CASE(DecodeWrongOrder)
+{
+ const uint8_t BAD_DEL[] = {
+ 0x1f, 0x08, // Delegation
+ 0x07, 0x03, 0x08, 0x01, 0x42, // Name=/B
+ 0x1e, 0x01, 0x02 // Preference=2
+ };
+
+ DelegationList dl;
+ Block block = makeDelegationListBlock(tlv::ForwardingHint, {DEL1A, BAD_DEL});
+ BOOST_CHECK_THROW(dl.wireDecode(block), DelegationList::Error);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // Decode
+
+BOOST_AUTO_TEST_SUITE(InsertEncode)
+
+BOOST_AUTO_TEST_CASE(InsertSimple)
+{
+ DelegationList dl;
+ BOOST_CHECK_EQUAL(dl.empty(), true);
+ dl.insert(2, "/A");
+ BOOST_CHECK_EQUAL(dl.empty(), false);
+ dl.insert(1, "/B");
+ BOOST_CHECK_EQUAL(dl.size(), 2);
+
+ EncodingBuffer encoder;
+ dl.wireEncode(encoder);
+ BOOST_CHECK_EQUAL(encoder.block(), makeDelegationListBlock(tlv::ForwardingHint, {DEL1B, DEL2A}));
+}
+
+BOOST_AUTO_TEST_CASE(InsertReplace)
+{
+ DelegationList dl({{2, "/A"}});
+ dl.insert(Delegation{1, "/A"}, DelegationList::INS_REPLACE);
+ BOOST_CHECK_EQUAL(dl.size(), 1);
+ BOOST_CHECK_EQUAL(dl.at(0).preference, 1);
+ BOOST_CHECK_EQUAL(dl[0].name, "/A");
+
+ EncodingBuffer encoder;
+ dl.wireEncode(encoder);
+ BOOST_CHECK_EQUAL(encoder.block(), makeDelegationListBlock(tlv::ForwardingHint, {DEL1A}));
+}
+
+BOOST_AUTO_TEST_CASE(InsertAppend)
+{
+ DelegationList dl({{2, "/A"}});
+ dl.insert(Delegation{1, "/A"}, DelegationList::INS_APPEND);
+ BOOST_CHECK_EQUAL(dl.size(), 2);
+ BOOST_CHECK_EQUAL(dl.at(0).preference, 1);
+ BOOST_CHECK_EQUAL(dl.at(1).preference, 2);
+
+ EncodingBuffer encoder;
+ dl.wireEncode(encoder);
+ BOOST_CHECK_EQUAL(encoder.block(), makeDelegationListBlock(tlv::ForwardingHint, {DEL1A, DEL2A}));
+}
+
+BOOST_AUTO_TEST_CASE(InsertSkip)
+{
+ DelegationList dl({{2, "/A"}});
+ dl.insert(Delegation{1, "/A"}, DelegationList::INS_SKIP);
+ BOOST_CHECK_EQUAL(dl.size(), 1);
+ BOOST_CHECK_EQUAL(dl.at(0).preference, 2);
+
+ EncodingBuffer encoder;
+ dl.wireEncode(encoder);
+ BOOST_CHECK_EQUAL(encoder.block(), makeDelegationListBlock(tlv::ForwardingHint, {DEL2A}));
+}
+
+BOOST_AUTO_TEST_CASE(Unsorted)
+{
+ DelegationList dl(makeDelegationListBlock(tlv::ForwardingHint, {DEL2A}), false);
+ dl.insert(1, "/B");
+ BOOST_CHECK_EQUAL(dl.size(), 2);
+ BOOST_CHECK_EQUAL(dl.at(0).preference, 2);
+ BOOST_CHECK_EQUAL(dl.at(0).name, "/A");
+ BOOST_CHECK_EQUAL(dl.at(1).preference, 1);
+ BOOST_CHECK_EQUAL(dl.at(1).name, "/B");
+
+ EncodingBuffer encoder;
+ dl.wireEncode(encoder, tlv::Content);
+ BOOST_CHECK_EQUAL(encoder.block(), makeDelegationListBlock(tlv::Content, {DEL2A, DEL1B}));
+}
+
+BOOST_AUTO_TEST_CASE(EncodeBadType)
+{
+ DelegationList dl(makeDelegationListBlock(tlv::ForwardingHint, {DEL2A}));
+ EncodingBuffer encoder;
+ BOOST_CHECK_THROW(dl.wireEncode(encoder, tlv::Selectors), std::invalid_argument);
+}
+
+BOOST_AUTO_TEST_CASE(EncodeEmpty)
+{
+ DelegationList dl;
+ EncodingBuffer encoder;
+ BOOST_CHECK_THROW(dl.wireEncode(encoder), DelegationList::Error);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // InsertEncode
+
+BOOST_AUTO_TEST_SUITE(Erase)
+
+BOOST_AUTO_TEST_CASE(EraseNoop)
+{
+ DelegationList dl;
+ dl.insert(1, "/A");
+ BOOST_CHECK_EQUAL(dl.erase(2, "/A"), 0);
+ BOOST_CHECK_EQUAL(dl.erase(Delegation{1, "/B"}), 0);
+ BOOST_CHECK_EQUAL(dl.size(), 1);
+ BOOST_CHECK_EQUAL(dl.at(0).preference, 1);
+ BOOST_CHECK_EQUAL(dl.at(0).name, "/A");
+}
+
+BOOST_AUTO_TEST_CASE(EraseOne)
+{
+ DelegationList dl;
+ dl.insert(1, "/A");
+ BOOST_CHECK_EQUAL(dl.erase(1, "/A"), 1);
+ BOOST_CHECK_EQUAL(dl.size(), 0);
+}
+
+BOOST_AUTO_TEST_CASE(EraseByName)
+{
+ DelegationList dl;
+ dl.insert(1, "/A");
+ dl.insert(2, "/A", DelegationList::INS_APPEND);
+ BOOST_CHECK_EQUAL(dl.size(), 2);
+ BOOST_CHECK_EQUAL(dl.erase("/A"), 2);
+ BOOST_CHECK_EQUAL(dl.size(), 0);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // Erase
+
+BOOST_AUTO_TEST_SUITE(Sort)
+
+BOOST_AUTO_TEST_CASE(Noop)
+{
+ DelegationList dl(makeDelegationListBlock(tlv::ForwardingHint, {DEL1A}));
+ BOOST_CHECK_EQUAL(dl.isSorted(), true);
+ dl.sort();
+ BOOST_CHECK_EQUAL(dl.isSorted(), true);
+}
+
+BOOST_AUTO_TEST_CASE(Sort)
+{
+ DelegationList dl(makeDelegationListBlock(tlv::ForwardingHint, {DEL2A, DEL2B, DEL1A}), false);
+ BOOST_CHECK_EQUAL(dl.isSorted(), false);
+ dl.sort();
+ BOOST_CHECK_EQUAL(dl.isSorted(), true);
+ BOOST_CHECK_EQUAL(dl.size(), 3);
+ BOOST_CHECK_EQUAL(dl.at(0).preference, 1);
+ BOOST_CHECK_EQUAL(dl.at(0).name, "/A");
+ BOOST_CHECK_EQUAL(dl.at(1).preference, 2);
+ BOOST_CHECK_EQUAL(dl.at(1).name, "/A");
+ BOOST_CHECK_EQUAL(dl.at(2).preference, 2);
+ BOOST_CHECK_EQUAL(dl.at(2).name, "/B");
+}
+
+BOOST_AUTO_TEST_SUITE_END() // Sort
+
+BOOST_AUTO_TEST_SUITE(Compare)
+
+BOOST_AUTO_TEST_CASE(Empty)
+{
+ DelegationList dl1, dl2;
+ BOOST_CHECK_EQUAL(dl1, dl2);
+}
+
+BOOST_AUTO_TEST_CASE(SortedEqual)
+{
+ DelegationList dl1(makeDelegationListBlock(tlv::ForwardingHint, {DEL2A, DEL1B})),
+ dl2(makeDelegationListBlock(tlv::Content, {DEL1B, DEL2A}));
+ BOOST_CHECK_EQUAL(dl1, dl2);
+}
+
+BOOST_AUTO_TEST_CASE(SortedUnequal)
+{
+ DelegationList dl1(makeDelegationListBlock(tlv::ForwardingHint, {DEL2A, DEL1B})),
+ dl2(makeDelegationListBlock(tlv::Content, {DEL1A, DEL2B}));
+ BOOST_CHECK_NE(dl1, dl2);
+}
+
+BOOST_AUTO_TEST_CASE(UnsortedSameOrder)
+{
+ DelegationList dl1(makeDelegationListBlock(tlv::ForwardingHint, {DEL2A, DEL1B}), false),
+ dl2(makeDelegationListBlock(tlv::Content, {DEL2A, DEL1B}), false);
+ BOOST_CHECK_EQUAL(dl1, dl2);
+}
+
+BOOST_AUTO_TEST_CASE(UnsortedDifferentOrder)
+{
+ DelegationList dl1(makeDelegationListBlock(tlv::ForwardingHint, {DEL2A, DEL1B}), false),
+ dl2(makeDelegationListBlock(tlv::Content, {DEL1B, DEL2A}), false);
+ BOOST_CHECK_NE(dl1, dl2);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // Compare
+
+BOOST_AUTO_TEST_SUITE(Print)
+
+BOOST_AUTO_TEST_CASE(PrintEmpty)
+{
+ DelegationList dl;
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(dl), "[]");
+}
+
+BOOST_AUTO_TEST_CASE(PrintNormal)
+{
+ DelegationList dl(makeDelegationListBlock(tlv::ForwardingHint, {DEL2A, DEL1B}));
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(dl), "[/B(1),/A(2)]");
+}
+
+BOOST_AUTO_TEST_SUITE_END() // Print
+
+BOOST_AUTO_TEST_SUITE_END() // TestDelegationList
+
+} // namespace tests
+} // namespace ndn
diff --git a/tests/unit/delegation.t.cpp b/tests/unit/delegation.t.cpp
new file mode 100644
index 0000000..6cf03dd
--- /dev/null
+++ b/tests/unit/delegation.t.cpp
@@ -0,0 +1,64 @@
+/* -*- 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 "delegation.hpp"
+
+#include "boost-test.hpp"
+#include <boost/lexical_cast.hpp>
+
+namespace ndn {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(TestDelegation)
+
+BOOST_AUTO_TEST_CASE(Compare)
+{
+ BOOST_CHECK_EQUAL((Delegation{1, "/A"}), (Delegation{1, "/A"}));
+ BOOST_CHECK_LE((Delegation{1, "/A"}), (Delegation{1, "/A"}));
+ BOOST_CHECK_GE((Delegation{1, "/A"}), (Delegation{1, "/A"}));
+
+ BOOST_CHECK_NE((Delegation{1, "/A"}), (Delegation{2, "/A"}));
+ BOOST_CHECK_NE((Delegation{1, "/A"}), (Delegation{1, "/B"}));
+
+ BOOST_CHECK_LT((Delegation{1, "/A"}), (Delegation{1, "/B"}));
+ BOOST_CHECK_LE((Delegation{1, "/A"}), (Delegation{1, "/B"}));
+ BOOST_CHECK_LT((Delegation{1, "/B"}), (Delegation{2, "/A"}));
+ BOOST_CHECK_LE((Delegation{1, "/B"}), (Delegation{2, "/A"}));
+ BOOST_CHECK_LT((Delegation{1, "/A"}), (Delegation{2, "/A"}));
+ BOOST_CHECK_LE((Delegation{1, "/A"}), (Delegation{2, "/A"}));
+
+ BOOST_CHECK_GT((Delegation{1, "/B"}), (Delegation{1, "/A"}));
+ BOOST_CHECK_GE((Delegation{1, "/B"}), (Delegation{1, "/A"}));
+ BOOST_CHECK_GT((Delegation{2, "/A"}), (Delegation{1, "/B"}));
+ BOOST_CHECK_GE((Delegation{2, "/A"}), (Delegation{1, "/B"}));
+ BOOST_CHECK_GT((Delegation{2, "/A"}), (Delegation{1, "/A"}));
+ BOOST_CHECK_GE((Delegation{2, "/A"}), (Delegation{1, "/A"}));
+}
+
+BOOST_AUTO_TEST_CASE(Print)
+{
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(Delegation{1, "/B"}), "/B(1)");
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestDelegation
+
+} // namespace tests
+} // namespace ndn
diff --git a/tests/unit/encoding/block-helpers.t.cpp b/tests/unit/encoding/block-helpers.t.cpp
new file mode 100644
index 0000000..5b9711d
--- /dev/null
+++ b/tests/unit/encoding/block-helpers.t.cpp
@@ -0,0 +1,139 @@
+/* -*- 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 "encoding/block-helpers.hpp"
+#include "name.hpp"
+
+#include "boost-test.hpp"
+
+namespace ndn {
+namespace encoding {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Encoding)
+BOOST_AUTO_TEST_SUITE(TestBlockHelpers)
+
+enum E8 : uint8_t
+{
+ E8_NONE
+};
+
+enum class EC8 : uint8_t
+{
+ NONE
+};
+
+enum E16 : uint16_t
+{
+ E16_NONE
+};
+
+enum class EC16 : uint16_t
+{
+ NONE
+};
+
+BOOST_AUTO_TEST_CASE(NonNegativeInteger)
+{
+ Block b = makeNonNegativeIntegerBlock(100, 1000);
+ BOOST_CHECK_EQUAL(b.type(), 100);
+ BOOST_CHECK_GT(b.value_size(), 0);
+ BOOST_CHECK_EQUAL(readNonNegativeInteger(b), 1000);
+
+ BOOST_CHECK_THROW(readNonNegativeInteger(Block()), tlv::Error);
+
+ BOOST_CHECK_THROW(readNonNegativeIntegerAs<uint8_t>(b), tlv::Error);
+ BOOST_CHECK_EQUAL(readNonNegativeIntegerAs<uint16_t>(b), 1000);
+ BOOST_CHECK_EQUAL(readNonNegativeIntegerAs<size_t>(b), 1000);
+ BOOST_CHECK_THROW(readNonNegativeIntegerAs<E8>(b), tlv::Error);
+ BOOST_CHECK_EQUAL(static_cast<uint16_t>(readNonNegativeIntegerAs<E16>(b)), 1000);
+ BOOST_CHECK_THROW(readNonNegativeIntegerAs<EC8>(b), tlv::Error);
+ BOOST_CHECK_EQUAL(static_cast<uint16_t>(readNonNegativeIntegerAs<EC16>(b)), 1000);
+}
+
+BOOST_AUTO_TEST_CASE(Empty)
+{
+ Block b = makeEmptyBlock(200);
+ BOOST_CHECK_EQUAL(b.type(), 200);
+ BOOST_CHECK_EQUAL(b.value_size(), 0);
+}
+
+BOOST_AUTO_TEST_CASE(String)
+{
+ Block b = makeStringBlock(100, "Hello, world!");
+ BOOST_CHECK_EQUAL(b.type(), 100);
+ BOOST_CHECK_GT(b.value_size(), 0);
+ BOOST_CHECK_EQUAL(readString(b), "Hello, world!");
+}
+
+BOOST_AUTO_TEST_CASE(Double)
+{
+ const double f = 0.25;
+ Block b = makeDoubleBlock(100, f);
+ BOOST_CHECK_EQUAL(b, "64083FD0000000000000"_block);
+
+ EncodingEstimator estimator;
+ size_t totalLength = prependDoubleBlock(estimator, 100, f);
+ EncodingBuffer encoder(totalLength, 0);
+ prependDoubleBlock(encoder, 100, f);
+ BOOST_CHECK_EQUAL(encoder.block(), b);
+
+ BOOST_CHECK_EQUAL(readDouble(b), f);
+ BOOST_CHECK_THROW(readDouble("4200"_block), tlv::Error);
+ BOOST_CHECK_THROW(readDouble("64043E800000"_block), tlv::Error);
+}
+
+BOOST_AUTO_TEST_CASE(Data)
+{
+ std::string buf1{1, 1, 1, 1};
+ const uint8_t buf2[]{1, 1, 1, 1};
+ std::list<uint8_t> buf3{1, 1, 1, 1};
+
+ Block b1 = makeBinaryBlock(100, buf1.data(), buf1.size());
+ Block b2 = makeBinaryBlock(100, buf2, sizeof(buf2));
+ Block b3 = makeBinaryBlock(100, buf1.begin(), buf1.end()); // fast encoding (random access iterator)
+ Block b4 = makeBinaryBlock(100, buf3.begin(), buf3.end()); // slow encoding (general iterator)
+
+ BOOST_CHECK_EQUAL(b1, b2);
+ BOOST_CHECK_EQUAL(b1, b3);
+ BOOST_CHECK_EQUAL(b1.type(), 100);
+ BOOST_CHECK_EQUAL(b1.value_size(), buf1.size());
+ BOOST_CHECK_EQUAL_COLLECTIONS(b1.value_begin(), b1.value_end(), buf2, buf2 + sizeof(buf2));
+}
+
+BOOST_AUTO_TEST_CASE(Nested)
+{
+ Name name("ndn:/Hello/World!");
+ Block b1 = makeNestedBlock(100, name);
+
+ BOOST_CHECK_EQUAL(b1.type(), 100);
+ b1.parse();
+ BOOST_CHECK_EQUAL(b1.elements().size(), 1);
+ BOOST_CHECK_EQUAL(b1.elements().begin()->type(), name.wireEncode().type());
+ BOOST_CHECK_EQUAL(*b1.elements().begin(), name.wireEncode());
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestBlockHelpers
+BOOST_AUTO_TEST_SUITE_END() // Encoding
+
+} // namespace tests
+} // namespace encoding
+} // namespace ndn
diff --git a/tests/unit/encoding/block.t.cpp b/tests/unit/encoding/block.t.cpp
new file mode 100644
index 0000000..a3459e7
--- /dev/null
+++ b/tests/unit/encoding/block.t.cpp
@@ -0,0 +1,571 @@
+/* -*- 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 "encoding/block.hpp"
+#include "encoding/block-helpers.hpp"
+
+#include "boost-test.hpp"
+#include <boost/lexical_cast.hpp>
+#include <cstring>
+#include <sstream>
+
+namespace ndn {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Encoding)
+BOOST_AUTO_TEST_SUITE(TestBlock)
+
+BOOST_AUTO_TEST_SUITE(Construction)
+
+static const uint8_t TEST_BUFFER[] = {
+ 0x00, 0x01, 0xfa, // ok
+ 0x01, 0x01, 0xfb, // ok
+ 0x03, 0x02, 0xff // bad: TLV-LENGTH is 2 but there's only 1-octet TLV-VALUE
+};
+
+BOOST_AUTO_TEST_CASE(Empty)
+{
+ Block b;
+ BOOST_CHECK_EQUAL(b.empty(), true);
+}
+
+BOOST_AUTO_TEST_CASE(FromEncodingBuffer)
+{
+ const uint8_t VALUE[4] = {0x11, 0x12, 0x13, 0x14};
+
+ EncodingBuffer encoder;
+ size_t length = encoder.prependByteArray(VALUE, sizeof(VALUE));
+ encoder.prependVarNumber(length);
+ encoder.prependVarNumber(0xe0);
+
+ Block b = encoder.block();
+ BOOST_CHECK_EQUAL(b.type(), 0xe0);
+ BOOST_CHECK_EQUAL(b.value_size(), sizeof(VALUE));
+ BOOST_CHECK_EQUAL_COLLECTIONS(b.value_begin(), b.value_end(),
+ VALUE, VALUE + sizeof(VALUE));
+
+ b = Block(encoder);
+ BOOST_CHECK_EQUAL(b.type(), 0xe0);
+ BOOST_CHECK_EQUAL(b.value_size(), sizeof(VALUE));
+ BOOST_CHECK_EQUAL_COLLECTIONS(b.value_begin(), b.value_end(),
+ VALUE, VALUE + sizeof(VALUE));
+}
+
+BOOST_AUTO_TEST_CASE(FromEmptyEncodingBuffer)
+{
+ EncodingBuffer encoder;
+ Block b;
+ BOOST_CHECK_THROW(b = Block(encoder), tlv::Error);
+}
+
+BOOST_AUTO_TEST_CASE(FromBlock)
+{
+ static uint8_t buffer[] = {0x80, 0x06, 0x81, 0x01, 0x01, 0x82, 0x01, 0x01};
+ Block block(buffer, sizeof(buffer));
+
+ Block derivedBlock(block, block.begin(), block.end());
+ BOOST_CHECK_EQUAL(derivedBlock.wire(), block.wire()); // pointers should match
+ BOOST_CHECK(derivedBlock == block); // blocks should match
+
+ derivedBlock = Block(block, block.begin() + 2, block.begin() + 5);
+ BOOST_CHECK(derivedBlock.begin() == block.begin() + 2);
+ BOOST_CHECK(derivedBlock == Block(buffer + 2, 3));
+
+ Buffer otherBuffer(buffer, sizeof(buffer));
+ BOOST_CHECK_THROW(Block(block, otherBuffer.begin(), block.end()), std::invalid_argument);
+ BOOST_CHECK_THROW(Block(block, block.begin(), otherBuffer.end()), std::invalid_argument);
+ BOOST_CHECK_THROW(Block(block, otherBuffer.begin(), otherBuffer.end()), std::invalid_argument);
+}
+
+BOOST_AUTO_TEST_CASE(FromBlockCopyOnWriteModifyOriginal)
+{
+ const uint8_t BUFFER[] = {
+ 0x05, 0x0b, 0x07, 0x03, 0x01, 0x02, 0x03, 0x0a, 0x04, 0x04, 0x05, 0x06, 0x07,
+ };
+
+ Block b1(BUFFER, sizeof(BUFFER));
+
+ Block b2(b1, b1.begin(), b1.end());
+ auto buf2 = b2.getBuffer();
+
+ b1.parse();
+ b1.remove(tlv::Name);
+ b1.encode();
+
+ b2.parse();
+
+ BOOST_CHECK_EQUAL_COLLECTIONS(b2.begin(), b2.end(), BUFFER, BUFFER + sizeof(BUFFER));
+ BOOST_CHECK_EQUAL(buf2, b2.getBuffer());
+}
+
+BOOST_AUTO_TEST_CASE(FromBlockCopyOnWriteModifyCopy)
+{
+ const uint8_t BUFFER[] = {
+ 0x05, 0x0b, 0x07, 0x03, 0x01, 0x02, 0x03, 0x0a, 0x04, 0x04, 0x05, 0x06, 0x07,
+ };
+
+ Block b1(BUFFER, sizeof(BUFFER));
+ auto buf1 = b1.getBuffer();
+
+ Block b2(b1, b1.begin(), b1.end());
+
+ b2.parse();
+ b2.remove(tlv::Name);
+ b2.encode();
+
+ b1.parse();
+ BOOST_CHECK_EQUAL_COLLECTIONS(b1.begin(), b1.end(), BUFFER, BUFFER + sizeof(BUFFER));
+ BOOST_CHECK_EQUAL(buf1, b1.getBuffer());
+}
+
+BOOST_AUTO_TEST_CASE(FromType)
+{
+ Block b1(4);
+ BOOST_CHECK_EQUAL(b1.empty(), false);
+ BOOST_CHECK_EQUAL(b1.type(), 4);
+ BOOST_CHECK_EQUAL(b1.size(), 2); // 1-octet TLV-TYPE and 1-octet TLV-LENGTH
+ BOOST_CHECK_EQUAL(b1.value_size(), 0);
+
+ Block b2(258);
+ BOOST_CHECK_EQUAL(b2.type(), 258);
+ BOOST_CHECK_EQUAL(b2.size(), 4); // 3-octet TLV-TYPE and 1-octet TLV-LENGTH
+ BOOST_CHECK_EQUAL(b2.value_size(), 0);
+}
+
+BOOST_AUTO_TEST_CASE(FromStream)
+{
+ std::stringstream stream;
+ stream.write(reinterpret_cast<const char*>(TEST_BUFFER), sizeof(TEST_BUFFER));
+ stream.seekg(0);
+
+ Block b = Block::fromStream(stream);
+ BOOST_CHECK_EQUAL(b.type(), 0);
+ BOOST_CHECK_EQUAL(b.size(), 3);
+ BOOST_CHECK_EQUAL(b.value_size(), 1);
+ BOOST_CHECK_EQUAL(*b.wire(), 0x00);
+ BOOST_CHECK_EQUAL(*b.value(), 0xfa);
+
+ b = Block::fromStream(stream);
+ BOOST_CHECK_EQUAL(b.type(), 1);
+ BOOST_CHECK_EQUAL(b.size(), 3);
+ BOOST_CHECK_EQUAL(b.value_size(), 1);
+ BOOST_CHECK_EQUAL(*b.wire(), 0x01);
+ BOOST_CHECK_EQUAL(*b.value(), 0xfb);
+
+ BOOST_CHECK_THROW(Block::fromStream(stream), tlv::Error);
+}
+
+BOOST_AUTO_TEST_CASE(FromStreamWhitespace) // Bug 2728
+{
+ const uint8_t PACKET[] = {
+ 0x06, 0x20, // Data
+ 0x07, 0x11, // Name
+ 0x08, 0x05, 0x68, 0x65, 0x6c, 0x6c, 0x6f, // GenericNameComponent 'hello'
+ 0x08, 0x01, 0x31, // GenericNameComponent '1'
+ 0x08, 0x05, 0x77, 0x6f, 0x72, 0x6c, 0x64, // GenericNameComponent 'world'
+ 0x14, 0x00, // MetaInfo empty
+ 0x15, 0x00, // Content empty
+ 0x16, 0x05, // SignatureInfo
+ 0x1b, 0x01, 0x01, // SignatureType RSA
+ 0x1c, 0x00, // KeyLocator empty
+ 0x17, 0x00 // SignatureValue empty
+ };
+ // TLV-LENGTH of <Data> is 0x20 which happens to be ASCII whitespace
+
+ std::stringstream stream;
+ stream.write(reinterpret_cast<const char*>(PACKET), sizeof(PACKET));
+ stream.seekg(0);
+
+ Block b = Block::fromStream(stream);
+ BOOST_CHECK_EQUAL(b.type(), 6);
+ BOOST_CHECK_EQUAL(b.value_size(), 32);
+ b.parse();
+}
+
+BOOST_AUTO_TEST_CASE(FromStreamZeroLength)
+{
+ const uint8_t BUFFER[] = {0x70, 0x00,
+ 0x71, 0x03, 0x86, 0x11, 0x24,
+ 0x72, 0x00};
+
+ std::stringstream stream;
+ stream.write(reinterpret_cast<const char*>(BUFFER), sizeof(BUFFER));
+ stream.seekg(0);
+
+ Block b1 = Block::fromStream(stream);
+ BOOST_CHECK_EQUAL(b1.type(), 0x70);
+ BOOST_CHECK_EQUAL(b1.value_size(), 0);
+
+ Block b2 = Block::fromStream(stream);
+ BOOST_CHECK_EQUAL(b2.type(), 0x71);
+ BOOST_CHECK_EQUAL(b2.value_size(), 3);
+ const uint8_t EXPECTED_VALUE2[] = {0x86, 0x11, 0x24};
+ BOOST_CHECK_EQUAL_COLLECTIONS(b2.value_begin(), b2.value_end(),
+ EXPECTED_VALUE2, EXPECTED_VALUE2 + sizeof(EXPECTED_VALUE2));
+
+ Block b3 = Block::fromStream(stream);
+ BOOST_CHECK_EQUAL(b3.type(), 0x72);
+ BOOST_CHECK_EQUAL(b3.value_size(), 0);
+
+ BOOST_CHECK_THROW(Block::fromStream(stream), tlv::Error);
+}
+
+BOOST_AUTO_TEST_CASE(FromStreamPacketTooLarge)
+{
+ const uint8_t BUFFER[] = {0x07, 0xfe, 0x00, 0x01, 0x00, 0x00};
+
+ std::stringstream stream;
+ stream.write(reinterpret_cast<const char*>(BUFFER), sizeof(BUFFER));
+ for (int i = 0; i < 0x10000; ++i) {
+ stream.put('\0');
+ }
+ stream.seekg(0);
+
+ BOOST_CHECK_THROW(Block::fromStream(stream), tlv::Error);
+}
+
+BOOST_AUTO_TEST_CASE(FromWireBuffer)
+{
+ ConstBufferPtr buffer = make_shared<Buffer>(TEST_BUFFER, sizeof(TEST_BUFFER));
+
+ size_t offset = 0;
+ bool isOk = false;
+ Block b;
+ std::tie(isOk, b) = Block::fromBuffer(buffer, offset);
+ BOOST_CHECK(isOk);
+ BOOST_CHECK_EQUAL(b.type(), 0);
+ BOOST_CHECK_EQUAL(b.size(), 3);
+ BOOST_CHECK_EQUAL(b.value_size(), 1);
+ BOOST_CHECK_EQUAL(*b.wire(), 0x00);
+ BOOST_CHECK_EQUAL(*b.value(), 0xfa);
+ offset += b.size();
+
+ std::tie(isOk, b) = Block::fromBuffer(buffer, offset);
+ BOOST_CHECK(isOk);
+ BOOST_CHECK_EQUAL(b.type(), 1);
+ BOOST_CHECK_EQUAL(b.size(), 3);
+ BOOST_CHECK_EQUAL(b.value_size(), 1);
+ BOOST_CHECK_EQUAL(*b.wire(), 0x01);
+ BOOST_CHECK_EQUAL(*b.value(), 0xfb);
+ offset += b.size();
+
+ std::tie(isOk, b) = Block::fromBuffer(buffer, offset);
+ BOOST_CHECK(!isOk);
+}
+
+BOOST_AUTO_TEST_CASE(FromRawBuffer)
+{
+ size_t offset = 0;
+ bool isOk = false;
+ Block b;
+ std::tie(isOk, b) = Block::fromBuffer(TEST_BUFFER + offset, sizeof(TEST_BUFFER) - offset);
+ BOOST_CHECK(isOk);
+ BOOST_CHECK_EQUAL(b.type(), 0);
+ BOOST_CHECK_EQUAL(b.size(), 3);
+ BOOST_CHECK_EQUAL(b.value_size(), 1);
+ BOOST_CHECK_EQUAL(*b.wire(), 0x00);
+ BOOST_CHECK_EQUAL(*b.value(), 0xfa);
+ offset += b.size();
+
+ std::tie(isOk, b) = Block::fromBuffer(TEST_BUFFER + offset, sizeof(TEST_BUFFER) - offset);
+ BOOST_CHECK(isOk);
+ BOOST_CHECK_EQUAL(b.type(), 1);
+ BOOST_CHECK_EQUAL(b.size(), 3);
+ BOOST_CHECK_EQUAL(b.value_size(), 1);
+ BOOST_CHECK_EQUAL(*b.wire(), 0x01);
+ BOOST_CHECK_EQUAL(*b.value(), 0xfb);
+ offset += b.size();
+
+ std::tie(isOk, b) = Block::fromBuffer(TEST_BUFFER + offset, sizeof(TEST_BUFFER) - offset);
+ BOOST_CHECK(!isOk);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // Construction
+
+BOOST_AUTO_TEST_SUITE(SubElements)
+
+BOOST_AUTO_TEST_CASE(Parse)
+{
+ const uint8_t PACKET[] = {
+ 0x06, 0x20, // Data
+ 0x07, 0x11, // Name
+ 0x08, 0x05, 0x68, 0x65, 0x6c, 0x6c, 0x6f, // GenericNameComponent 'hello'
+ 0x08, 0x01, 0x31, // GenericNameComponent '1'
+ 0x08, 0x05, 0x77, 0x6f, 0x72, 0x6c, 0x64, // GenericNameComponent 'world'
+ 0x14, 0x00, // MetaInfo empty
+ 0x15, 0x00, // Content empty
+ 0x16, 0x05, // SignatureInfo
+ 0x1b, 0x01, 0x01, // SignatureType RSA
+ 0x1c, 0x00, // KeyLocator empty
+ 0x17, 0x00 // SignatureValue empty
+ };
+
+ Block data(PACKET, sizeof(PACKET));
+ data.parse();
+
+ BOOST_CHECK_EQUAL(data.elements_size(), 5);
+ BOOST_CHECK_EQUAL(data.elements().at(0).type(), 0x07);
+ BOOST_CHECK_EQUAL(data.elements().at(0).elements().size(), 0); // parse is not recursive
+
+ BOOST_CHECK(data.get(0x15) == data.elements().at(2));
+ BOOST_CHECK_THROW(data.get(0x01), Block::Error);
+
+ BOOST_CHECK(data.find(0x15) == data.elements_begin() + 2);
+ BOOST_CHECK(data.find(0x01) == data.elements_end());
+}
+
+BOOST_AUTO_TEST_CASE(InsertBeginning)
+{
+ Block masterBlock(tlv::Name);
+ Block firstBlock = makeStringBlock(tlv::GenericNameComponent, "firstName");
+ Block secondBlock = makeStringBlock(tlv::GenericNameComponent, "secondName");
+ Block thirdBlock = makeStringBlock(tlv::GenericNameComponent, "thirdName");
+
+ BOOST_CHECK_EQUAL(masterBlock.elements_size(), 0);
+ masterBlock.push_back(secondBlock);
+ masterBlock.push_back(thirdBlock);
+ BOOST_CHECK_EQUAL(masterBlock.elements_size(), 2);
+ Block::element_const_iterator it = masterBlock.find(tlv::GenericNameComponent);
+ BOOST_CHECK_EQUAL(*it == secondBlock, true);
+
+ it = masterBlock.insert(it, firstBlock);
+
+ BOOST_CHECK_EQUAL(masterBlock.elements_size(), 3);
+ BOOST_CHECK_EQUAL(*(it + 1) == secondBlock, true);
+ BOOST_CHECK_EQUAL(*(masterBlock.elements_begin()) == firstBlock, true);
+}
+
+BOOST_AUTO_TEST_CASE(InsertEnd)
+{
+ Block masterBlock(tlv::Name);
+ Block firstBlock = makeStringBlock(tlv::GenericNameComponent, "firstName");
+ Block secondBlock = makeStringBlock(tlv::GenericNameComponent, "secondName");
+ Block thirdBlock = makeStringBlock(tlv::GenericNameComponent, "thirdName");
+
+ BOOST_CHECK_EQUAL(masterBlock.elements_size(), 0);
+ masterBlock.push_back(firstBlock);
+ masterBlock.push_back(secondBlock);
+ BOOST_CHECK_EQUAL(masterBlock.elements_size(), 2);
+ Block::element_const_iterator it = masterBlock.elements_end();
+ BOOST_CHECK_EQUAL(*(it - 1) == secondBlock, true);
+
+ it = masterBlock.insert(it, thirdBlock);
+
+ BOOST_CHECK_EQUAL(masterBlock.elements_size(), 3);
+ BOOST_CHECK_EQUAL(*(it - 1) == secondBlock, true);
+ BOOST_CHECK_EQUAL(*(masterBlock.elements_end() - 1) == thirdBlock, true);
+}
+
+BOOST_AUTO_TEST_CASE(InsertMiddle)
+{
+ Block masterBlock(tlv::Name);
+ Block firstBlock = makeStringBlock(tlv::GenericNameComponent, "firstName");
+ Block secondBlock = makeStringBlock(tlv::GenericNameComponent, "secondName");
+ Block thirdBlock = makeStringBlock(tlv::GenericNameComponent, "thirdName");
+
+ BOOST_CHECK_EQUAL(masterBlock.elements_size(), 0);
+ masterBlock.push_back(firstBlock);
+ masterBlock.push_back(thirdBlock);
+ BOOST_CHECK_EQUAL(masterBlock.elements_size(), 2);
+ Block::element_const_iterator it = masterBlock.find(tlv::GenericNameComponent);
+ BOOST_CHECK_EQUAL(*it == firstBlock, true);
+
+ it = masterBlock.insert(it + 1, secondBlock);
+
+ BOOST_CHECK_EQUAL(*it == secondBlock, true);
+ BOOST_CHECK_EQUAL(*(it + 1) == thirdBlock, true);
+ BOOST_CHECK_EQUAL(*(it - 1) == firstBlock, true);
+}
+
+BOOST_AUTO_TEST_CASE(EraseSingleElement)
+{
+ Block masterBlock(tlv::Name);
+ Block firstBlock = makeStringBlock(tlv::GenericNameComponent, "firstName");
+ Block secondBlock = makeStringBlock(tlv::GenericNameComponent, "secondName");
+ Block thirdBlock = makeStringBlock(tlv::GenericNameComponent, "thirdName");
+
+ BOOST_CHECK_EQUAL(masterBlock.elements_size(), 0);
+ masterBlock.push_back(firstBlock);
+ masterBlock.push_back(secondBlock);
+ masterBlock.push_back(thirdBlock);
+ BOOST_CHECK_EQUAL(masterBlock.elements_size(), 3);
+ Block::element_const_iterator it = masterBlock.find(tlv::GenericNameComponent);
+ it++;
+ BOOST_CHECK_EQUAL(*it == secondBlock, true);
+
+ it = masterBlock.erase(it);
+
+ BOOST_CHECK_EQUAL(masterBlock.elements_size(), 2);
+ BOOST_CHECK_EQUAL(*(it) == thirdBlock, true);
+ BOOST_CHECK_EQUAL(*(it - 1) == firstBlock, true);
+}
+
+BOOST_AUTO_TEST_CASE(EraseRange)
+{
+ Block masterBlock(tlv::Name);
+ Block firstBlock = makeStringBlock(tlv::GenericNameComponent, "firstName");
+ Block secondBlock = makeStringBlock(tlv::GenericNameComponent, "secondName");
+ Block thirdBlock = makeStringBlock(tlv::GenericNameComponent, "thirdName");
+ Block fourthBlock = makeStringBlock(tlv::GenericNameComponent, "fourthName");
+ Block fifthBlock = makeStringBlock(tlv::GenericNameComponent, "fifthName");
+ Block sixthBlock = makeStringBlock(tlv::GenericNameComponent, "sixthName");
+
+ BOOST_CHECK_EQUAL(masterBlock.elements_size(), 0);
+ masterBlock.push_back(firstBlock);
+ masterBlock.push_back(secondBlock);
+ masterBlock.push_back(thirdBlock);
+ masterBlock.push_back(fourthBlock);
+ masterBlock.push_back(fifthBlock);
+ masterBlock.push_back(sixthBlock);
+ BOOST_CHECK_EQUAL(masterBlock.elements_size(), 6);
+ Block::element_const_iterator itStart = masterBlock.find(tlv::GenericNameComponent);
+ itStart++;
+ Block::element_const_iterator itEnd = itStart + 3;
+ BOOST_CHECK_EQUAL(*itStart == secondBlock, true);
+ BOOST_CHECK_EQUAL(*itEnd == fifthBlock, true);
+
+ Block::element_const_iterator newIt = masterBlock.erase(itStart, itEnd);
+
+ BOOST_CHECK_EQUAL(masterBlock.elements_size(), 3);
+ BOOST_CHECK_EQUAL(*(newIt) == fifthBlock, true);
+ BOOST_CHECK_EQUAL(*(newIt - 1) == firstBlock, true);
+}
+
+BOOST_AUTO_TEST_CASE(Remove)
+{
+ Block block(tlv::Data);
+ block.push_back(makeNonNegativeIntegerBlock(tlv::ContentType, 0));
+ block.push_back(makeNonNegativeIntegerBlock(tlv::FreshnessPeriod, 123));
+ block.push_back(makeStringBlock(tlv::Name, "ndn:/test-prefix"));
+ block.push_back(makeNonNegativeIntegerBlock(tlv::ContentType, 2));
+ block.push_back(makeNonNegativeIntegerBlock(tlv::ContentType, 1));
+
+ BOOST_CHECK_EQUAL(5, block.elements_size());
+ BOOST_REQUIRE_NO_THROW(block.remove(tlv::ContentType));
+ BOOST_CHECK_EQUAL(2, block.elements_size());
+
+ Block::element_container elements = block.elements();
+
+ BOOST_CHECK_EQUAL(tlv::FreshnessPeriod, elements[0].type());
+ BOOST_CHECK_EQUAL(123, readNonNegativeInteger(elements[0]));
+ BOOST_CHECK_EQUAL(tlv::Name, elements[1].type());
+ BOOST_CHECK(readString(elements[1]).compare("ndn:/test-prefix") == 0);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // SubElements
+
+BOOST_AUTO_TEST_CASE(Equality)
+{
+ const uint8_t one[] = {0x08, 0x00};
+ Block a(one, sizeof(one));
+ Block b(one, sizeof(one));
+ BOOST_CHECK_EQUAL(a == b, true);
+ BOOST_CHECK_EQUAL(a != b, false);
+
+ const uint8_t two[] = {0x06, 0x00};
+ Block c(two, sizeof(two));
+ Block d(one, sizeof(one));
+ BOOST_CHECK_EQUAL(c == d, false);
+ BOOST_CHECK_EQUAL(c != d, true);
+
+ const uint8_t three[] = {0x06, 0x01, 0xcc};
+ Block e(two, sizeof(two));
+ Block f(three, sizeof(three));
+ BOOST_CHECK_EQUAL(e == f, false);
+ BOOST_CHECK_EQUAL(e != f, true);
+}
+
+BOOST_AUTO_TEST_CASE(Print)
+{
+ // default constructed
+ Block b;
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(b), "[invalid]");
+
+ // zero length
+ b = "0700"_block;
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(b), "7[empty]");
+
+ // unparsed
+ b = "0E10FF7E4E6B3B21C902660F16ED589FCCCC"_block;
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(b),
+ "14[16]=FF7E4E6B3B21C902660F16ED589FCCCC");
+ // set and restore format flags
+ {
+ std::ostringstream oss;
+ oss << std::showbase << std::hex << 0xd23c4 << b << 0x4981e;
+ BOOST_CHECK_EQUAL(oss.str(), "0xd23c414[16]=FF7E4E6B3B21C902660F16ED589FCCCC0x4981e");
+ }
+
+ // parsed
+ b = "FD010808 0502CADD 59024E42"_block;
+ b.parse();
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(b),
+ "264[8]={5[2]=CADD,89[2]=4E42}");
+
+ // parsed then modified: print modified sub-elements
+ b = "FD010808 0502CADD 59024E42"_block;
+ b.parse();
+ b.erase(b.elements_begin());
+ b.push_back("10022386"_block);
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(b),
+ "264[8]={89[2]=4E42,16[2]=2386}");
+}
+
+BOOST_AUTO_TEST_SUITE(BlockLiteral)
+
+BOOST_AUTO_TEST_CASE(Simple)
+{
+ Block b0 = "0000"_block;
+ BOOST_CHECK_EQUAL(b0.type(), 0x00);
+ BOOST_CHECK_EQUAL(b0.value_size(), 0);
+
+ Block b1 = "0101A0"_block;
+ BOOST_CHECK_EQUAL(b1.type(), 0x01);
+ BOOST_REQUIRE_EQUAL(b1.value_size(), 1);
+ BOOST_CHECK_EQUAL(b1.value()[0], 0xA0);
+}
+
+BOOST_AUTO_TEST_CASE(Comment)
+{
+ Block b0 = "a2b0c0d2eBf0G.B 1+"_block;
+ BOOST_CHECK_EQUAL(b0.type(), 0x20);
+ BOOST_REQUIRE_EQUAL(b0.value_size(), 2);
+ BOOST_CHECK_EQUAL(b0.value()[0], 0xB0);
+ BOOST_CHECK_EQUAL(b0.value()[1], 0xB1);
+}
+
+BOOST_AUTO_TEST_CASE(BadInput)
+{
+ BOOST_CHECK_THROW(""_block, std::invalid_argument);
+ BOOST_CHECK_THROW("1"_block, std::invalid_argument);
+ BOOST_CHECK_THROW("333"_block, std::invalid_argument);
+
+ BOOST_CHECK_THROW("0202C0"_block, tlv::Error);
+ BOOST_CHECK_THROW("0201C0C1"_block, tlv::Error);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // BlockLiteral
+
+BOOST_AUTO_TEST_SUITE_END() // TestBlock
+BOOST_AUTO_TEST_SUITE_END() // Encoding
+
+} // namespace tests
+} // namespace ndn
diff --git a/tests/unit/encoding/buffer-stream.t.cpp b/tests/unit/encoding/buffer-stream.t.cpp
new file mode 100644
index 0000000..9c95b2c
--- /dev/null
+++ b/tests/unit/encoding/buffer-stream.t.cpp
@@ -0,0 +1,77 @@
+/* -*- 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 "encoding/buffer-stream.hpp"
+
+#include "boost-test.hpp"
+
+namespace ndn {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Encoding)
+BOOST_AUTO_TEST_SUITE(TestBufferStream)
+
+BOOST_AUTO_TEST_CASE(Empty)
+{
+ OBufferStream os;
+
+ shared_ptr<Buffer> buf = os.buf();
+ BOOST_CHECK_EQUAL(buf->size(), 0);
+}
+
+BOOST_AUTO_TEST_CASE(Put)
+{
+ OBufferStream os;
+ os.put(0x33);
+ os.put(0x44);
+
+ shared_ptr<Buffer> buf = os.buf();
+ BOOST_REQUIRE_EQUAL(buf->size(), 2);
+ BOOST_CHECK_EQUAL(buf->at(0), 0x33);
+ BOOST_CHECK_EQUAL(buf->at(1), 0x44);
+}
+
+BOOST_AUTO_TEST_CASE(Write)
+{
+ OBufferStream os;
+ os.write("\x11\x22", 2);
+
+ shared_ptr<Buffer> buf = os.buf();
+ BOOST_REQUIRE_EQUAL(buf->size(), 2);
+ BOOST_CHECK_EQUAL(buf->at(0), 0x11);
+ BOOST_CHECK_EQUAL(buf->at(1), 0x22);
+}
+
+BOOST_AUTO_TEST_CASE(Destructor) // Bug 3727
+{
+ auto os = make_unique<OBufferStream>();
+ *os << 'x';
+ os.reset(); // should not cause use-after-free
+
+ // avoid "test case [...] did not check any assertions" message from Boost.Test
+ BOOST_CHECK(true);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestBufferStream
+BOOST_AUTO_TEST_SUITE_END() // Encoding
+
+} // namespace tests
+} // namespace ndn
diff --git a/tests/unit/encoding/encoder.t.cpp b/tests/unit/encoding/encoder.t.cpp
new file mode 100644
index 0000000..47cf8e4
--- /dev/null
+++ b/tests/unit/encoding/encoder.t.cpp
@@ -0,0 +1,173 @@
+/* -*- 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 "encoding/encoder.hpp"
+
+#include "boost-test.hpp"
+
+namespace ndn {
+namespace encoding {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Encoding)
+BOOST_AUTO_TEST_SUITE(TestEncoder)
+
+BOOST_AUTO_TEST_CASE(Basic)
+{
+ Encoder e;
+ BOOST_CHECK_GT(e.capacity(), 100);
+
+ Encoder e1(100);
+ BOOST_CHECK_EQUAL(e1.capacity(), 100);
+
+ Encoder e2(100, 100);
+ BOOST_CHECK_EQUAL(e2.capacity(), 100);
+
+ BOOST_CHECK_EQUAL(e.prependByte(1), 1);
+ BOOST_CHECK_EQUAL(e.appendByte(1), 1);
+
+ uint8_t buf1[] = {'t', 'e', 's', 't', '1'};
+ BOOST_CHECK_EQUAL(e1.prependByteArray(buf1, sizeof(buf1)), 5);
+ BOOST_CHECK_EQUAL(e1.appendByteArray(buf1, sizeof(buf1)), 5);
+
+ std::vector<uint8_t> buf2 = {'t', 'e', 's', 't', '2'};
+ BOOST_CHECK_EQUAL(e1.prependRange(buf2.begin(), buf2.end()), 5);
+ BOOST_CHECK_EQUAL(e1.appendRange(buf2.begin(), buf2.end()), 5);
+
+ std::list<uint8_t> buf3 = {'t', 'e', 's', 't', '2'};
+ BOOST_CHECK_EQUAL(e2.prependRange(buf3.begin(), buf3.end()), 5);
+ BOOST_CHECK_EQUAL(e2.appendRange(buf3.begin(), buf3.end()), 5);
+
+ uint8_t expected1[] = {1, 1};
+ BOOST_CHECK_EQUAL_COLLECTIONS(e.buf(), e.buf() + e.size(),
+ expected1, expected1 + sizeof(expected1));
+
+ const Encoder& constE = e;
+ BOOST_CHECK_EQUAL_COLLECTIONS(constE.buf(), constE.buf() + constE.size(),
+ expected1, expected1 + sizeof(expected1));
+
+ uint8_t expected2[] = {'t', 'e', 's', 't', '2',
+ 't', 'e', 's', 't', '1', 't', 'e', 's', 't', '1',
+ 't', 'e', 's', 't', '2'};
+ BOOST_CHECK_EQUAL_COLLECTIONS(e1.begin(), e1.end(),
+ expected2, expected2 + sizeof(expected2));
+ const Encoder& constE1 = e1;
+ BOOST_CHECK_EQUAL_COLLECTIONS(constE1.begin(), constE1.end(),
+ expected2, expected2 + sizeof(expected2));
+
+ BOOST_CHECK_THROW(e1.block(), tlv::Error);
+ BOOST_CHECK_NO_THROW(e1.block(false));
+
+ e1.prependVarNumber(20);
+ e1.prependVarNumber(100);
+
+ BOOST_CHECK_NO_THROW(e1.block());
+}
+
+BOOST_AUTO_TEST_CASE(Tlv)
+{
+ Encoder e;
+
+ BOOST_CHECK_EQUAL(e.prependVarNumber(1), 1);
+ BOOST_CHECK_EQUAL(e.appendVarNumber(1), 1);
+
+ BOOST_CHECK_EQUAL(e.prependVarNumber(252), 1);
+ BOOST_CHECK_EQUAL(e.appendVarNumber(252), 1);
+
+ BOOST_CHECK_EQUAL(e.prependVarNumber(253), 3);
+ BOOST_CHECK_EQUAL(e.appendVarNumber(253), 3);
+
+ BOOST_CHECK_EQUAL(e.prependVarNumber(65536), 5);
+ BOOST_CHECK_EQUAL(e.appendVarNumber(65536), 5);
+
+ BOOST_CHECK_EQUAL(e.prependVarNumber(4294967296LL), 9);
+ BOOST_CHECK_EQUAL(e.appendVarNumber(4294967296LL), 9);
+
+ //
+
+ BOOST_CHECK_EQUAL(e.prependNonNegativeInteger(1), 1);
+ BOOST_CHECK_EQUAL(e.appendNonNegativeInteger(1), 1);
+
+ BOOST_CHECK_EQUAL(e.prependNonNegativeInteger(252), 1);
+ BOOST_CHECK_EQUAL(e.appendNonNegativeInteger(252), 1);
+
+ BOOST_CHECK_EQUAL(e.prependNonNegativeInteger(253), 1);
+ BOOST_CHECK_EQUAL(e.appendNonNegativeInteger(253), 1);
+
+ BOOST_CHECK_EQUAL(e.prependNonNegativeInteger(255), 1);
+ BOOST_CHECK_EQUAL(e.appendNonNegativeInteger(255), 1);
+
+ BOOST_CHECK_EQUAL(e.prependNonNegativeInteger(256), 2);
+ BOOST_CHECK_EQUAL(e.appendNonNegativeInteger(256), 2);
+
+ BOOST_CHECK_EQUAL(e.prependNonNegativeInteger(65535), 2);
+ BOOST_CHECK_EQUAL(e.appendNonNegativeInteger(65535), 2);
+
+ BOOST_CHECK_EQUAL(e.prependNonNegativeInteger(65536), 4);
+ BOOST_CHECK_EQUAL(e.appendNonNegativeInteger(65536), 4);
+
+ BOOST_CHECK_EQUAL(e.prependNonNegativeInteger(4294967296LL), 8);
+ BOOST_CHECK_EQUAL(e.appendNonNegativeInteger(4294967296LL), 8);
+
+ //
+
+ uint8_t buf[] = {0x01, 0x03, 0x00, 0x00, 0x00};
+ Block block1(buf, sizeof(buf));
+
+ BOOST_CHECK_EQUAL(e.prependByteArrayBlock(100, buf, sizeof(buf)), 7);
+ BOOST_CHECK_EQUAL(e.appendByteArrayBlock(100, buf, sizeof(buf)), 7);
+
+ BOOST_CHECK_EQUAL(e.prependBlock(block1), 5);
+ BOOST_CHECK_EQUAL(e.appendBlock(block1), 5);
+
+ Block block2(100, block1);
+
+ BOOST_CHECK_EQUAL(e.prependBlock(block2), 7);
+ BOOST_CHECK_EQUAL(e.appendBlock(block2), 7);
+}
+
+BOOST_AUTO_TEST_CASE(Reserve)
+{
+ Encoder e(100, 0);
+ BOOST_CHECK_EQUAL(e.capacity(), 100);
+
+ e.reserve(100, true);
+ BOOST_CHECK_EQUAL(e.capacity(), 100);
+
+ e.reserve(200, true);
+ BOOST_CHECK_EQUAL(e.capacity(), 200);
+
+ e.reserve(100, false);
+ BOOST_CHECK_EQUAL(e.capacity(), 200);
+
+ e.reserveFront(1000);
+ BOOST_CHECK_GT(e.capacity(), 1000);
+
+ e.reserveBack(1000);
+ BOOST_CHECK_GT(e.capacity(), 2000);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestEncoder
+BOOST_AUTO_TEST_SUITE_END() // Encoding
+
+} // namespace tests
+} // namespace encoding
+} // namespace ndn
diff --git a/tests/unit/encoding/encoding-buffer.t.cpp b/tests/unit/encoding/encoding-buffer.t.cpp
new file mode 100644
index 0000000..646024b
--- /dev/null
+++ b/tests/unit/encoding/encoding-buffer.t.cpp
@@ -0,0 +1,202 @@
+/* -*- 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 "encoding/encoding-buffer.hpp"
+#include "encoding/block.hpp"
+
+#include "boost-test.hpp"
+
+namespace ndn {
+namespace tests {
+
+class BufferEstimatorFixture
+{
+public:
+ EncodingBuffer buffer;
+ EncodingEstimator estimator;
+};
+
+BOOST_AUTO_TEST_SUITE(Encoding)
+BOOST_AUTO_TEST_SUITE(TestEncodingBuffer)
+
+BOOST_AUTO_TEST_CASE(ConstructFromBlock)
+{
+ auto buf = make_shared<Buffer>(10);
+ Block block(0xab, buf);
+ block.encode();
+
+ {
+ EncodingBuffer buffer(block);
+ BOOST_CHECK_EQUAL(buffer.size(), 12);
+ BOOST_CHECK_EQUAL(buffer.capacity(), 12);
+ }
+
+ (*buf)[1] = 0xe0;
+ (*buf)[2] = 2;
+ block = Block(buf, buf->begin() + 1, buf->begin() + 5);
+ BOOST_CHECK_EQUAL(block.type(), 0xe0);
+
+ {
+ EncodingBuffer buffer(block);
+ BOOST_CHECK_EQUAL(buffer.size(), 4);
+ BOOST_CHECK_EQUAL(buffer.capacity(), 10);
+ }
+}
+
+BOOST_FIXTURE_TEST_SUITE(PrependVarNumber, BufferEstimatorFixture)
+
+BOOST_AUTO_TEST_CASE(OneByte1)
+{
+ size_t s1 = buffer.prependVarNumber(252);
+ size_t s2 = estimator.prependVarNumber(252);
+ BOOST_CHECK_EQUAL(buffer.size(), 1);
+ BOOST_CHECK_EQUAL(s1, 1);
+ BOOST_CHECK_EQUAL(s2, 1);
+}
+
+BOOST_AUTO_TEST_CASE(ThreeBytes1)
+{
+ size_t s1 = buffer.prependVarNumber(253);
+ size_t s2 = estimator.prependVarNumber(253);
+ BOOST_CHECK_EQUAL(buffer.size(), 3);
+ BOOST_CHECK_EQUAL(s1, 3);
+ BOOST_CHECK_EQUAL(s2, 3);
+}
+
+BOOST_AUTO_TEST_CASE(ThreeBytes2)
+{
+ size_t s1 = buffer.prependVarNumber(255);
+ size_t s2 = estimator.prependVarNumber(255);
+ BOOST_CHECK_EQUAL(buffer.size(), 3);
+ BOOST_CHECK_EQUAL(s1, 3);
+ BOOST_CHECK_EQUAL(s2, 3);
+}
+
+BOOST_AUTO_TEST_CASE(ThreeBytes3)
+{
+ size_t s1 = buffer.prependVarNumber(65535);
+ size_t s2 = estimator.prependVarNumber(65535);
+ BOOST_CHECK_EQUAL(buffer.size(), 3);
+ BOOST_CHECK_EQUAL(s1, 3);
+ BOOST_CHECK_EQUAL(s2, 3);
+}
+
+BOOST_AUTO_TEST_CASE(FiveBytes1)
+{
+ size_t s1 = buffer.prependVarNumber(65536);
+ size_t s2 = estimator.prependVarNumber(65536);
+ BOOST_CHECK_EQUAL(buffer.size(), 5);
+ BOOST_CHECK_EQUAL(s1, 5);
+ BOOST_CHECK_EQUAL(s2, 5);
+}
+
+BOOST_AUTO_TEST_CASE(FiveBytes2)
+{
+ size_t s1 = buffer.prependVarNumber(4294967295LL);
+ size_t s2 = estimator.prependVarNumber(4294967295LL);
+ BOOST_CHECK_EQUAL(buffer.size(), 5);
+ BOOST_CHECK_EQUAL(s1, 5);
+ BOOST_CHECK_EQUAL(s2, 5);
+}
+
+BOOST_AUTO_TEST_CASE(NineBytes)
+{
+ size_t s1 = buffer.prependVarNumber(4294967296LL);
+ size_t s2 = estimator.prependVarNumber(4294967296LL);
+ BOOST_CHECK_EQUAL(buffer.size(), 9);
+ BOOST_CHECK_EQUAL(s1, 9);
+ BOOST_CHECK_EQUAL(s2, 9);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // PrependVarNumber
+
+BOOST_FIXTURE_TEST_SUITE(PrependNonNegativeNumber, BufferEstimatorFixture)
+
+BOOST_AUTO_TEST_CASE(NonNegativeNumberOneByte1)
+{
+ size_t s1 = buffer.prependNonNegativeInteger(252);
+ size_t s2 = estimator.prependNonNegativeInteger(252);
+ BOOST_CHECK_EQUAL(buffer.size(), 1);
+ BOOST_CHECK_EQUAL(s1, 1);
+ BOOST_CHECK_EQUAL(s2, 1);
+}
+
+BOOST_AUTO_TEST_CASE(NonNegativeNumberOneByte2)
+{
+ size_t s1 = buffer.prependNonNegativeInteger(255);
+ size_t s2 = estimator.prependNonNegativeInteger(255);
+ BOOST_CHECK_EQUAL(buffer.size(), 1);
+ BOOST_CHECK_EQUAL(s1, 1);
+ BOOST_CHECK_EQUAL(s2, 1);
+}
+
+BOOST_AUTO_TEST_CASE(NonNegativeNumberTwoBytes1)
+{
+ size_t s1 = buffer.prependNonNegativeInteger(256);
+ size_t s2 = estimator.prependNonNegativeInteger(256);
+ BOOST_CHECK_EQUAL(buffer.size(), 2);
+ BOOST_CHECK_EQUAL(s1, 2);
+ BOOST_CHECK_EQUAL(s2, 2);
+}
+
+BOOST_AUTO_TEST_CASE(NonNegativeNumberTwoBytes2)
+{
+ size_t s1 = buffer.prependNonNegativeInteger(65535);
+ size_t s2 = estimator.prependNonNegativeInteger(65535);
+ BOOST_CHECK_EQUAL(buffer.size(), 2);
+ BOOST_CHECK_EQUAL(s1, 2);
+ BOOST_CHECK_EQUAL(s2, 2);
+}
+
+BOOST_AUTO_TEST_CASE(NonNegativeNumberFourBytes1)
+{
+ size_t s1 = buffer.prependNonNegativeInteger(65536);
+ size_t s2 = estimator.prependNonNegativeInteger(65536);
+ BOOST_CHECK_EQUAL(buffer.size(), 4);
+ BOOST_CHECK_EQUAL(s1, 4);
+ BOOST_CHECK_EQUAL(s2, 4);
+}
+
+BOOST_AUTO_TEST_CASE(NonNegativeNumberFourBytes2)
+{
+ size_t s1 = buffer.prependNonNegativeInteger(4294967295LL);
+ size_t s2 = estimator.prependNonNegativeInteger(4294967295LL);
+ BOOST_CHECK_EQUAL(buffer.size(), 4);
+ BOOST_CHECK_EQUAL(s1, 4);
+ BOOST_CHECK_EQUAL(s2, 4);
+}
+
+BOOST_AUTO_TEST_CASE(NonNegativeNumberEightBytes)
+{
+ size_t s1 = buffer.prependNonNegativeInteger(4294967296LL);
+ size_t s2 = estimator.prependNonNegativeInteger(4294967296LL);
+ BOOST_CHECK_EQUAL(buffer.size(), 8);
+ BOOST_CHECK_EQUAL(s1, 8);
+ BOOST_CHECK_EQUAL(s2, 8);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // PrependNonNegativeNumber
+
+BOOST_AUTO_TEST_SUITE_END() // TestEncodingBuffer
+BOOST_AUTO_TEST_SUITE_END() // Encoding
+
+} // namespace tests
+} // namespace ndn
diff --git a/tests/unit/encoding/estimator.t.cpp b/tests/unit/encoding/estimator.t.cpp
new file mode 100644
index 0000000..8ab4399
--- /dev/null
+++ b/tests/unit/encoding/estimator.t.cpp
@@ -0,0 +1,120 @@
+/* -*- 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 "encoding/estimator.hpp"
+
+#include "boost-test.hpp"
+
+namespace ndn {
+namespace encoding {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Encoding)
+BOOST_AUTO_TEST_SUITE(TestEstimator)
+
+BOOST_AUTO_TEST_CASE(Basic)
+{
+ Estimator e;
+
+ BOOST_CHECK_EQUAL(e.prependByte(1), 1);
+ BOOST_CHECK_EQUAL(e.appendByte(1), 1);
+
+ uint8_t buf1[] = {'t', 'e', 's', 't', '1'};
+ BOOST_CHECK_EQUAL(e.prependByteArray(buf1, sizeof(buf1)), 5);
+ BOOST_CHECK_EQUAL(e.appendByteArray(buf1, sizeof(buf1)), 5);
+
+ std::vector<uint8_t> buf2 = {'t', 'e', 's', 't', '2'};
+ BOOST_CHECK_EQUAL(e.prependRange(buf2.begin(), buf2.end()), 5);
+ BOOST_CHECK_EQUAL(e.appendRange(buf2.begin(), buf2.end()), 5);
+
+ std::list<uint8_t> buf3 = {'t', 'e', 's', 't', '2'};
+ BOOST_CHECK_EQUAL(e.prependRange(buf3.begin(), buf3.end()), 5);
+ BOOST_CHECK_EQUAL(e.appendRange(buf3.begin(), buf3.end()), 5);
+}
+
+BOOST_AUTO_TEST_CASE(Tlv)
+{
+ Estimator e;
+
+ BOOST_CHECK_EQUAL(e.prependVarNumber(1), 1);
+ BOOST_CHECK_EQUAL(e.appendVarNumber(1), 1);
+
+ BOOST_CHECK_EQUAL(e.prependVarNumber(252), 1);
+ BOOST_CHECK_EQUAL(e.appendVarNumber(252), 1);
+
+ BOOST_CHECK_EQUAL(e.prependVarNumber(253), 3);
+ BOOST_CHECK_EQUAL(e.appendVarNumber(253), 3);
+
+ BOOST_CHECK_EQUAL(e.prependVarNumber(65536), 5);
+ BOOST_CHECK_EQUAL(e.appendVarNumber(65536), 5);
+
+ BOOST_CHECK_EQUAL(e.prependVarNumber(4294967296LL), 9);
+ BOOST_CHECK_EQUAL(e.appendVarNumber(4294967296LL), 9);
+
+ //
+
+ BOOST_CHECK_EQUAL(e.prependNonNegativeInteger(1), 1);
+ BOOST_CHECK_EQUAL(e.appendNonNegativeInteger(1), 1);
+
+ BOOST_CHECK_EQUAL(e.prependNonNegativeInteger(252), 1);
+ BOOST_CHECK_EQUAL(e.appendNonNegativeInteger(252), 1);
+
+ BOOST_CHECK_EQUAL(e.prependNonNegativeInteger(253), 1);
+ BOOST_CHECK_EQUAL(e.appendNonNegativeInteger(253), 1);
+
+ BOOST_CHECK_EQUAL(e.prependNonNegativeInteger(255), 1);
+ BOOST_CHECK_EQUAL(e.appendNonNegativeInteger(255), 1);
+
+ BOOST_CHECK_EQUAL(e.prependNonNegativeInteger(256), 2);
+ BOOST_CHECK_EQUAL(e.appendNonNegativeInteger(256), 2);
+
+ BOOST_CHECK_EQUAL(e.prependNonNegativeInteger(65535), 2);
+ BOOST_CHECK_EQUAL(e.appendNonNegativeInteger(65535), 2);
+
+ BOOST_CHECK_EQUAL(e.prependNonNegativeInteger(65536), 4);
+ BOOST_CHECK_EQUAL(e.appendNonNegativeInteger(65536), 4);
+
+ BOOST_CHECK_EQUAL(e.prependNonNegativeInteger(4294967296LL), 8);
+ BOOST_CHECK_EQUAL(e.appendNonNegativeInteger(4294967296LL), 8);
+
+ //
+
+ uint8_t buf[] = {0x01, 0x03, 0x00, 0x00, 0x00};
+ Block block1(buf, sizeof(buf));
+
+ BOOST_CHECK_EQUAL(e.prependByteArrayBlock(100, buf, sizeof(buf)), 7);
+ BOOST_CHECK_EQUAL(e.appendByteArrayBlock(100, buf, sizeof(buf)), 7);
+
+ BOOST_CHECK_EQUAL(e.prependBlock(block1), 5);
+ BOOST_CHECK_EQUAL(e.appendBlock(block1), 5);
+
+ Block block2(100, block1);
+
+ BOOST_CHECK_EQUAL(e.prependBlock(block2), 7);
+ BOOST_CHECK_EQUAL(e.appendBlock(block2), 7);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestEstimator
+BOOST_AUTO_TEST_SUITE_END() // Encoding
+
+} // namespace tests
+} // namespace encoding
+} // namespace ndn
diff --git a/tests/unit/encoding/nfd-constants.t.cpp b/tests/unit/encoding/nfd-constants.t.cpp
new file mode 100644
index 0000000..af9997b
--- /dev/null
+++ b/tests/unit/encoding/nfd-constants.t.cpp
@@ -0,0 +1,143 @@
+/* -*- 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 "encoding/nfd-constants.hpp"
+
+#include "boost-test.hpp"
+
+#include <boost/lexical_cast.hpp>
+#include <sstream>
+
+namespace ndn {
+namespace nfd {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Encoding)
+BOOST_AUTO_TEST_SUITE(TestNfdConstants)
+
+BOOST_AUTO_TEST_CASE(PrintFaceScope)
+{
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(FACE_SCOPE_NONE), "none");
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(FACE_SCOPE_NON_LOCAL), "non-local");
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(FACE_SCOPE_LOCAL), "local");
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(static_cast<FaceScope>(126)), "126");
+}
+
+BOOST_AUTO_TEST_CASE(PrintFacePersistency)
+{
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(FACE_PERSISTENCY_NONE), "none");
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(FACE_PERSISTENCY_ON_DEMAND), "on-demand");
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(FACE_PERSISTENCY_PERSISTENT), "persistent");
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(FACE_PERSISTENCY_PERMANENT), "permanent");
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(static_cast<FacePersistency>(110)), "110");
+}
+
+BOOST_AUTO_TEST_CASE(PrintLinkType)
+{
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(LINK_TYPE_NONE), "none");
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(LINK_TYPE_POINT_TO_POINT), "point-to-point");
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(LINK_TYPE_MULTI_ACCESS), "multi-access");
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(LINK_TYPE_AD_HOC), "adhoc");
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(static_cast<LinkType>(104)), "104");
+}
+
+BOOST_AUTO_TEST_CASE(PrintFaceEventKind)
+{
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(FACE_EVENT_NONE), "none");
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(FACE_EVENT_CREATED), "created");
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(FACE_EVENT_DESTROYED), "destroyed");
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(FACE_EVENT_UP), "up");
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(FACE_EVENT_DOWN), "down");
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(static_cast<FaceEventKind>(175)), "175");
+}
+
+BOOST_AUTO_TEST_CASE(ParseRouteOrigin)
+{
+ auto expectSuccess = [] (const std::string& input, RouteOrigin expected) {
+ std::istringstream is(input);
+ RouteOrigin routeOrigin;
+ is >> routeOrigin;
+
+ BOOST_TEST_MESSAGE("parsing " << input);
+ BOOST_CHECK_EQUAL(routeOrigin, expected);
+ };
+
+ auto expectFail = [] (const std::string& input) {
+ std::istringstream is(input);
+ RouteOrigin routeOrigin;
+ is >> routeOrigin;
+
+ BOOST_TEST_MESSAGE("parsing " << input);
+ BOOST_CHECK(is.fail());
+ BOOST_CHECK_EQUAL(routeOrigin, ROUTE_ORIGIN_NONE);
+ };
+
+ expectSuccess("none", ROUTE_ORIGIN_NONE);
+ expectSuccess("App", ROUTE_ORIGIN_APP);
+ expectSuccess("AutoReg", ROUTE_ORIGIN_AUTOREG);
+ expectSuccess("Client", ROUTE_ORIGIN_CLIENT);
+ expectSuccess("AutoConf", ROUTE_ORIGIN_AUTOCONF);
+ expectSuccess("NLSR", ROUTE_ORIGIN_NLSR);
+ expectSuccess("PrefixAnn", ROUTE_ORIGIN_PREFIXANN);
+ expectSuccess("static", ROUTE_ORIGIN_STATIC);
+ expectSuccess("27", static_cast<RouteOrigin>(27));
+
+ expectSuccess(" app", ROUTE_ORIGIN_APP);
+ expectSuccess("app ", ROUTE_ORIGIN_APP);
+ expectSuccess(" app ", ROUTE_ORIGIN_APP);
+
+ expectFail("unrecognized");
+ expectFail("-1");
+ expectFail("0.1");
+ expectFail("65537");
+ expectFail("");
+}
+
+BOOST_AUTO_TEST_CASE(PrintRouteOrigin)
+{
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(ROUTE_ORIGIN_NONE), "none");
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(ROUTE_ORIGIN_APP), "app");
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(ROUTE_ORIGIN_AUTOREG), "autoreg");
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(ROUTE_ORIGIN_CLIENT), "client");
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(ROUTE_ORIGIN_AUTOCONF), "autoconf");
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(ROUTE_ORIGIN_NLSR), "nlsr");
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(ROUTE_ORIGIN_PREFIXANN), "prefixann");
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(ROUTE_ORIGIN_STATIC), "static");
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(static_cast<RouteOrigin>(27)), "27");
+}
+
+BOOST_AUTO_TEST_CASE(PrintRouteFlags)
+{
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(ROUTE_FLAGS_NONE), "none");
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(ROUTE_FLAG_CHILD_INHERIT), "child-inherit");
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(ROUTE_FLAG_CAPTURE), "capture");
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(static_cast<RouteFlags>(
+ ROUTE_FLAG_CHILD_INHERIT | ROUTE_FLAG_CAPTURE)), "child-inherit|capture");
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(static_cast<RouteFlags>(
+ ROUTE_FLAG_CAPTURE | 0x9c)), "capture|0x9c");
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestNfdConstants
+BOOST_AUTO_TEST_SUITE_END() // Encoding
+
+} // namespace tests
+} // namespace nfd
+} // namespace ndn
diff --git a/tests/unit/encoding/tlv.t.cpp b/tests/unit/encoding/tlv.t.cpp
new file mode 100644
index 0000000..a82ffbe
--- /dev/null
+++ b/tests/unit/encoding/tlv.t.cpp
@@ -0,0 +1,522 @@
+/* -*- 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 "encoding/tlv.hpp"
+#include "encoding/buffer.hpp"
+
+#include "boost-test.hpp"
+
+#include <array>
+#include <deque>
+#include <list>
+#include <sstream>
+#include <boost/concept_archetype.hpp>
+#include <boost/iostreams/stream.hpp>
+#include <boost/iostreams/device/array.hpp>
+#include <boost/lexical_cast.hpp>
+
+namespace ndn {
+namespace tlv {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Encoding)
+BOOST_AUTO_TEST_SUITE(TestTlv)
+
+BOOST_AUTO_TEST_CASE(CriticalType)
+{
+ BOOST_CHECK_EQUAL(isCriticalType(0), true);
+ BOOST_CHECK_EQUAL(isCriticalType(1), true);
+ BOOST_CHECK_EQUAL(isCriticalType(2), true);
+ BOOST_CHECK_EQUAL(isCriticalType(30), true);
+ BOOST_CHECK_EQUAL(isCriticalType(31), true);
+ BOOST_CHECK_EQUAL(isCriticalType(32), false);
+ BOOST_CHECK_EQUAL(isCriticalType(33), true);
+ BOOST_CHECK_EQUAL(isCriticalType(34), false);
+ BOOST_CHECK_EQUAL(isCriticalType(10000), false);
+ BOOST_CHECK_EQUAL(isCriticalType(10001), true);
+}
+
+using ArrayStream = boost::iostreams::stream<boost::iostreams::array_source>;
+using StreamIterator = std::istream_iterator<uint8_t>;
+
+#define ASSERT_READ_NUMBER_IS_FAST(T) \
+ static_assert(std::is_base_of<detail::ReadNumberFast<T>, detail::ReadNumber<T>>::value, \
+ # T " should use ReadNumberFast")
+#define ASSERT_READ_NUMBER_IS_SLOW(T) \
+ static_assert(std::is_base_of<detail::ReadNumberSlow<T>, detail::ReadNumber<T>>::value, \
+ # T " should use ReadNumberSlow")
+
+ASSERT_READ_NUMBER_IS_FAST(const uint8_t*);
+ASSERT_READ_NUMBER_IS_FAST(uint8_t*);
+ASSERT_READ_NUMBER_IS_FAST(int8_t*);
+ASSERT_READ_NUMBER_IS_FAST(char*);
+ASSERT_READ_NUMBER_IS_FAST(unsigned char*);
+ASSERT_READ_NUMBER_IS_FAST(signed char*);
+ASSERT_READ_NUMBER_IS_FAST(const uint8_t[]);
+ASSERT_READ_NUMBER_IS_FAST(uint8_t[]);
+ASSERT_READ_NUMBER_IS_FAST(const uint8_t[12]);
+ASSERT_READ_NUMBER_IS_FAST(uint8_t[12]);
+using Uint8Array = std::array<uint8_t, 87>;
+ASSERT_READ_NUMBER_IS_FAST(Uint8Array::const_iterator);
+ASSERT_READ_NUMBER_IS_FAST(Uint8Array::iterator);
+using CharArray = std::array<char, 87>;
+ASSERT_READ_NUMBER_IS_FAST(CharArray::iterator);
+ASSERT_READ_NUMBER_IS_FAST(std::string::const_iterator);
+ASSERT_READ_NUMBER_IS_FAST(std::string::iterator);
+ASSERT_READ_NUMBER_IS_FAST(Buffer::const_iterator);
+ASSERT_READ_NUMBER_IS_FAST(Buffer::iterator);
+ASSERT_READ_NUMBER_IS_FAST(std::vector<uint8_t>::const_iterator);
+ASSERT_READ_NUMBER_IS_FAST(std::vector<uint8_t>::iterator);
+ASSERT_READ_NUMBER_IS_FAST(std::vector<int8_t>::iterator);
+ASSERT_READ_NUMBER_IS_FAST(std::vector<char>::iterator);
+ASSERT_READ_NUMBER_IS_FAST(std::vector<unsigned char>::iterator);
+ASSERT_READ_NUMBER_IS_FAST(std::vector<signed char>::iterator);
+ASSERT_READ_NUMBER_IS_SLOW(std::vector<bool>::iterator);
+ASSERT_READ_NUMBER_IS_SLOW(std::vector<uint16_t>::iterator);
+ASSERT_READ_NUMBER_IS_SLOW(std::vector<uint32_t>::iterator);
+ASSERT_READ_NUMBER_IS_SLOW(std::vector<uint64_t>::iterator);
+ASSERT_READ_NUMBER_IS_SLOW(std::deque<uint8_t>::iterator);
+ASSERT_READ_NUMBER_IS_SLOW(std::list<uint8_t>::iterator);
+ASSERT_READ_NUMBER_IS_SLOW(StreamIterator);
+
+BOOST_AUTO_TEST_SUITE(VarNumber)
+
+// This check ensures readVarNumber and readType only require InputIterator concept and nothing
+// more. This function should compile, but should never be executed.
+void
+checkArchetype()
+{
+ boost::input_iterator_archetype<uint8_t> begin, end;
+ uint64_t number = readVarNumber(begin, end);
+ uint32_t type = readType(begin, end);;
+ readVarNumber(begin, end, number);
+ readType(begin, end, type);
+}
+
+static const uint8_t BUFFER[] = {
+ 0x01, // == 1
+ 0xfc, // == 252
+ 0xfd, 0x00, 0xfd, // == 253
+ 0xfe, 0x00, 0x01, 0x00, 0x00, // == 65536
+ 0xff, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00 // == 4294967296
+};
+
+BOOST_AUTO_TEST_CASE(SizeOf)
+{
+ BOOST_CHECK_EQUAL(sizeOfVarNumber(1), 1);
+ BOOST_CHECK_EQUAL(sizeOfVarNumber(252), 1);
+ BOOST_CHECK_EQUAL(sizeOfVarNumber(253), 3);
+ BOOST_CHECK_EQUAL(sizeOfVarNumber(65536), 5);
+ BOOST_CHECK_EQUAL(sizeOfVarNumber(4294967296), 9);
+}
+
+BOOST_AUTO_TEST_CASE(Write)
+{
+ std::ostringstream os;
+
+ writeVarNumber(os, 1);
+ writeVarNumber(os, 252);
+ writeVarNumber(os, 253);
+ writeVarNumber(os, 65536);
+ writeVarNumber(os, 4294967296);
+
+ std::string buffer = os.str();
+ const uint8_t* actual = reinterpret_cast<const uint8_t*>(buffer.c_str());
+
+ BOOST_CHECK_EQUAL(buffer.size(), sizeof(BUFFER));
+ BOOST_CHECK_EQUAL_COLLECTIONS(BUFFER, BUFFER + sizeof(BUFFER),
+ actual, actual + sizeof(BUFFER));
+}
+
+BOOST_AUTO_TEST_CASE(ReadFromBuffer)
+{
+ const uint8_t* begin;
+ uint64_t value;
+
+ begin = BUFFER;
+ BOOST_CHECK_EQUAL(readVarNumber(begin, begin + 1, value), true);
+ begin = BUFFER;
+ BOOST_CHECK_NO_THROW(readVarNumber(begin, begin + 1));
+ BOOST_CHECK_EQUAL(value, 1);
+
+ begin = BUFFER + 1;
+ BOOST_CHECK_EQUAL(readVarNumber(begin, begin + 1, value), true);
+ begin = BUFFER + 1;
+ BOOST_CHECK_NO_THROW(readVarNumber(begin, begin + 1));
+ BOOST_CHECK_EQUAL(value, 252);
+
+ begin = BUFFER + 2;
+ BOOST_CHECK_EQUAL(readVarNumber(begin, begin + 1, value), false);
+ begin = BUFFER + 2;
+ BOOST_CHECK_THROW(readVarNumber(begin, begin + 1), Error);
+
+ begin = BUFFER + 2;
+ BOOST_CHECK_EQUAL(readVarNumber(begin, begin + 2, value), false);
+ begin = BUFFER + 2;
+ BOOST_CHECK_THROW(readVarNumber(begin, begin + 2), Error);
+
+ begin = BUFFER + 2;
+ BOOST_CHECK_EQUAL(readVarNumber(begin, begin + 3, value), true);
+ begin = BUFFER + 2;
+ BOOST_CHECK_NO_THROW(readVarNumber(begin, begin + 3));
+ BOOST_CHECK_EQUAL(value, 253);
+
+
+ begin = BUFFER + 5;
+ BOOST_CHECK_EQUAL(readVarNumber(begin, begin + 1, value), false);
+ begin = BUFFER + 5;
+ BOOST_CHECK_THROW(readVarNumber(begin, begin + 1), Error);
+
+ begin = BUFFER + 5;
+ BOOST_CHECK_EQUAL(readVarNumber(begin, begin + 4, value), false);
+ begin = BUFFER + 5;
+ BOOST_CHECK_THROW(readVarNumber(begin, begin + 4), Error);
+
+ begin = BUFFER + 5;
+ BOOST_CHECK_EQUAL(readVarNumber(begin, begin + 5, value), true);
+ begin = BUFFER + 5;
+ BOOST_CHECK_NO_THROW(readVarNumber(begin, begin + 5));
+ BOOST_CHECK_EQUAL(value, 65536);
+
+ begin = BUFFER + 10;
+ BOOST_CHECK_EQUAL(readVarNumber(begin, begin + 1, value), false);
+ begin = BUFFER + 10;
+ BOOST_CHECK_THROW(readVarNumber(begin, begin + 1), Error);
+
+ begin = BUFFER + 10;
+ BOOST_CHECK_EQUAL(readVarNumber(begin, begin + 8, value), false);
+ begin = BUFFER + 10;
+ BOOST_CHECK_THROW(readVarNumber(begin, begin + 8), Error);
+
+ begin = BUFFER + 10;
+ BOOST_CHECK_EQUAL(readVarNumber(begin, begin + 9, value), true);
+ begin = BUFFER + 10;
+ BOOST_CHECK_NO_THROW(readVarNumber(begin, begin + 9));
+ BOOST_CHECK_EQUAL(value, 4294967296);
+}
+
+BOOST_AUTO_TEST_CASE(ReadFromStream)
+{
+ StreamIterator end; // end of stream
+ uint64_t value;
+ {
+ ArrayStream stream(reinterpret_cast<const char*>(BUFFER), 1);
+ StreamIterator begin(stream);
+ BOOST_CHECK_EQUAL(readVarNumber(begin, end, value), true);
+ }
+ {
+ ArrayStream stream(reinterpret_cast<const char*>(BUFFER), 1);
+ StreamIterator begin(stream);
+ BOOST_CHECK_NO_THROW(readVarNumber(begin, end));
+ BOOST_CHECK_EQUAL(value, 1);
+ }
+
+ {
+ ArrayStream stream(reinterpret_cast<const char*>(BUFFER) + 1, 1);
+ StreamIterator begin(stream);
+ BOOST_CHECK_EQUAL(readVarNumber(begin, end, value), true);
+ }
+ {
+ ArrayStream stream(reinterpret_cast<const char*>(BUFFER) + 1, 1);
+ StreamIterator begin(stream);
+ BOOST_CHECK_NO_THROW(readVarNumber(begin, end));
+ BOOST_CHECK_EQUAL(value, 252);
+ }
+
+ {
+ ArrayStream stream(reinterpret_cast<const char*>(BUFFER) + 2, 1);
+ StreamIterator begin(stream);
+ BOOST_CHECK_EQUAL(readVarNumber(begin, end, value), false);
+ }
+ {
+ ArrayStream stream(reinterpret_cast<const char*>(BUFFER) + 2, 1);
+ StreamIterator begin(stream);
+ BOOST_CHECK_THROW(readVarNumber(begin, end), Error);
+ }
+
+ {
+ ArrayStream stream(reinterpret_cast<const char*>(BUFFER) + 2, 2);
+ StreamIterator begin(stream);
+ BOOST_CHECK_EQUAL(readVarNumber(begin, end, value), false);
+ }
+ {
+ ArrayStream stream(reinterpret_cast<const char*>(BUFFER) + 2, 2);
+ StreamIterator begin(stream);
+ BOOST_CHECK_THROW(readVarNumber(begin, end), Error);
+ }
+
+ {
+ ArrayStream stream(reinterpret_cast<const char*>(BUFFER) + 2, 3);
+ StreamIterator begin(stream);
+ BOOST_CHECK_EQUAL(readVarNumber(begin, end, value), true);
+ }
+ {
+ ArrayStream stream(reinterpret_cast<const char*>(BUFFER) + 2, 3);
+ StreamIterator begin(stream);
+ BOOST_CHECK_NO_THROW(readVarNumber(begin, end));
+ BOOST_CHECK_EQUAL(value, 253);
+ }
+
+ {
+ ArrayStream stream(reinterpret_cast<const char*>(BUFFER) + 5, 1);
+ StreamIterator begin(stream);
+ BOOST_CHECK_EQUAL(readVarNumber(begin, end, value), false);
+ }
+ {
+ ArrayStream stream(reinterpret_cast<const char*>(BUFFER) + 5, 1);
+ StreamIterator begin(stream);
+ BOOST_CHECK_THROW(readVarNumber(begin, end), Error);
+ }
+
+ {
+ ArrayStream stream(reinterpret_cast<const char*>(BUFFER) + 5, 4);
+ StreamIterator begin(stream);
+ BOOST_CHECK_EQUAL(readVarNumber(begin, end, value), false);
+ }
+ {
+ ArrayStream stream(reinterpret_cast<const char*>(BUFFER) + 5, 4);
+ StreamIterator begin(stream);
+ BOOST_CHECK_THROW(readVarNumber(begin, end), Error);
+ }
+
+ {
+ ArrayStream stream(reinterpret_cast<const char*>(BUFFER) + 5, 5);
+ StreamIterator begin(stream);
+ BOOST_CHECK_EQUAL(readVarNumber(begin, end, value), true);
+ }
+ {
+ ArrayStream stream(reinterpret_cast<const char*>(BUFFER) + 5, 5);
+ StreamIterator begin(stream);
+ BOOST_CHECK_NO_THROW(readVarNumber(begin, end));
+ BOOST_CHECK_EQUAL(value, 65536);
+ }
+
+ {
+ ArrayStream stream(reinterpret_cast<const char*>(BUFFER) + 10, 1);
+ StreamIterator begin(stream);
+ BOOST_CHECK_EQUAL(readVarNumber(begin, end, value), false);
+ }
+ {
+ ArrayStream stream(reinterpret_cast<const char*>(BUFFER) + 10, 1);
+ StreamIterator begin(stream);
+ BOOST_CHECK_THROW(readVarNumber(begin, end), Error);
+ }
+
+ {
+ ArrayStream stream(reinterpret_cast<const char*>(BUFFER) + 10, 8);
+ StreamIterator begin(stream);
+ BOOST_CHECK_EQUAL(readVarNumber(begin, end, value), false);
+ }
+ {
+ ArrayStream stream(reinterpret_cast<const char*>(BUFFER) + 10, 8);
+ StreamIterator begin(stream);
+ BOOST_CHECK_THROW(readVarNumber(begin, end), Error);
+ }
+
+ {
+ ArrayStream stream(reinterpret_cast<const char*>(BUFFER) + 10, 9);
+ StreamIterator begin(stream);
+ BOOST_CHECK_EQUAL(readVarNumber(begin, end, value), true);
+ }
+ {
+ ArrayStream stream(reinterpret_cast<const char*>(BUFFER) + 10, 9);
+ StreamIterator begin(stream);
+ BOOST_CHECK_NO_THROW(readVarNumber(begin, end));
+ BOOST_CHECK_EQUAL(value, 4294967296);
+ }
+}
+
+BOOST_AUTO_TEST_SUITE_END() // VarNumber
+
+BOOST_AUTO_TEST_SUITE(NonNegativeInteger)
+
+// This check ensures readNonNegativeInteger only requires InputIterator concept and nothing more.
+// This function should compile, but should never be executed.
+void
+checkArchetype()
+{
+ boost::input_iterator_archetype<uint8_t> begin, end;
+ readNonNegativeInteger(0, begin, end);
+}
+
+static const uint8_t BUFFER[] = {
+ 0x01, // 1
+ 0xff, // 255
+ 0x01, 0x02, // 258
+ 0x01, 0x01, 0x01, 0x02, // 16843010
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02 // 72340172838076674
+};
+
+BOOST_AUTO_TEST_CASE(SizeOf)
+{
+ BOOST_CHECK_EQUAL(sizeOfNonNegativeInteger(1), 1);
+ BOOST_CHECK_EQUAL(sizeOfNonNegativeInteger(253), 1);
+ BOOST_CHECK_EQUAL(sizeOfNonNegativeInteger(255), 1);
+ BOOST_CHECK_EQUAL(sizeOfNonNegativeInteger(256), 2);
+ BOOST_CHECK_EQUAL(sizeOfNonNegativeInteger(65536), 4);
+ BOOST_CHECK_EQUAL(sizeOfNonNegativeInteger(16843009), 4);
+ BOOST_CHECK_EQUAL(sizeOfNonNegativeInteger(4294967296), 8);
+ BOOST_CHECK_EQUAL(sizeOfNonNegativeInteger(72340172838076673), 8);
+}
+
+BOOST_AUTO_TEST_CASE(Write)
+{
+ std::ostringstream os;
+
+ writeNonNegativeInteger(os, 1);
+ writeNonNegativeInteger(os, 255);
+ writeNonNegativeInteger(os, 258);
+ writeNonNegativeInteger(os, 16843010);
+ writeNonNegativeInteger(os, 72340172838076674);
+
+ std::string buffer = os.str();
+ const uint8_t* actual = reinterpret_cast<const uint8_t*>(buffer.c_str());
+
+ BOOST_CHECK_EQUAL_COLLECTIONS(BUFFER, BUFFER + sizeof(BUFFER),
+ actual, actual + sizeof(BUFFER));
+}
+
+BOOST_AUTO_TEST_CASE(ReadFromBuffer)
+{
+ const uint8_t* begin = nullptr;
+
+ begin = BUFFER;
+ BOOST_CHECK_EQUAL(readNonNegativeInteger(1, begin, begin + 1), 1);
+ BOOST_CHECK_EQUAL(begin, BUFFER + 1);
+
+ begin = BUFFER + 1;
+ BOOST_CHECK_EQUAL(readNonNegativeInteger(1, begin, begin + 1), 255);
+ BOOST_CHECK_EQUAL(begin, BUFFER + 2);
+
+ begin = BUFFER + 2;
+ BOOST_CHECK_EQUAL(readNonNegativeInteger(2, begin, begin + 2), 258);
+ BOOST_CHECK_EQUAL(begin, BUFFER + 4);
+
+ begin = BUFFER + 4;
+ BOOST_CHECK_EQUAL(readNonNegativeInteger(4, begin, begin + 4), 16843010);
+ BOOST_CHECK_EQUAL(begin, BUFFER + 8);
+
+ begin = BUFFER + 8;
+ BOOST_CHECK_EQUAL(readNonNegativeInteger(8, begin, begin + 8), 72340172838076674);
+ BOOST_CHECK_EQUAL(begin, BUFFER + 16);
+
+ // invalid size
+ begin = BUFFER;
+ BOOST_CHECK_THROW(readNonNegativeInteger(3, begin, begin + 3), Error);
+
+ // available buffer smaller than size
+ begin = BUFFER;
+ BOOST_CHECK_THROW(readNonNegativeInteger(1, begin, begin + 0), Error);
+ begin = BUFFER;
+ BOOST_CHECK_THROW(readNonNegativeInteger(2, begin, begin + 1), Error);
+ begin = BUFFER;
+ BOOST_CHECK_THROW(readNonNegativeInteger(4, begin, begin + 3), Error);
+ begin = BUFFER;
+ BOOST_CHECK_THROW(readNonNegativeInteger(8, begin, begin + 7), Error);
+}
+
+BOOST_AUTO_TEST_CASE(ReadFromStream)
+{
+ StreamIterator end; // end of stream
+
+ {
+ ArrayStream stream(reinterpret_cast<const char*>(BUFFER), sizeof(BUFFER));
+ StreamIterator begin(stream);
+ BOOST_CHECK_EQUAL(readNonNegativeInteger(1, begin, end), 1);
+ BOOST_CHECK_EQUAL(readNonNegativeInteger(1, begin, end), 255);
+ BOOST_CHECK_EQUAL(readNonNegativeInteger(2, begin, end), 258);
+ BOOST_CHECK_EQUAL(readNonNegativeInteger(4, begin, end), 16843010);
+ BOOST_CHECK_EQUAL(readNonNegativeInteger(8, begin, end), 72340172838076674);
+ BOOST_CHECK(begin == end);
+ }
+
+ // invalid size
+ {
+ ArrayStream stream(reinterpret_cast<const char*>(BUFFER), 3);
+ StreamIterator begin(stream);
+ BOOST_CHECK_THROW(readNonNegativeInteger(3, begin, end), Error);
+ }
+
+ // available buffer smaller than size
+ {
+ ArrayStream stream(reinterpret_cast<const char*>(BUFFER), 0);
+ StreamIterator begin(stream);
+ BOOST_CHECK_THROW(readNonNegativeInteger(1, begin, end), Error);
+ }
+ {
+ ArrayStream stream(reinterpret_cast<const char*>(BUFFER), 1);
+ StreamIterator begin(stream);
+ BOOST_CHECK_THROW(readNonNegativeInteger(2, begin, end), Error);
+ }
+ {
+ ArrayStream stream(reinterpret_cast<const char*>(BUFFER), 3);
+ StreamIterator begin(stream);
+ BOOST_CHECK_THROW(readNonNegativeInteger(4, begin, end), Error);
+ }
+ {
+ ArrayStream stream(reinterpret_cast<const char*>(BUFFER), 7);
+ StreamIterator begin(stream);
+ BOOST_CHECK_THROW(readNonNegativeInteger(8, begin, end), Error);
+ }
+}
+
+BOOST_AUTO_TEST_SUITE_END() // NonNegativeInteger
+
+BOOST_AUTO_TEST_SUITE(PrintHelpers)
+
+BOOST_AUTO_TEST_CASE(PrintSignatureTypeValue)
+{
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(DigestSha256), "DigestSha256");
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(SignatureSha256WithRsa), "SignatureSha256WithRsa");
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(static_cast<SignatureTypeValue>(2)), "Unknown(2)");
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(SignatureSha256WithEcdsa), "SignatureSha256WithEcdsa");
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(SignatureHmacWithSha256), "SignatureHmacWithSha256");
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(static_cast<SignatureTypeValue>(5)), "Unknown(5)");
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(static_cast<SignatureTypeValue>(200)), "Unknown(200)");
+}
+
+BOOST_AUTO_TEST_CASE(PrintContentTypeValue)
+{
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(ContentType_Blob), "Blob");
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(ContentType_Link), "Link");
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(ContentType_Key), "Key");
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(ContentType_Nack), "Nack");
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(ContentType_Manifest), "Manifest");
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(ContentType_PrefixAnn), "PrefixAnn");
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(static_cast<ContentTypeValue>(6)), "Reserved(6)");
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(static_cast<ContentTypeValue>(1023)), "Reserved(1023)");
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(ContentType_Flic), "FLIC");
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(static_cast<ContentTypeValue>(1025)), "Unknown(1025)");
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(static_cast<ContentTypeValue>(8999)), "Unknown(8999)");
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(static_cast<ContentTypeValue>(9000)), "Experimental(9000)");
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(static_cast<ContentTypeValue>(9999)), "Experimental(9999)");
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(static_cast<ContentTypeValue>(10000)), "Unknown(10000)");
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(static_cast<ContentTypeValue>(19910118)), "Unknown(19910118)");
+}
+
+BOOST_AUTO_TEST_SUITE_END() // PrintHelpers
+
+BOOST_AUTO_TEST_SUITE_END() // TestTlv
+BOOST_AUTO_TEST_SUITE_END() // Encoding
+
+} // namespace tests
+} // namespace tlv
+} // namespace ndn
diff --git a/tests/unit/exclude.t.cpp b/tests/unit/exclude.t.cpp
new file mode 100644
index 0000000..da93a85
--- /dev/null
+++ b/tests/unit/exclude.t.cpp
@@ -0,0 +1,568 @@
+/* -*- 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 "exclude.hpp"
+#include "util/sha256.hpp"
+
+#include "boost-test.hpp"
+
+#include <boost/lexical_cast.hpp>
+
+namespace ndn {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(TestExclude)
+
+BOOST_AUTO_TEST_SUITE(GenericComponent) // exclude GenericNameComponent
+
+BOOST_AUTO_TEST_CASE(One)
+{
+ Exclude e;
+ std::vector<Exclude::Range> enumerated;
+
+ e.excludeOne(name::Component("b"));
+ BOOST_CHECK_EQUAL(e.toUri(), "b");
+ BOOST_CHECK_EQUAL(e.size(), 1);
+ enumerated.clear();
+ std::copy(e.begin(), e.end(), std::back_inserter(enumerated));
+ BOOST_REQUIRE_EQUAL(enumerated.size(), 1);
+ BOOST_CHECK_EQUAL(enumerated[0].isSingular(), true);
+ BOOST_CHECK_EQUAL(enumerated[0].from, name::Component("b"));
+
+ e.excludeOne(name::Component("d"));
+ BOOST_CHECK_EQUAL(e.toUri(), "b,d");
+ BOOST_CHECK_EQUAL(e.size(), 2);
+ enumerated.clear();
+ std::copy(e.begin(), e.end(), std::back_inserter(enumerated));
+ BOOST_REQUIRE_EQUAL(enumerated.size(), 2);
+ BOOST_CHECK_EQUAL(enumerated[0].isSingular(), true);
+ BOOST_CHECK_EQUAL(enumerated[0].from, name::Component("b"));
+ BOOST_CHECK_EQUAL(enumerated[1].isSingular(), true);
+ BOOST_CHECK_EQUAL(enumerated[1].from, name::Component("d"));
+
+ e.excludeOne(name::Component("a"));
+ BOOST_CHECK_EQUAL(e.toUri(), "a,b,d");
+ BOOST_CHECK_EQUAL(e.size(), 3);
+ enumerated.clear();
+ std::copy(e.begin(), e.end(), std::back_inserter(enumerated));
+ BOOST_REQUIRE_EQUAL(enumerated.size(), 3);
+ BOOST_CHECK_EQUAL(enumerated[0].isSingular(), true);
+ BOOST_CHECK_EQUAL(enumerated[0].from, name::Component("a"));
+ BOOST_CHECK_EQUAL(enumerated[1].isSingular(), true);
+ BOOST_CHECK_EQUAL(enumerated[1].from, name::Component("b"));
+ BOOST_CHECK_EQUAL(enumerated[2].isSingular(), true);
+ BOOST_CHECK_EQUAL(enumerated[2].from, name::Component("d"));
+
+ e.excludeOne(name::Component("aa"));
+ BOOST_CHECK_EQUAL(e.toUri(), "a,b,d,aa");
+ BOOST_CHECK_EQUAL(e.size(), 4);
+
+ e.excludeOne(name::Component("cc"));
+ BOOST_CHECK_EQUAL(e.toUri(), "a,b,d,aa,cc");
+ BOOST_CHECK_EQUAL(e.size(), 5);
+
+ e.excludeOne(name::Component("c"));
+ BOOST_CHECK_EQUAL(e.toUri(), "a,b,c,d,aa,cc");
+ BOOST_CHECK_EQUAL(e.size(), 6);
+}
+
+BOOST_AUTO_TEST_CASE(Before)
+{
+ // based on https://redmine.named-data.net/issues/1158
+ ndn::Exclude e;
+ BOOST_REQUIRE_NO_THROW(e.excludeBefore(name::Component("PuQxMaf91")));
+
+ BOOST_CHECK_EQUAL(e.toUri(), "*,PuQxMaf91");
+}
+
+BOOST_AUTO_TEST_CASE(Ranges)
+{
+ // example: ANY /b /d ANY /f
+
+ Exclude e;
+ std::vector<Exclude::Range> enumerated;
+
+ e.excludeOne(name::Component("b0"));
+ BOOST_CHECK_EQUAL(e.toUri(), "b0");
+ BOOST_CHECK_EQUAL(e.size(), 1);
+ enumerated.clear();
+ std::copy(e.begin(), e.end(), std::back_inserter(enumerated));
+ BOOST_REQUIRE_EQUAL(enumerated.size(), 1);
+ BOOST_CHECK_EQUAL(enumerated[0].isSingular(), true);
+ BOOST_CHECK_EQUAL(enumerated[0].from, name::Component("b0"));
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(enumerated[0]), "{b0}");
+ BOOST_CHECK_EQUAL(enumerated[0], (Exclude::Range{false, name::Component("b0"), false, name::Component("b0")}));
+ BOOST_CHECK_NE(enumerated[0], (Exclude::Range{false, name::Component("b0"), false, name::Component("b1")}));
+
+ e.excludeBefore(name::Component("b1"));
+ BOOST_CHECK_EQUAL(e.toUri(), "*,b1");
+ BOOST_CHECK_EQUAL(e.size(), 1);
+ enumerated.clear();
+ std::copy(e.begin(), e.end(), std::back_inserter(enumerated));
+ BOOST_REQUIRE_EQUAL(enumerated.size(), 1);
+ BOOST_CHECK_EQUAL(enumerated[0].fromInfinity, true);
+ BOOST_CHECK_EQUAL(enumerated[0].toInfinity, false);
+ BOOST_CHECK_EQUAL(enumerated[0].to, name::Component("b1"));
+
+ e.excludeBefore(name::Component("c0"));
+ BOOST_CHECK_EQUAL(e.toUri(), "*,c0");
+ BOOST_CHECK_EQUAL(e.size(), 1);
+ enumerated.clear();
+ std::copy(e.begin(), e.end(), std::back_inserter(enumerated));
+ BOOST_REQUIRE_EQUAL(enumerated.size(), 1);
+ BOOST_CHECK_EQUAL(enumerated[0].fromInfinity, true);
+ BOOST_CHECK_EQUAL(enumerated[0].toInfinity, false);
+ BOOST_CHECK_EQUAL(enumerated[0].to, name::Component("c0"));
+
+ e.excludeRange(name::Component("a0"), name::Component("c0"));
+ BOOST_CHECK_EQUAL(e.toUri(), "*,c0");
+ BOOST_CHECK_EQUAL(e.size(), 1);
+ enumerated.clear();
+ std::copy(e.begin(), e.end(), std::back_inserter(enumerated));
+ BOOST_REQUIRE_EQUAL(enumerated.size(), 1);
+ BOOST_CHECK_EQUAL(enumerated[0].fromInfinity, true);
+ BOOST_CHECK_EQUAL(enumerated[0].toInfinity, false);
+ BOOST_CHECK_EQUAL(enumerated[0].to, name::Component("c0"));
+
+ e.excludeRange(name::Component("d0"), name::Component("e0"));
+ BOOST_CHECK_EQUAL(e.toUri(), "*,c0,d0,*,e0");
+ BOOST_CHECK_EQUAL(e.size(), 2);
+ enumerated.clear();
+ std::copy(e.begin(), e.end(), std::back_inserter(enumerated));
+ BOOST_REQUIRE_EQUAL(enumerated.size(), 2);
+ BOOST_CHECK_EQUAL(enumerated[0].fromInfinity, true);
+ BOOST_CHECK_EQUAL(enumerated[0].toInfinity, false);
+ BOOST_CHECK_EQUAL(enumerated[0].to, name::Component("c0"));
+ BOOST_CHECK_EQUAL(enumerated[1].fromInfinity, false);
+ BOOST_CHECK_EQUAL(enumerated[1].from, name::Component("d0"));
+ BOOST_CHECK_EQUAL(enumerated[1].toInfinity, false);
+ BOOST_CHECK_EQUAL(enumerated[1].to, name::Component("e0"));
+
+ e.excludeRange(name::Component("c1"), name::Component("d1"));
+ BOOST_CHECK_EQUAL(e.toUri(), "*,c0,c1,*,e0");
+ BOOST_CHECK_EQUAL(e.size(), 2);
+ enumerated.clear();
+ std::copy(e.begin(), e.end(), std::back_inserter(enumerated));
+ BOOST_REQUIRE_EQUAL(enumerated.size(), 2);
+ BOOST_CHECK_EQUAL(enumerated[0].fromInfinity, true);
+ BOOST_CHECK_EQUAL(enumerated[0].toInfinity, false);
+ BOOST_CHECK_EQUAL(enumerated[0].to, name::Component("c0"));
+ BOOST_CHECK_EQUAL(enumerated[1].fromInfinity, false);
+ BOOST_CHECK_EQUAL(enumerated[1].from, name::Component("c1"));
+ BOOST_CHECK_EQUAL(enumerated[1].toInfinity, false);
+ BOOST_CHECK_EQUAL(enumerated[1].to, name::Component("e0"));
+
+ e.excludeRange(name::Component("a1"), name::Component("d1"));
+ BOOST_CHECK_EQUAL(e.toUri(), "*,e0");
+ BOOST_CHECK_EQUAL(e.size(), 1);
+ enumerated.clear();
+ std::copy(e.begin(), e.end(), std::back_inserter(enumerated));
+ BOOST_REQUIRE_EQUAL(enumerated.size(), 1);
+ BOOST_CHECK_EQUAL(enumerated[0].fromInfinity, true);
+ BOOST_CHECK_EQUAL(enumerated[0].toInfinity, false);
+ BOOST_CHECK_EQUAL(enumerated[0].to, name::Component("e0"));
+
+ e.excludeBefore(name::Component("e2"));
+ BOOST_CHECK_EQUAL(e.toUri(), "*,e2");
+ BOOST_CHECK_EQUAL(e.size(), 1);
+ enumerated.clear();
+ std::copy(e.begin(), e.end(), std::back_inserter(enumerated));
+ BOOST_REQUIRE_EQUAL(enumerated.size(), 1);
+ BOOST_CHECK_EQUAL(enumerated[0].fromInfinity, true);
+ BOOST_CHECK_EQUAL(enumerated[0].toInfinity, false);
+ BOOST_CHECK_EQUAL(enumerated[0].to, name::Component("e2"));
+
+ e.excludeAfter(name::Component("f0"));
+ BOOST_CHECK_EQUAL(e.toUri(), "*,e2,f0,*");
+ BOOST_CHECK_EQUAL(e.size(), 2);
+ enumerated.clear();
+ std::copy(e.begin(), e.end(), std::back_inserter(enumerated));
+ BOOST_REQUIRE_EQUAL(enumerated.size(), 2);
+ BOOST_CHECK_EQUAL(enumerated[0].fromInfinity, true);
+ BOOST_CHECK_EQUAL(enumerated[0].toInfinity, false);
+ BOOST_CHECK_EQUAL(enumerated[0].to, name::Component("e2"));
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(enumerated[0]), "(-∞,e2]");
+ BOOST_CHECK_EQUAL(enumerated[0], (Exclude::Range{true, name::Component("ignore"), false, name::Component("e2")}));
+ BOOST_CHECK_EQUAL(enumerated[1].fromInfinity, false);
+ BOOST_CHECK_EQUAL(enumerated[1].from, name::Component("f0"));
+ BOOST_CHECK_EQUAL(enumerated[1].toInfinity, true);
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(enumerated[1]), "[f0,+∞)");
+ BOOST_CHECK_EQUAL(enumerated[1], (Exclude::Range{false, name::Component("f0"), true, name::Component("ignore")}));
+
+ e.excludeAfter(name::Component("e5"));
+ BOOST_CHECK_EQUAL(e.toUri(), "*,e2,e5,*");
+ BOOST_CHECK_EQUAL(e.size(), 2);
+ enumerated.clear();
+ std::copy(e.begin(), e.end(), std::back_inserter(enumerated));
+ BOOST_REQUIRE_EQUAL(enumerated.size(), 2);
+ BOOST_CHECK_EQUAL(enumerated[0].fromInfinity, true);
+ BOOST_CHECK_EQUAL(enumerated[0].toInfinity, false);
+ BOOST_CHECK_EQUAL(enumerated[0].to, name::Component("e2"));
+ BOOST_CHECK_EQUAL(enumerated[1].fromInfinity, false);
+ BOOST_CHECK_EQUAL(enumerated[1].from, name::Component("e5"));
+ BOOST_CHECK_EQUAL(enumerated[1].toInfinity, true);
+
+ e.excludeAfter(name::Component("b2"));
+ BOOST_CHECK_EQUAL(e.toUri(), "*");
+ BOOST_CHECK_EQUAL(e.size(), 1);
+ enumerated.clear();
+ std::copy(e.begin(), e.end(), std::back_inserter(enumerated));
+ BOOST_REQUIRE_EQUAL(enumerated.size(), 1);
+ BOOST_CHECK_EQUAL(enumerated[0].fromInfinity, true);
+ BOOST_CHECK_EQUAL(enumerated[0].toInfinity, true);
+
+ BOOST_REQUIRE_THROW(e.excludeRange(name::Component("d0"), name::Component("a0")),
+ Exclude::Error);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // GenericComponent
+
+BOOST_AUTO_TEST_SUITE(ImplicitDigest) // exclude ImplicitSha256DigestComponent
+
+/** \brief make a name::Component with an octet repeated util::Sha256::DIGEST_SIZE times
+ * \param octet the octet to fill the component
+ * \param isDigest whether to make an ImplicitSha256DigestComponent or a GenericNameComponent
+ * \param lastOctet if non-negative, set the last octet to a different value
+ */
+static name::Component
+makeComponent(uint8_t octet, bool isDigest, int lastOctet = -1)
+{
+ uint8_t wire[util::Sha256::DIGEST_SIZE];
+ std::memset(wire, octet, sizeof(wire));
+ if (lastOctet >= 0) {
+ wire[util::Sha256::DIGEST_SIZE - 1] = static_cast<uint8_t>(lastOctet);
+ }
+
+ if (isDigest) {
+ return name::Component::fromImplicitSha256Digest(wire, sizeof(wire));
+ }
+ else {
+ return name::Component(wire, sizeof(wire));
+ }
+}
+
+BOOST_AUTO_TEST_CASE(One)
+{
+ name::Component digestC = makeComponent(0xCC, true);;
+ name::Component genericC = makeComponent(0xCC, false);
+ name::Component digestD = makeComponent(0xDD, true);
+
+ Exclude e;
+ e.excludeOne(digestC);
+ BOOST_CHECK_EQUAL(e.isExcluded(digestC), true);
+ BOOST_CHECK_EQUAL(e.isExcluded(genericC), false);
+ BOOST_CHECK_EQUAL(e.isExcluded(digestD), false);
+
+ e.clear();
+ e.excludeOne(genericC);
+ BOOST_CHECK_EQUAL(e.isExcluded(digestC), false);
+ BOOST_CHECK_EQUAL(e.isExcluded(genericC), true);
+}
+
+BOOST_AUTO_TEST_CASE(BeforeDigest)
+{
+ name::Component digestBA = makeComponent(0xBB, true, 0xBA);
+ name::Component digestBB = makeComponent(0xBB, true);
+ name::Component digestBC = makeComponent(0xBB, true, 0xBC);
+
+ Exclude e;
+ e.excludeBefore(digestBB);
+ BOOST_CHECK_EQUAL(e.isExcluded(digestBA), true);
+ BOOST_CHECK_EQUAL(e.isExcluded(digestBB), true);
+ BOOST_CHECK_EQUAL(e.isExcluded(digestBC), false);
+ BOOST_CHECK_EQUAL(e.isExcluded(name::Component("")), false);
+ BOOST_CHECK_EQUAL(e.isExcluded(name::Component("generic")), false);
+
+ BOOST_CHECK_EQUAL(e.size(), 1);
+ std::vector<Exclude::Range> enumerated;
+ std::copy(e.begin(), e.end(), std::back_inserter(enumerated));
+ BOOST_REQUIRE_EQUAL(enumerated.size(), 1);
+ BOOST_CHECK_EQUAL(enumerated[0].fromInfinity, true);
+ BOOST_CHECK_EQUAL(enumerated[0].toInfinity, false);
+ BOOST_CHECK_EQUAL(enumerated[0].to, digestBB);
+}
+
+BOOST_AUTO_TEST_CASE(BeforeGeneric)
+{
+ name::Component digest0 = makeComponent(0x00, true);
+ name::Component digest9 = makeComponent(0x99, true);
+ name::Component digestF = makeComponent(0xFF, true);
+
+ Exclude e;
+ e.excludeBefore(name::Component(""));
+ BOOST_CHECK_EQUAL(e.isExcluded(digest0), true);
+ BOOST_CHECK_EQUAL(e.isExcluded(digest9), true);
+ BOOST_CHECK_EQUAL(e.isExcluded(digestF), true);
+ BOOST_CHECK_EQUAL(e.isExcluded(name::Component("")), true);
+ BOOST_CHECK_EQUAL(e.isExcluded(name::Component("generic")), false);
+}
+
+BOOST_AUTO_TEST_CASE(AfterDigest)
+{
+ name::Component digestBA = makeComponent(0xBB, true, 0xBA);
+ name::Component digestBB = makeComponent(0xBB, true);
+ name::Component digestBC = makeComponent(0xBB, true, 0xBC);
+
+ Exclude e;
+ e.excludeAfter(digestBB);
+ BOOST_CHECK_EQUAL(e.isExcluded(digestBA), false);
+ BOOST_CHECK_EQUAL(e.isExcluded(digestBB), true);
+ BOOST_CHECK_EQUAL(e.isExcluded(digestBC), true);
+ BOOST_CHECK_EQUAL(e.isExcluded(name::Component("")), true);
+ BOOST_CHECK_EQUAL(e.isExcluded(name::Component("generic")), true);
+
+ BOOST_CHECK_EQUAL(e.size(), 1);
+ std::vector<Exclude::Range> enumerated;
+ std::copy(e.begin(), e.end(), std::back_inserter(enumerated));
+ BOOST_REQUIRE_EQUAL(enumerated.size(), 1);
+ BOOST_CHECK_EQUAL(enumerated[0].fromInfinity, false);
+ BOOST_CHECK_EQUAL(enumerated[0].from, digestBB);
+ BOOST_CHECK_EQUAL(enumerated[0].toInfinity, true);
+}
+
+BOOST_AUTO_TEST_CASE(AfterDigestFF)
+{
+ name::Component digest00 = makeComponent(0x00, true);
+ name::Component digest99 = makeComponent(0x99, true);
+ name::Component digestFE = makeComponent(0xFF, true, 0xFE);
+ name::Component digestFF = makeComponent(0xFF, true);
+
+ Exclude e;
+ e.excludeAfter(digestFF);
+ BOOST_CHECK_EQUAL(e.isExcluded(digest00), false);
+ BOOST_CHECK_EQUAL(e.isExcluded(digest99), false);
+ BOOST_CHECK_EQUAL(e.isExcluded(digestFE), false);
+ BOOST_CHECK_EQUAL(e.isExcluded(digestFF), true);
+ BOOST_CHECK_EQUAL(e.isExcluded(name::Component("")), true);
+ BOOST_CHECK_EQUAL(e.isExcluded(name::Component("generic")), true);
+}
+
+BOOST_AUTO_TEST_CASE(AfterGeneric)
+{
+ name::Component digest0 = makeComponent(0x00, true);
+ name::Component digest9 = makeComponent(0x99, true);
+ name::Component digestF = makeComponent(0xFF, true);
+
+ Exclude e;
+ e.excludeAfter(name::Component(""));
+ BOOST_CHECK_EQUAL(e.isExcluded(digest0), false);
+ BOOST_CHECK_EQUAL(e.isExcluded(digest9), false);
+ BOOST_CHECK_EQUAL(e.isExcluded(digestF), false);
+ BOOST_CHECK_EQUAL(e.isExcluded(name::Component("")), true);
+ BOOST_CHECK_EQUAL(e.isExcluded(name::Component("generic")), true);
+}
+
+BOOST_AUTO_TEST_CASE(RangeDigest)
+{
+ name::Component digest0 = makeComponent(0x00, true);
+ name::Component digest7 = makeComponent(0x77, true);
+ name::Component digest8 = makeComponent(0x88, true);
+ name::Component digest9 = makeComponent(0x99, true);
+ name::Component digestF = makeComponent(0xFF, true);
+
+ Exclude e;
+ e.excludeRange(digest7, digest9);
+ BOOST_CHECK_EQUAL(e.isExcluded(digest0), false);
+ BOOST_CHECK_EQUAL(e.isExcluded(digest7), true);
+ BOOST_CHECK_EQUAL(e.isExcluded(digest8), true);
+ BOOST_CHECK_EQUAL(e.isExcluded(digest9), true);
+ BOOST_CHECK_EQUAL(e.isExcluded(digestF), false);
+ BOOST_CHECK_EQUAL(e.isExcluded(name::Component("")), false);
+ BOOST_CHECK_EQUAL(e.isExcluded(name::Component("generic")), false);
+}
+
+BOOST_AUTO_TEST_CASE(RangeDigestReverse)
+{
+ name::Component digest7 = makeComponent(0x77, true);
+ name::Component digest9 = makeComponent(0x99, true);
+
+ Exclude e;
+ BOOST_CHECK_THROW(e.excludeRange(digest9, digest7), Exclude::Error);
+}
+
+BOOST_AUTO_TEST_CASE(RangeDigestGeneric)
+{
+ name::Component digest0 = makeComponent(0x00, true);
+ name::Component digest7 = makeComponent(0x77, true);
+ name::Component digest9 = makeComponent(0x99, true);
+ name::Component digestF = makeComponent(0xFF, true);
+
+ Exclude e;
+ e.excludeRange(digest9, name::Component(""));
+ BOOST_CHECK_EQUAL(e.isExcluded(digest0), false);
+ BOOST_CHECK_EQUAL(e.isExcluded(digest7), false);
+ BOOST_CHECK_EQUAL(e.isExcluded(digest9), true);
+ BOOST_CHECK_EQUAL(e.isExcluded(digestF), true);
+ BOOST_CHECK_EQUAL(e.isExcluded(name::Component("")), true);
+ BOOST_CHECK_EQUAL(e.isExcluded(name::Component("generic")), false);
+
+ BOOST_CHECK_EQUAL(e.size(), 1);
+ std::vector<Exclude::Range> enumerated;
+ std::copy(e.begin(), e.end(), std::back_inserter(enumerated));
+ BOOST_REQUIRE_EQUAL(enumerated.size(), 1);
+ BOOST_CHECK_EQUAL(enumerated[0].fromInfinity, false);
+ BOOST_CHECK_EQUAL(enumerated[0].from, digest9);
+ BOOST_CHECK_EQUAL(enumerated[0].toInfinity, false);
+ BOOST_CHECK_EQUAL(enumerated[0].to, name::Component(""));
+}
+
+BOOST_AUTO_TEST_CASE(RangeGenericDigest)
+{
+ name::Component digestF = makeComponent(0xFF, true);
+
+ Exclude e;
+ BOOST_CHECK_THROW(e.excludeRange(name::Component(""), digestF), Exclude::Error);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // ImplicitDigest
+
+BOOST_AUTO_TEST_SUITE(WireCompare) // wireEncode, wireDecode, operator==, operator!=
+
+BOOST_AUTO_TEST_CASE(EqualityComparable)
+{
+ Exclude e1;
+ Exclude e2;
+ BOOST_CHECK_EQUAL(e1, e2);
+
+ e1.excludeOne(name::Component("T"));
+ BOOST_CHECK_NE(e1, e2);
+
+ e2.excludeOne(name::Component("D"));
+ BOOST_CHECK_NE(e1, e2);
+
+ e2.clear();
+ e2.excludeOne(name::Component("T"));
+ BOOST_CHECK_EQUAL(e1, e2);
+
+ e2.clear();
+ const uint8_t EXCLUDE[] = { 0x10, 0x15, 0x13, 0x00, 0x08, 0x01, 0x41, 0x08, 0x01, 0x42,
+ 0x08, 0x01, 0x43, 0x13, 0x00, 0x08, 0x01, 0x44, 0x08, 0x01,
+ 0x45, 0x13, 0x00 };
+ e2.wireDecode(Block(EXCLUDE, sizeof(EXCLUDE)));
+
+ e1.clear();
+ e1.excludeBefore(name::Component("A"));
+ e1.excludeOne(name::Component("B"));
+ e1.excludeRange(name::Component("C"), name::Component("D"));
+ e1.excludeAfter(name::Component("E"));
+ BOOST_CHECK_EQUAL(e1, e2);
+}
+
+BOOST_AUTO_TEST_CASE(Malformed)
+{
+ Exclude e1;
+ BOOST_CHECK_THROW(e1.wireEncode(), Exclude::Error);
+
+ Exclude e2;
+
+ // top-level TLV-TYPE is not tlv::Exclude
+ const uint8_t NON_EXCLUDE[] = { 0x01, 0x02, 0x13, 0x00 };
+ BOOST_CHECK_THROW(e2.wireDecode(Block(NON_EXCLUDE, sizeof(NON_EXCLUDE))),
+ tlv::Error);
+
+ // Exclude element is empty
+ const uint8_t EMPTY_EXCLUDE[] = { 0x10, 0x00 };
+ BOOST_CHECK_THROW(e2.wireDecode(Block(EMPTY_EXCLUDE, sizeof(EMPTY_EXCLUDE))),
+ Exclude::Error);
+
+ // Exclude element contains unknown element
+ const uint8_t UNKNOWN_COMP1[] = { 0x10, 0x02, 0xAA, 0x00 };
+ BOOST_CHECK_THROW(e2.wireDecode(Block(UNKNOWN_COMP1, sizeof(UNKNOWN_COMP1))),
+ Exclude::Error);
+
+ // Exclude element contains unknown element
+ const uint8_t UNKNOWN_COMP2[] = { 0x10, 0x05, 0x08, 0x01, 0x54, 0xAA, 0x00 };
+ BOOST_CHECK_THROW(e2.wireDecode(Block(UNKNOWN_COMP2, sizeof(UNKNOWN_COMP2))),
+ Exclude::Error);
+
+ // // <Exclude><Any/></Exclude>
+ // const uint8_t ONLY_ANY[] = { 0x10, 0x02, 0x13, 0x00 };
+ // BOOST_CHECK_THROW(e2.wireDecode(Block(ONLY_ANY, sizeof(ONLY_ANY))),
+ // Exclude::Error);
+
+ // <Exclude><Any/><Any/></Exclude>
+ const uint8_t ANY_ANY[] = { 0x10, 0x04, 0x13, 0x00, 0x13, 0x00 };
+ BOOST_CHECK_THROW(e2.wireDecode(Block(ANY_ANY, sizeof(ANY_ANY))),
+ Exclude::Error);
+
+ // // <Exclude><Any/><GenericNameComponent>T</GenericNameComponent><Any/></Exclude>
+ // const uint8_t ANY_COMPONENT_ANY[] = { 0x10, 0x07, 0x13, 0x00, 0x08, 0x01, 0x54, 0x13, 0x00 };
+ // BOOST_CHECK_THROW(e2.wireDecode(Block(ANY_COMPONENT_ANY, sizeof(ANY_COMPONENT_ANY))),
+ // Exclude::Error);
+
+ uint8_t WIRE[] = {
+ 0x10, 0x20, // Exclude
+ 0x01, 0x1E, // ImplicitSha256DigestComponent with incorrect length
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd
+ };
+
+ BOOST_CHECK_THROW(Exclude().wireDecode(Block(WIRE, sizeof(WIRE))), Exclude::Error);
+}
+
+BOOST_AUTO_TEST_CASE(ImplicitSha256Digest)
+{
+ uint8_t WIRE[] = {
+ 0x10, 0x22, // Exclude
+ 0x01, 0x20, // ImplicitSha256DigestComponent
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd
+ };
+
+ Block block(WIRE, sizeof(WIRE));
+
+ Exclude exclude;
+ BOOST_CHECK_NO_THROW(exclude.wireDecode(block));
+ BOOST_CHECK(exclude.wireEncode() == block);
+}
+
+BOOST_AUTO_TEST_CASE(EmptyComponent) // Bug #2660
+{
+ Exclude e1, e2;
+
+ e1.excludeOne(name::Component());
+ e2.excludeOne(name::Component(""));
+
+ BOOST_CHECK_EQUAL(e1, e2);
+ BOOST_CHECK_EQUAL(e1.toUri(), e2.toUri());
+ BOOST_CHECK(e1.wireEncode() == e2.wireEncode());
+
+ BOOST_CHECK_EQUAL("...", e1.toUri());
+
+ uint8_t WIRE[] {0x10, 0x02, 0x08, 0x00};
+ BOOST_CHECK_EQUAL_COLLECTIONS(e1.wireEncode().begin(), e1.wireEncode().end(),
+ WIRE, WIRE + sizeof(WIRE));
+
+ Exclude e3(Block(WIRE, sizeof(WIRE)));
+ BOOST_CHECK_EQUAL(e1, e3);
+ BOOST_CHECK_EQUAL(e1.toUri(), e3.toUri());
+}
+
+BOOST_AUTO_TEST_SUITE_END() // WireCompare
+
+BOOST_AUTO_TEST_SUITE_END() // TestExclude
+
+} // namespace tests
+} // namespace ndn
diff --git a/tests/unit/face.t.cpp b/tests/unit/face.t.cpp
new file mode 100644
index 0000000..3f2b82a
--- /dev/null
+++ b/tests/unit/face.t.cpp
@@ -0,0 +1,917 @@
+/* -*- 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 "face.hpp"
+#include "lp/tags.hpp"
+#include "transport/tcp-transport.hpp"
+#include "transport/unix-transport.hpp"
+#include "util/dummy-client-face.hpp"
+#include "util/scheduler.hpp"
+
+#include "boost-test.hpp"
+#include "make-interest-data.hpp"
+#include "identity-management-time-fixture.hpp"
+#include "test-home-fixture.hpp"
+
+namespace ndn {
+namespace tests {
+
+using ndn::util::DummyClientFace;
+
+class FaceFixture : public IdentityManagementTimeFixture
+{
+public:
+ explicit
+ FaceFixture(bool enableRegistrationReply = true)
+ : face(io, m_keyChain, {true, enableRegistrationReply})
+ {
+ }
+
+public:
+ DummyClientFace face;
+};
+
+class FacesNoRegistrationReplyFixture : public FaceFixture
+{
+public:
+ FacesNoRegistrationReplyFixture()
+ : FaceFixture(false)
+ {
+ }
+};
+
+BOOST_FIXTURE_TEST_SUITE(TestFace, FaceFixture)
+
+BOOST_AUTO_TEST_SUITE(Consumer)
+
+BOOST_AUTO_TEST_CASE(ExpressInterestData)
+{
+ size_t nData = 0;
+ face.expressInterest(*makeInterest("/Hello/World", true, 50_ms),
+ [&] (const Interest& i, const Data& d) {
+ BOOST_CHECK(i.getName().isPrefixOf(d.getName()));
+ BOOST_CHECK_EQUAL(i.getName(), "/Hello/World");
+ ++nData;
+ },
+ bind([] { BOOST_FAIL("Unexpected Nack"); }),
+ bind([] { BOOST_FAIL("Unexpected timeout"); }));
+
+ advanceClocks(40_ms);
+
+ face.receive(*makeData("/Bye/World/a"));
+ face.receive(*makeData("/Hello/World/a"));
+
+ advanceClocks(50_ms, 2);
+
+ BOOST_CHECK_EQUAL(nData, 1);
+ BOOST_CHECK_EQUAL(face.sentInterests.size(), 1);
+ BOOST_CHECK_EQUAL(face.sentData.size(), 0);
+
+ size_t nTimeouts = 0;
+ face.expressInterest(*makeInterest("/Hello/World/a/2", false, 50_ms),
+ bind([]{}),
+ bind([]{}),
+ bind([&nTimeouts] { ++nTimeouts; }));
+ advanceClocks(200_ms, 5);
+ BOOST_CHECK_EQUAL(nTimeouts, 1);
+}
+
+BOOST_AUTO_TEST_CASE(ExpressMultipleInterestData)
+{
+ size_t nData = 0;
+
+ face.expressInterest(*makeInterest("/Hello/World", true, 50_ms),
+ [&] (const Interest& i, const Data& d) {
+ ++nData;
+ },
+ bind([] { BOOST_FAIL("Unexpected Nack"); }),
+ bind([] { BOOST_FAIL("Unexpected timeout"); }));
+
+ face.expressInterest(*makeInterest("/Hello/World/a", true, 50_ms),
+ [&] (const Interest& i, const Data& d) {
+ ++nData;
+ },
+ bind([] { BOOST_FAIL("Unexpected Nack"); }),
+ bind([] { BOOST_FAIL("Unexpected timeout"); }));
+
+ advanceClocks(40_ms);
+
+ face.receive(*makeData("/Hello/World/a/b"));
+
+ advanceClocks(50_ms, 2);
+
+ BOOST_CHECK_EQUAL(nData, 2);
+ BOOST_CHECK_EQUAL(face.sentInterests.size(), 2);
+ BOOST_CHECK_EQUAL(face.sentData.size(), 0);
+}
+
+BOOST_AUTO_TEST_CASE(ExpressInterestEmptyDataCallback)
+{
+ face.expressInterest(*makeInterest("/Hello/World", true),
+ nullptr,
+ bind([] { BOOST_FAIL("Unexpected Nack"); }),
+ bind([] { BOOST_FAIL("Unexpected timeout"); }));
+ advanceClocks(1_ms);
+
+ BOOST_CHECK_NO_THROW(do {
+ face.receive(*makeData("/Hello/World/a"));
+ advanceClocks(1_ms);
+ } while (false));
+}
+
+BOOST_AUTO_TEST_CASE(ExpressInterestTimeout)
+{
+ size_t nTimeouts = 0;
+ face.expressInterest(*makeInterest("/Hello/World", false, 50_ms),
+ bind([] { BOOST_FAIL("Unexpected Data"); }),
+ bind([] { BOOST_FAIL("Unexpected Nack"); }),
+ [&nTimeouts] (const Interest& i) {
+ BOOST_CHECK_EQUAL(i.getName(), "/Hello/World");
+ ++nTimeouts;
+ });
+
+ advanceClocks(200_ms, 5);
+
+ BOOST_CHECK_EQUAL(nTimeouts, 1);
+ BOOST_CHECK_EQUAL(face.sentInterests.size(), 1);
+ BOOST_CHECK_EQUAL(face.sentData.size(), 0);
+ BOOST_CHECK_EQUAL(face.sentNacks.size(), 0);
+}
+
+BOOST_AUTO_TEST_CASE(ExpressInterestEmptyTimeoutCallback)
+{
+ face.expressInterest(*makeInterest("/Hello/World", false, 50_ms),
+ bind([] { BOOST_FAIL("Unexpected Data"); }),
+ bind([] { BOOST_FAIL("Unexpected Nack"); }),
+ nullptr);
+ advanceClocks(40_ms);
+
+ BOOST_CHECK_NO_THROW(do {
+ advanceClocks(6_ms, 2);
+ } while (false));
+}
+
+BOOST_AUTO_TEST_CASE(ExpressInterestNack)
+{
+ size_t nNacks = 0;
+
+ auto interest = makeInterest("/Hello/World", false, 50_ms);
+
+ face.expressInterest(*interest,
+ bind([] { BOOST_FAIL("Unexpected Data"); }),
+ [&] (const Interest& i, const lp::Nack& n) {
+ BOOST_CHECK(i.getName().isPrefixOf(n.getInterest().getName()));
+ BOOST_CHECK_EQUAL(i.getName(), "/Hello/World");
+ BOOST_CHECK_EQUAL(n.getReason(), lp::NackReason::DUPLICATE);
+ ++nNacks;
+ },
+ bind([] { BOOST_FAIL("Unexpected timeout"); }));
+
+ advanceClocks(40_ms);
+
+ face.receive(makeNack(face.sentInterests.at(0), lp::NackReason::DUPLICATE));
+
+ advanceClocks(50_ms, 2);
+
+ BOOST_CHECK_EQUAL(nNacks, 1);
+ BOOST_CHECK_EQUAL(face.sentInterests.size(), 1);
+}
+
+BOOST_AUTO_TEST_CASE(ExpressMultipleInterestNack)
+{
+ size_t nNacks = 0;
+
+ auto interest = makeInterest("/Hello/World", false, 50_ms, 1);
+ face.expressInterest(*interest,
+ bind([] { BOOST_FAIL("Unexpected Data"); }),
+ [&] (const Interest& i, const lp::Nack& n) {
+ ++nNacks;
+ },
+ bind([] { BOOST_FAIL("Unexpected timeout"); }));
+
+ interest->setNonce(2);
+ face.expressInterest(*interest,
+ bind([] { BOOST_FAIL("Unexpected Data"); }),
+ [&] (const Interest& i, const lp::Nack& n) {
+ ++nNacks;
+ },
+ bind([] { BOOST_FAIL("Unexpected timeout"); }));
+
+ advanceClocks(40_ms);
+
+ face.receive(makeNack(face.sentInterests.at(1), lp::NackReason::DUPLICATE));
+
+ advanceClocks(50_ms, 2);
+
+ BOOST_CHECK_EQUAL(nNacks, 2);
+ BOOST_CHECK_EQUAL(face.sentInterests.size(), 2);
+}
+
+BOOST_AUTO_TEST_CASE(ExpressInterestEmptyNackCallback)
+{
+ face.expressInterest(*makeInterest("/Hello/World"),
+ bind([] { BOOST_FAIL("Unexpected Data"); }),
+ nullptr,
+ bind([] { BOOST_FAIL("Unexpected timeout"); }));
+ advanceClocks(1_ms);
+
+ BOOST_CHECK_NO_THROW(do {
+ face.receive(makeNack(face.sentInterests.at(0), lp::NackReason::DUPLICATE));
+ advanceClocks(1_ms);
+ } while (false));
+}
+
+BOOST_AUTO_TEST_CASE(RemovePendingInterest)
+{
+ const PendingInterestId* interestId =
+ face.expressInterest(*makeInterest("/Hello/World", true, 50_ms),
+ bind([] { BOOST_FAIL("Unexpected data"); }),
+ bind([] { BOOST_FAIL("Unexpected nack"); }),
+ bind([] { BOOST_FAIL("Unexpected timeout"); }));
+ advanceClocks(10_ms);
+
+ face.removePendingInterest(interestId);
+ advanceClocks(10_ms);
+
+ face.receive(*makeData("/Hello/World/%21"));
+ advanceClocks(200_ms, 5);
+
+ // avoid "test case [...] did not check any assertions" message from Boost.Test
+ BOOST_CHECK(true);
+}
+
+BOOST_AUTO_TEST_CASE(RemoveAllPendingInterests)
+{
+ face.expressInterest(*makeInterest("/Hello/World/0", false, 50_ms),
+ bind([] { BOOST_FAIL("Unexpected data"); }),
+ bind([] { BOOST_FAIL("Unexpected nack"); }),
+ bind([] { BOOST_FAIL("Unexpected timeout"); }));
+
+ face.expressInterest(*makeInterest("/Hello/World/1", false, 50_ms),
+ bind([] { BOOST_FAIL("Unexpected data"); }),
+ bind([] { BOOST_FAIL("Unexpected nack"); }),
+ bind([] { BOOST_FAIL("Unexpected timeout"); }));
+
+ advanceClocks(10_ms);
+
+ face.removeAllPendingInterests();
+ advanceClocks(10_ms);
+
+ BOOST_CHECK_EQUAL(face.getNPendingInterests(), 0);
+
+ face.receive(*makeData("/Hello/World/0"));
+ face.receive(*makeData("/Hello/World/1"));
+ advanceClocks(200_ms, 5);
+}
+
+BOOST_AUTO_TEST_CASE(DestructionWithoutCancellingPendingInterests) // Bug #2518
+{
+ {
+ DummyClientFace face2(io, m_keyChain);
+ face2.expressInterest(*makeInterest("/Hello/World", false, 50_ms),
+ nullptr, nullptr, nullptr);
+ advanceClocks(50_ms, 2);
+ }
+
+ advanceClocks(50_ms, 2); // should not crash
+
+ // avoid "test case [...] did not check any assertions" message from Boost.Test
+ BOOST_CHECK(true);
+}
+
+BOOST_AUTO_TEST_CASE(DataCallbackPutData) // Bug 4596
+{
+ face.expressInterest(*makeInterest("/localhost/notification/1"),
+ [&] (const Interest& i, const Data& d) {
+ face.put(*makeData("/chronosync/sampleDigest/1"));
+ }, nullptr, nullptr);
+ advanceClocks(10_ms);
+ BOOST_CHECK_EQUAL(face.sentInterests.back().getName(), "/localhost/notification/1");
+
+ face.receive(*makeInterest("/chronosync/sampleDigest", true));
+ advanceClocks(10_ms);
+
+ face.put(*makeData("/localhost/notification/1"));
+ advanceClocks(10_ms);
+ BOOST_CHECK_EQUAL(face.sentData.back().getName(), "/chronosync/sampleDigest/1");
+}
+
+BOOST_AUTO_TEST_SUITE_END() // Consumer
+
+BOOST_AUTO_TEST_SUITE(Producer)
+
+BOOST_AUTO_TEST_CASE(PutData)
+{
+ BOOST_CHECK_EQUAL(face.sentData.size(), 0);
+
+ Data data("/4g7xxcuEow/KFvK5Kf2m");
+ signData(data);
+ face.put(data);
+
+ lp::CachePolicy cachePolicy;
+ cachePolicy.setPolicy(lp::CachePolicyType::NO_CACHE);
+ data.setTag(make_shared<lp::CachePolicyTag>(cachePolicy));
+ data.setTag(make_shared<lp::CongestionMarkTag>(1));
+ face.put(data);
+
+ advanceClocks(10_ms);
+ BOOST_REQUIRE_EQUAL(face.sentData.size(), 2);
+ BOOST_CHECK(face.sentData[0].getTag<lp::CachePolicyTag>() == nullptr);
+ BOOST_CHECK(face.sentData[0].getTag<lp::CongestionMarkTag>() == nullptr);
+ BOOST_CHECK(face.sentData[1].getTag<lp::CachePolicyTag>() != nullptr);
+ BOOST_CHECK(face.sentData[1].getTag<lp::CongestionMarkTag>() != nullptr);
+}
+
+BOOST_AUTO_TEST_CASE(PutDataLoopback)
+{
+ bool hasInterest1 = false, hasData = false;
+
+ // first InterestFilter allows loopback and should receive Interest
+ face.setInterestFilter("/", [&] (const InterestFilter&, const Interest& interest) {
+ hasInterest1 = true;
+ // do not respond with Data right away, so Face must send Interest to forwarder
+ });
+ // second InterestFilter disallows loopback and should not receive Interest
+ face.setInterestFilter(InterestFilter("/").allowLoopback(false),
+ bind([] { BOOST_ERROR("Unexpected Interest on second InterestFilter"); }));
+
+ face.expressInterest(*makeInterest("/A", true),
+ bind([&] { hasData = true; }),
+ bind([] { BOOST_FAIL("Unexpected nack"); }),
+ bind([] { BOOST_FAIL("Unexpected timeout"); }));
+ advanceClocks(1_ms);
+ BOOST_CHECK_EQUAL(hasInterest1, true); // Interest looped back
+ BOOST_CHECK_EQUAL(face.sentInterests.size(), 1); // Interest sent to forwarder
+ BOOST_CHECK_EQUAL(hasData, false); // waiting for Data
+
+ face.put(*makeData("/A/B")); // first InterestFilter responds with Data
+ advanceClocks(1_ms);
+ BOOST_CHECK_EQUAL(hasData, true);
+ BOOST_CHECK_EQUAL(face.sentData.size(), 0); // do not spill Data to forwarder
+}
+
+BOOST_AUTO_TEST_CASE(PutMultipleData)
+{
+ bool hasInterest1 = false;
+ // register two Interest destinations
+ face.setInterestFilter("/", bind([&] {
+ hasInterest1 = true;
+ // sending Data right away from the first destination, don't care whether Interest goes to second destination
+ face.put(*makeData("/A/B"));
+ }));
+ face.setInterestFilter("/", bind([]{}));
+ advanceClocks(10_ms);
+
+ face.receive(*makeInterest("/A", true));
+ advanceClocks(10_ms);
+ BOOST_CHECK(hasInterest1);
+ BOOST_CHECK_EQUAL(face.sentData.size(), 1);
+ BOOST_CHECK_EQUAL(face.sentData.at(0).getName(), "/A/B");
+
+ face.put(*makeData("/A/C"));
+ BOOST_CHECK_EQUAL(face.sentData.size(), 1); // additional Data are ignored
+}
+
+BOOST_AUTO_TEST_CASE(PutNack)
+{
+ face.setInterestFilter("/", bind([]{})); // register one Interest destination so that face can accept Nacks
+ advanceClocks(10_ms);
+
+ BOOST_CHECK_EQUAL(face.sentNacks.size(), 0);
+
+ face.put(makeNack(*makeInterest("/unsolicited", false, DEFAULT_INTEREST_LIFETIME, 18645250),
+ lp::NackReason::NO_ROUTE));
+ advanceClocks(10_ms);
+ BOOST_CHECK_EQUAL(face.sentNacks.size(), 0); // unsolicited Nack would not be sent
+
+ auto interest1 = makeInterest("/Hello/World", false, DEFAULT_INTEREST_LIFETIME, 14247162);
+ face.receive(*interest1);
+ auto interest2 = makeInterest("/another/prefix", false, DEFAULT_INTEREST_LIFETIME, 92203002);
+ face.receive(*interest2);
+ advanceClocks(10_ms);
+
+ face.put(makeNack(*interest1, lp::NackReason::DUPLICATE));
+ advanceClocks(10_ms);
+ BOOST_REQUIRE_EQUAL(face.sentNacks.size(), 1);
+ BOOST_CHECK_EQUAL(face.sentNacks[0].getReason(), lp::NackReason::DUPLICATE);
+ BOOST_CHECK(face.sentNacks[0].getTag<lp::CongestionMarkTag>() == nullptr);
+
+ auto nack = makeNack(*interest2, lp::NackReason::NO_ROUTE);
+ nack.setTag(make_shared<lp::CongestionMarkTag>(1));
+ face.put(nack);
+ advanceClocks(10_ms);
+ BOOST_REQUIRE_EQUAL(face.sentNacks.size(), 2);
+ BOOST_CHECK_EQUAL(face.sentNacks[1].getReason(), lp::NackReason::NO_ROUTE);
+ BOOST_CHECK(face.sentNacks[1].getTag<lp::CongestionMarkTag>() != nullptr);
+}
+
+BOOST_AUTO_TEST_CASE(PutMultipleNack)
+{
+ bool hasInterest1 = false, hasInterest2 = false;
+ // register two Interest destinations
+ face.setInterestFilter("/", [&] (const InterestFilter&, const Interest& interest) {
+ hasInterest1 = true;
+ // sending Nack right away from the first destination, Interest should still go to second destination
+ face.put(makeNack(interest, lp::NackReason::CONGESTION));
+ });
+ face.setInterestFilter("/", bind([&] { hasInterest2 = true; }));
+ advanceClocks(10_ms);
+
+ auto interest = makeInterest("/A", false, DEFAULT_INTEREST_LIFETIME, 14333271);
+ face.receive(*interest);
+ advanceClocks(10_ms);
+ BOOST_CHECK(hasInterest1);
+ BOOST_CHECK(hasInterest2);
+
+ // Nack from first destination is received, should wait for a response from the other destination
+ BOOST_CHECK_EQUAL(face.sentNacks.size(), 0);
+
+ face.put(makeNack(*interest, lp::NackReason::NO_ROUTE)); // Nack from second destination
+ advanceClocks(10_ms);
+ BOOST_CHECK_EQUAL(face.sentNacks.size(), 1); // sending Nack after both destinations Nacked
+ BOOST_CHECK_EQUAL(face.sentNacks.at(0).getReason(), lp::NackReason::CONGESTION); // least severe reason
+
+ face.put(makeNack(*interest, lp::NackReason::DUPLICATE));
+ BOOST_CHECK_EQUAL(face.sentNacks.size(), 1); // additional Nacks are ignored
+}
+
+BOOST_AUTO_TEST_CASE(PutMultipleNackLoopback)
+{
+ bool hasInterest1 = false, hasNack = false;
+
+ // first InterestFilter allows loopback and should receive Interest
+ face.setInterestFilter("/", [&] (const InterestFilter&, const Interest& interest) {
+ hasInterest1 = true;
+ face.put(makeNack(interest, lp::NackReason::CONGESTION));
+ });
+ // second InterestFilter disallows loopback and should not receive Interest
+ face.setInterestFilter(InterestFilter("/").allowLoopback(false),
+ bind([] { BOOST_ERROR("Unexpected Interest on second InterestFilter"); }));
+
+ auto interest = makeInterest("/A", false, DEFAULT_INTEREST_LIFETIME, 28395852);
+ face.expressInterest(*interest,
+ bind([] { BOOST_FAIL("Unexpected data"); }),
+ [&] (const Interest&, const lp::Nack& nack) {
+ hasNack = true;
+ BOOST_CHECK_EQUAL(nack.getReason(), lp::NackReason::CONGESTION);
+ },
+ bind([] { BOOST_FAIL("Unexpected timeout"); }));
+ advanceClocks(1_ms);
+ BOOST_CHECK_EQUAL(hasInterest1, true); // Interest looped back
+ BOOST_CHECK_EQUAL(face.sentInterests.size(), 1); // Interest sent to forwarder
+ BOOST_CHECK_EQUAL(hasNack, false); // waiting for Nack from forwarder
+
+ face.receive(makeNack(*interest, lp::NackReason::NO_ROUTE));
+ advanceClocks(1_ms);
+ BOOST_CHECK_EQUAL(hasNack, true);
+}
+
+BOOST_AUTO_TEST_CASE(SetUnsetInterestFilter)
+{
+ size_t nInterests = 0;
+ size_t nRegs = 0;
+ const RegisteredPrefixId* regPrefixId =
+ face.setInterestFilter("/Hello/World",
+ bind([&nInterests] { ++nInterests; }),
+ bind([&nRegs] { ++nRegs; }),
+ bind([] { BOOST_FAIL("Unexpected setInterestFilter failure"); }));
+ advanceClocks(25_ms, 4);
+ BOOST_CHECK_EQUAL(nRegs, 1);
+ BOOST_CHECK_EQUAL(nInterests, 0);
+
+ face.receive(*makeInterest("/Hello/World/%21"));
+ advanceClocks(25_ms, 4);
+
+ BOOST_CHECK_EQUAL(nRegs, 1);
+ BOOST_CHECK_EQUAL(nInterests, 1);
+
+ face.receive(*makeInterest("/Bye/World/%21"));
+ advanceClocks(10000_ms, 10);
+ BOOST_CHECK_EQUAL(nInterests, 1);
+
+ face.receive(*makeInterest("/Hello/World/%21/2"));
+ advanceClocks(25_ms, 4);
+ BOOST_CHECK_EQUAL(nInterests, 2);
+
+ // removing filter
+ face.unsetInterestFilter(regPrefixId);
+ advanceClocks(25_ms, 4);
+
+ face.receive(*makeInterest("/Hello/World/%21/3"));
+ BOOST_CHECK_EQUAL(nInterests, 2);
+
+ face.unsetInterestFilter(static_cast<const RegisteredPrefixId*>(nullptr));
+ advanceClocks(25_ms, 4);
+
+ face.unsetInterestFilter(static_cast<const InterestFilterId*>(nullptr));
+ advanceClocks(25_ms, 4);
+}
+
+BOOST_AUTO_TEST_CASE(SetInterestFilterEmptyInterestCallback)
+{
+ face.setInterestFilter("/A", nullptr);
+ advanceClocks(1_ms);
+
+ BOOST_CHECK_NO_THROW(do {
+ face.receive(*makeInterest("/A/1"));
+ advanceClocks(1_ms);
+ } while (false));
+}
+
+BOOST_AUTO_TEST_CASE(SetUnsetInterestFilterWithoutSucessCallback)
+{
+ size_t nInterests = 0;
+ const RegisteredPrefixId* regPrefixId =
+ face.setInterestFilter("/Hello/World",
+ bind([&nInterests] { ++nInterests; }),
+ bind([] { BOOST_FAIL("Unexpected setInterestFilter failure"); }));
+ advanceClocks(25_ms, 4);
+ BOOST_CHECK_EQUAL(nInterests, 0);
+
+ face.receive(*makeInterest("/Hello/World/%21"));
+ advanceClocks(25_ms, 4);
+
+ BOOST_CHECK_EQUAL(nInterests, 1);
+
+ face.receive(*makeInterest("/Bye/World/%21"));
+ advanceClocks(10000_ms, 10);
+ BOOST_CHECK_EQUAL(nInterests, 1);
+
+ face.receive(*makeInterest("/Hello/World/%21/2"));
+ advanceClocks(25_ms, 4);
+ BOOST_CHECK_EQUAL(nInterests, 2);
+
+ // removing filter
+ face.unsetInterestFilter(regPrefixId);
+ advanceClocks(25_ms, 4);
+
+ face.receive(*makeInterest("/Hello/World/%21/3"));
+ BOOST_CHECK_EQUAL(nInterests, 2);
+
+ face.unsetInterestFilter(static_cast<const RegisteredPrefixId*>(nullptr));
+ advanceClocks(25_ms, 4);
+
+ face.unsetInterestFilter(static_cast<const InterestFilterId*>(nullptr));
+ advanceClocks(25_ms, 4);
+}
+
+BOOST_FIXTURE_TEST_CASE(SetInterestFilterFail, FacesNoRegistrationReplyFixture)
+{
+ // don't enable registration reply
+ size_t nRegFailed = 0;
+ face.setInterestFilter("/Hello/World",
+ bind([] { BOOST_FAIL("Unexpected Interest"); }),
+ bind([] { BOOST_FAIL("Unexpected success of setInterestFilter"); }),
+ bind([&nRegFailed] { ++nRegFailed; }));
+
+ advanceClocks(25_ms, 4);
+ BOOST_CHECK_EQUAL(nRegFailed, 0);
+
+ advanceClocks(2000_ms, 5);
+ BOOST_CHECK_EQUAL(nRegFailed, 1);
+}
+
+BOOST_FIXTURE_TEST_CASE(SetInterestFilterFailWithoutSuccessCallback, FacesNoRegistrationReplyFixture)
+{
+ // don't enable registration reply
+ size_t nRegFailed = 0;
+ face.setInterestFilter("/Hello/World",
+ bind([] { BOOST_FAIL("Unexpected Interest"); }),
+ bind([&nRegFailed] { ++nRegFailed; }));
+
+ advanceClocks(25_ms, 4);
+ BOOST_CHECK_EQUAL(nRegFailed, 0);
+
+ advanceClocks(2000_ms, 5);
+ BOOST_CHECK_EQUAL(nRegFailed, 1);
+}
+
+BOOST_AUTO_TEST_CASE(RegisterUnregisterPrefix)
+{
+ size_t nRegSuccesses = 0;
+ const RegisteredPrefixId* regPrefixId =
+ face.registerPrefix("/Hello/World",
+ bind([&nRegSuccesses] { ++nRegSuccesses; }),
+ bind([] { BOOST_FAIL("Unexpected registerPrefix failure"); }));
+
+ advanceClocks(25_ms, 4);
+ BOOST_CHECK_EQUAL(nRegSuccesses, 1);
+
+ size_t nUnregSuccesses = 0;
+ face.unregisterPrefix(regPrefixId,
+ bind([&nUnregSuccesses] { ++nUnregSuccesses; }),
+ bind([] { BOOST_FAIL("Unexpected unregisterPrefix failure"); }));
+
+ advanceClocks(25_ms, 4);
+ BOOST_CHECK_EQUAL(nUnregSuccesses, 1);
+}
+
+BOOST_FIXTURE_TEST_CASE(RegisterUnregisterPrefixFail, FacesNoRegistrationReplyFixture)
+{
+ // don't enable registration reply
+ size_t nRegFailures = 0;
+ face.registerPrefix("/Hello/World",
+ bind([] { BOOST_FAIL("Unexpected registerPrefix success"); }),
+ bind([&nRegFailures] { ++nRegFailures; }));
+
+ advanceClocks(5000_ms, 20);
+ BOOST_CHECK_EQUAL(nRegFailures, 1);
+}
+
+BOOST_AUTO_TEST_CASE(SimilarFilters)
+{
+ size_t nInInterests1 = 0;
+ face.setInterestFilter("/Hello/World",
+ bind([&nInInterests1] { ++nInInterests1; }),
+ nullptr,
+ bind([] { BOOST_FAIL("Unexpected setInterestFilter failure"); }));
+
+ size_t nInInterests2 = 0;
+ face.setInterestFilter("/Hello",
+ bind([&nInInterests2] { ++nInInterests2; }),
+ nullptr,
+ bind([] { BOOST_FAIL("Unexpected setInterestFilter failure"); }));
+
+ size_t nInInterests3 = 0;
+ face.setInterestFilter("/Los/Angeles/Lakers",
+ bind([&nInInterests3] { ++nInInterests3; }),
+ nullptr,
+ bind([] { BOOST_FAIL("Unexpected setInterestFilter failure"); }));
+
+ advanceClocks(25_ms, 4);
+
+ face.receive(*makeInterest("/Hello/World/%21"));
+ advanceClocks(25_ms, 4);
+
+ BOOST_CHECK_EQUAL(nInInterests1, 1);
+ BOOST_CHECK_EQUAL(nInInterests2, 1);
+ BOOST_CHECK_EQUAL(nInInterests3, 0);
+}
+
+BOOST_AUTO_TEST_CASE(SetRegexFilterError)
+{
+ face.setInterestFilter(InterestFilter("/Hello/World", "<><b><c>?"),
+ [] (const Name&, const Interest&) {
+ BOOST_FAIL("InterestFilter::Error should have been triggered");
+ },
+ nullptr,
+ bind([] { BOOST_FAIL("Unexpected setInterestFilter failure"); }));
+
+ advanceClocks(25_ms, 4);
+
+ BOOST_REQUIRE_THROW(face.receive(*makeInterest("/Hello/World/XXX/b/c")), InterestFilter::Error);
+}
+
+BOOST_AUTO_TEST_CASE(SetRegexFilter)
+{
+ size_t nInInterests = 0;
+ face.setInterestFilter(InterestFilter("/Hello/World", "<><b><c>?"),
+ bind([&nInInterests] { ++nInInterests; }),
+ nullptr,
+ bind([] { BOOST_FAIL("Unexpected setInterestFilter failure"); }));
+
+ advanceClocks(25_ms, 4);
+
+ face.receive(*makeInterest("/Hello/World/a")); // shouldn't match
+ BOOST_CHECK_EQUAL(nInInterests, 0);
+
+ face.receive(*makeInterest("/Hello/World/a/b")); // should match
+ BOOST_CHECK_EQUAL(nInInterests, 1);
+
+ face.receive(*makeInterest("/Hello/World/a/b/c")); // should match
+ BOOST_CHECK_EQUAL(nInInterests, 2);
+
+ face.receive(*makeInterest("/Hello/World/a/b/d")); // should not match
+ BOOST_CHECK_EQUAL(nInInterests, 2);
+}
+
+BOOST_AUTO_TEST_CASE(SetRegexFilterAndRegister)
+{
+ size_t nInInterests = 0;
+ face.setInterestFilter(InterestFilter("/Hello/World", "<><b><c>?"),
+ bind([&nInInterests] { ++nInInterests; }));
+
+ size_t nRegSuccesses = 0;
+ face.registerPrefix("/Hello/World",
+ bind([&nRegSuccesses] { ++nRegSuccesses; }),
+ bind([] { BOOST_FAIL("Unexpected setInterestFilter failure"); }));
+
+ advanceClocks(25_ms, 4);
+ BOOST_CHECK_EQUAL(nRegSuccesses, 1);
+
+ face.receive(*makeInterest("/Hello/World/a")); // shouldn't match
+ BOOST_CHECK_EQUAL(nInInterests, 0);
+
+ face.receive(*makeInterest("/Hello/World/a/b")); // should match
+ BOOST_CHECK_EQUAL(nInInterests, 1);
+
+ face.receive(*makeInterest("/Hello/World/a/b/c")); // should match
+ BOOST_CHECK_EQUAL(nInInterests, 2);
+
+ face.receive(*makeInterest("/Hello/World/a/b/d")); // should not match
+ BOOST_CHECK_EQUAL(nInInterests, 2);
+}
+
+BOOST_FIXTURE_TEST_CASE(SetInterestFilterNoReg, FacesNoRegistrationReplyFixture) // Bug 2318
+{
+ // This behavior is specific to DummyClientFace.
+ // Regular Face won't accept incoming packets until something is sent.
+
+ int hit = 0;
+ face.setInterestFilter(Name("/"), bind([&hit] { ++hit; }));
+ face.processEvents(time::milliseconds(-1));
+
+ face.receive(*makeInterest("/A"));
+ face.processEvents(time::milliseconds(-1));
+
+ BOOST_CHECK_EQUAL(hit, 1);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // Producer
+
+BOOST_AUTO_TEST_SUITE(IoRoutines)
+
+BOOST_AUTO_TEST_CASE(ProcessEvents)
+{
+ face.processEvents(time::milliseconds(-1)); // io_service::reset()/poll() inside
+
+ size_t nRegSuccesses = 0;
+ face.registerPrefix("/Hello/World",
+ bind([&nRegSuccesses] { ++nRegSuccesses; }),
+ bind([] { BOOST_FAIL("Unexpected setInterestFilter failure"); }));
+
+ // io_service::poll() without reset
+ face.getIoService().poll();
+ BOOST_CHECK_EQUAL(nRegSuccesses, 0);
+
+ face.processEvents(time::milliseconds(-1)); // io_service::reset()/poll() inside
+ BOOST_CHECK_EQUAL(nRegSuccesses, 1);
+}
+
+BOOST_AUTO_TEST_CASE(DestroyWithoutProcessEvents) // Bug 3248
+{
+ auto face2 = make_unique<Face>(io);
+ face2.reset();
+
+ io.poll(); // should not crash
+
+ // avoid "test case [...] did not check any assertions" message from Boost.Test
+ BOOST_CHECK(true);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // IoRoutines
+
+BOOST_AUTO_TEST_SUITE(Transport)
+
+using ndn::Transport;
+
+struct PibDirWithDefaultTpm
+{
+ const std::string PATH = "build/keys-with-default-tpm";
+};
+
+BOOST_FIXTURE_TEST_CASE(FaceTransport, IdentityManagementTimeFixture)
+{
+ BOOST_CHECK(Face().getTransport() != nullptr);
+
+ BOOST_CHECK(Face(shared_ptr<Transport>()).getTransport() != nullptr);
+ BOOST_CHECK(Face(shared_ptr<Transport>(), io).getTransport() != nullptr);
+ BOOST_CHECK(Face(shared_ptr<Transport>(), io, m_keyChain).getTransport() != nullptr);
+
+ auto transport = make_shared<TcpTransport>("localhost", "6363"); // no real io operations will be scheduled
+ BOOST_CHECK(Face(transport).getTransport() == transport);
+ BOOST_CHECK(Face(transport, io).getTransport() == transport);
+ BOOST_CHECK(Face(transport, io, m_keyChain).getTransport() == transport);
+}
+
+class WithEnv : private IdentityManagementTimeFixture
+{
+public:
+ WithEnv()
+ {
+ if (getenv("NDN_CLIENT_TRANSPORT") != nullptr) {
+ m_oldTransport = getenv("NDN_CLIENT_TRANSPORT");
+ unsetenv("NDN_CLIENT_TRANSPORT");
+ }
+ }
+
+ void
+ configure(const std::string& faceUri)
+ {
+ setenv("NDN_CLIENT_TRANSPORT", faceUri.c_str(), true);
+ }
+
+ ~WithEnv()
+ {
+ if (!m_oldTransport.empty()) {
+ setenv("NDN_CLIENT_TRANSPORT", m_oldTransport.c_str(), true);
+ }
+ else {
+ unsetenv("NDN_CLIENT_TRANSPORT");
+ }
+ }
+
+private:
+ std::string m_oldTransport;
+};
+
+class WithConfig : private TestHomeFixture<DefaultPibDir>
+{
+public:
+ void
+ configure(const std::string& faceUri)
+ {
+ createClientConf({"transport=" + faceUri});
+ }
+};
+
+class WithEnvAndConfig : public WithEnv, public WithConfig
+{
+};
+
+typedef boost::mpl::vector<WithEnv, WithConfig> ConfigOptions;
+
+BOOST_FIXTURE_TEST_CASE(NoConfig, WithEnvAndConfig) // fixture configures test HOME and PIB/TPM path
+{
+ shared_ptr<Face> face;
+ BOOST_REQUIRE_NO_THROW(face = make_shared<Face>());
+ BOOST_CHECK(dynamic_pointer_cast<UnixTransport>(face->getTransport()) != nullptr);
+}
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE(Unix, T, ConfigOptions, T)
+{
+ this->configure("unix://some/path");
+
+ shared_ptr<Face> face;
+ BOOST_REQUIRE_NO_THROW(face = make_shared<Face>());
+ BOOST_CHECK(dynamic_pointer_cast<UnixTransport>(face->getTransport()) != nullptr);
+}
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE(Tcp, T, ConfigOptions, T)
+{
+ this->configure("tcp://127.0.0.1:6000");
+
+ shared_ptr<Face> face;
+ BOOST_REQUIRE_NO_THROW(face = make_shared<Face>());
+ BOOST_CHECK(dynamic_pointer_cast<TcpTransport>(face->getTransport()) != nullptr);
+}
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE(WrongTransport, T, ConfigOptions, T)
+{
+ this->configure("wrong-transport:");
+
+ BOOST_CHECK_THROW(make_shared<Face>(), ConfigFile::Error);
+}
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE(WrongUri, T, ConfigOptions, T)
+{
+ this->configure("wrong-uri");
+
+ BOOST_CHECK_THROW(make_shared<Face>(), ConfigFile::Error);
+}
+
+BOOST_FIXTURE_TEST_CASE(EnvOverride, WithEnvAndConfig)
+{
+ this->WithEnv::configure("tcp://127.0.0.1:6000");
+ this->WithConfig::configure("unix://some/path");
+
+ shared_ptr<Face> face;
+ BOOST_REQUIRE_NO_THROW(face = make_shared<Face>());
+ BOOST_CHECK(dynamic_pointer_cast<TcpTransport>(face->getTransport()) != nullptr);
+}
+
+BOOST_FIXTURE_TEST_CASE(ExplicitTransport, WithEnvAndConfig)
+{
+ this->WithEnv::configure("wrong-uri");
+ this->WithConfig::configure("wrong-transport:");
+
+ auto transport = make_shared<UnixTransport>("unix://some/path");
+ shared_ptr<Face> face;
+ BOOST_REQUIRE_NO_THROW(face = make_shared<Face>(transport));
+ BOOST_CHECK(dynamic_pointer_cast<UnixTransport>(face->getTransport()) != nullptr);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // Transport
+
+BOOST_AUTO_TEST_SUITE_END() // TestFace
+
+} // namespace tests
+} // namespace ndn
diff --git a/tests/unit/identity-management-time-fixture.hpp b/tests/unit/identity-management-time-fixture.hpp
new file mode 100644
index 0000000..cafa9c6
--- /dev/null
+++ b/tests/unit/identity-management-time-fixture.hpp
@@ -0,0 +1,39 @@
+/* -*- 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.
+ */
+
+#ifndef NDN_TESTS_UNIT_TESTS_IDENTITY_MANAGEMENT_TIME_FIXTURE_HPP
+#define NDN_TESTS_UNIT_TESTS_IDENTITY_MANAGEMENT_TIME_FIXTURE_HPP
+
+#include "identity-management-fixture.hpp"
+#include "unit-test-time-fixture.hpp"
+
+namespace ndn {
+namespace tests {
+
+class IdentityManagementTimeFixture : public UnitTestTimeFixture
+ , public IdentityManagementFixture
+{
+};
+
+} // namespace tests
+} // namespace ndn
+
+#endif // NDN_TESTS_UNIT_TESTS_IDENTITY_MANAGEMENT_TIME_FIXTURE_HPP
diff --git a/tests/unit/ims/in-memory-storage-fifo.t.cpp b/tests/unit/ims/in-memory-storage-fifo.t.cpp
new file mode 100644
index 0000000..00cde40
--- /dev/null
+++ b/tests/unit/ims/in-memory-storage-fifo.t.cpp
@@ -0,0 +1,95 @@
+/* -*- 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 "ims/in-memory-storage-fifo.hpp"
+
+#include "boost-test.hpp"
+#include "make-interest-data.hpp"
+
+namespace ndn {
+namespace tests {
+
+using namespace ndn::tests;
+
+BOOST_AUTO_TEST_SUITE(Ims)
+BOOST_AUTO_TEST_SUITE(TestInMemoryStorageFifo)
+
+BOOST_AUTO_TEST_CASE(ArrivalQueue)
+{
+ InMemoryStorageFifo ims;
+
+ ims.insert(*makeData("/1"));
+ ims.insert(*makeData("/2"));
+ ims.insert(*makeData("/3"));
+
+ ims.evictItem();
+ BOOST_CHECK_EQUAL(ims.size(), 2);
+
+ shared_ptr<Interest> interest = makeInterest("/1");
+ shared_ptr<const Data> found = ims.find(*interest);
+ BOOST_CHECK(found == nullptr);
+}
+
+BOOST_AUTO_TEST_CASE(ArrivalQueue2)
+{
+ InMemoryStorageFifo ims;
+
+ ims.insert(*makeData("/1"));
+ ims.insert(*makeData("/2"));
+ ims.insert(*makeData("/3"));
+
+ ims.evictItem();
+ BOOST_CHECK_EQUAL(ims.size(), 2);
+
+ shared_ptr<Interest> interest1 = makeInterest("/1");
+ shared_ptr<const Data> found1 = ims.find(*interest1);
+ BOOST_CHECK(found1 == nullptr);
+
+ ims.insert(*makeData("/4"));
+
+ ims.evictItem();
+ BOOST_CHECK_EQUAL(ims.size(), 2);
+
+ shared_ptr<Interest> interest2 = makeInterest("/2");
+ shared_ptr<const Data> found2 = ims.find(*interest2);
+ BOOST_CHECK(found2 == nullptr);
+}
+
+BOOST_AUTO_TEST_CASE(MemoryPoolSizeZeroBug) // Bug #4769
+{
+ InMemoryStorageFifo ims;
+
+ BOOST_CHECK_EQUAL(ims.getCapacity(), 16);
+ for (int i = 1; i < 5; ++i) {
+ ims.insert(*makeData(to_string(i)));
+ ims.erase(Name(to_string(i)));
+ }
+
+ BOOST_CHECK_EQUAL(ims.getCapacity(), 16);
+ ims.insert(*makeData("/5"));
+ BOOST_CHECK_EQUAL(ims.getCapacity(), 16);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestInMemoryStorageFifo
+BOOST_AUTO_TEST_SUITE_END() // Ims
+
+} // namespace tests
+} // namespace ndn
diff --git a/tests/unit/ims/in-memory-storage-lfu.t.cpp b/tests/unit/ims/in-memory-storage-lfu.t.cpp
new file mode 100644
index 0000000..ef58ce7
--- /dev/null
+++ b/tests/unit/ims/in-memory-storage-lfu.t.cpp
@@ -0,0 +1,135 @@
+/* -*- 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 "ims/in-memory-storage-lfu.hpp"
+
+#include "boost-test.hpp"
+#include "make-interest-data.hpp"
+
+namespace ndn {
+namespace tests {
+
+using namespace ndn::tests;
+
+BOOST_AUTO_TEST_SUITE(Ims)
+BOOST_AUTO_TEST_SUITE(TestInMemoryStorageLfu)
+
+BOOST_AUTO_TEST_CASE(FrequencyQueue)
+{
+ InMemoryStorageLfu ims;
+
+ Name name1("/insert/1");
+ shared_ptr<Data> data1 = makeData(name1);
+ ims.insert(*data1);
+
+ Name name2("/insert/2");
+ shared_ptr<Data> data2 = makeData(name2);
+ ims.insert(*data2);
+
+ Name name3("/insert/3");
+ shared_ptr<Data> data3 = makeData(name3);
+ ims.insert(*data3);
+
+ shared_ptr<Interest> interest1 = makeInterest(name1);
+ shared_ptr<Interest> interest2 = makeInterest(name2);
+ shared_ptr<Interest> interest3 = makeInterest(name3);
+
+ ims.find(*interest1);
+ ims.find(*interest2);
+ ims.find(*interest3);
+ ims.find(*interest1);
+ ims.find(*interest3);
+ ims.find(*interest3);
+
+ ims.evictItem();
+ BOOST_CHECK_EQUAL(ims.size(), 2);
+
+ shared_ptr<const Data> found2 = ims.find(*interest2);
+ BOOST_CHECK(found2 == nullptr);
+
+ shared_ptr<const Data> found1 = ims.find(*interest1);
+ BOOST_CHECK_EQUAL(found1->getName(), name1);
+ shared_ptr<const Data> found3 = ims.find(*interest3);
+ BOOST_CHECK_EQUAL(found3->getName(), name3);
+}
+
+BOOST_AUTO_TEST_CASE(FrequencyQueue2)
+{
+ InMemoryStorageLfu ims;
+
+ Name name1("/insert/1");
+ shared_ptr<Data> data1 = makeData(name1);
+ ims.insert(*data1);
+
+ Name name2("/insert/2");
+ shared_ptr<Data> data2 = makeData(name2);
+ ims.insert(*data2);
+
+ Name name3("/insert/3");
+ shared_ptr<Data> data3 = makeData(name3);
+ ims.insert(*data3);
+
+ shared_ptr<Interest> interest1 = makeInterest(name1);
+ shared_ptr<Interest> interest2 = makeInterest(name2);
+ shared_ptr<Interest> interest3 = makeInterest(name3);
+
+ ims.find(*interest1);
+ ims.find(*interest2);
+ ims.find(*interest3);
+ ims.find(*interest1);
+ ims.find(*interest3);
+ ims.find(*interest3);
+
+ ims.evictItem();
+ BOOST_CHECK_EQUAL(ims.size(), 2);
+
+ shared_ptr<const Data> found2 = ims.find(*interest2);
+ BOOST_CHECK(found2 == nullptr);
+
+ shared_ptr<const Data> found1 = ims.find(*interest1);
+ BOOST_CHECK_EQUAL(found1->getName(), name1);
+ shared_ptr<const Data> found3 = ims.find(*interest3);
+ BOOST_CHECK_EQUAL(found3->getName(), name3);
+
+ Name name4("/insert/4");
+ shared_ptr<Data> data4 = makeData(name4);
+ ims.insert(*data4);
+
+ shared_ptr<Interest> interest4 = makeInterest(name4);
+ ims.find(*interest4);
+
+ ims.evictItem();
+ BOOST_CHECK_EQUAL(ims.size(), 2);
+
+ shared_ptr<const Data> found4 = ims.find(*interest4);
+ BOOST_CHECK(found4 == nullptr);
+
+ found1 = ims.find(*interest1);
+ BOOST_CHECK_EQUAL(found1->getName(), name1);
+ found3 = ims.find(*interest3);
+ BOOST_CHECK_EQUAL(found3->getName(), name3);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestInMemoryStorageLfu
+BOOST_AUTO_TEST_SUITE_END() // Ims
+
+} // namespace tests
+} // namespace ndn
diff --git a/tests/unit/ims/in-memory-storage-lru.t.cpp b/tests/unit/ims/in-memory-storage-lru.t.cpp
new file mode 100644
index 0000000..9058d66
--- /dev/null
+++ b/tests/unit/ims/in-memory-storage-lru.t.cpp
@@ -0,0 +1,137 @@
+/* -*- 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 "ims/in-memory-storage-lru.hpp"
+
+#include "boost-test.hpp"
+#include "make-interest-data.hpp"
+
+namespace ndn {
+namespace tests {
+
+using namespace ndn::tests;
+
+BOOST_AUTO_TEST_SUITE(Ims)
+BOOST_AUTO_TEST_SUITE(TestInMemoryStorageLru)
+
+BOOST_AUTO_TEST_CASE(UsedTimeQueue)
+{
+ InMemoryStorageLru ims;
+
+ Name name1("/insert/1");
+ shared_ptr<Data> data1 = makeData(name1);
+ ims.insert(*data1);
+
+ Name name2("/insert/2");
+ shared_ptr<Data> data2 = makeData(name2);
+ ims.insert(*data2);
+
+ Name name3("/insert/3");
+ shared_ptr<Data> data3 = makeData(name3);
+ ims.insert(*data3);
+
+ shared_ptr<Interest> interest1 = makeInterest(name1);
+ shared_ptr<Interest> interest2 = makeInterest(name2);
+ shared_ptr<Interest> interest3 = makeInterest(name3);
+
+ ims.find(*interest1);
+ ims.find(*interest2);
+ ims.find(*interest3);
+ ims.find(*interest1);
+ ims.find(*interest3);
+ ims.find(*interest3);
+
+ ims.evictItem();
+ BOOST_CHECK_EQUAL(ims.size(), 2);
+
+ shared_ptr<const Data> found2 = ims.find(*interest2);
+ BOOST_CHECK(found2 == nullptr);
+
+ shared_ptr<const Data> found1 = ims.find(*interest1);
+ BOOST_CHECK_EQUAL(found1->getName(), name1);
+ shared_ptr<const Data> found3 = ims.find(*interest3);
+ BOOST_CHECK_EQUAL(found3->getName(), name3);
+}
+
+BOOST_AUTO_TEST_CASE(UsedTimeQueue2)
+{
+ InMemoryStorageLru ims;
+
+ Name name1("/insert/1");
+ shared_ptr<Data> data = makeData(name1);
+ ims.insert(*data);
+
+ Name name2("/insert/2");
+ shared_ptr<Data> data2 = makeData(name2);
+ ims.insert(*data2);
+
+ Name name3("/insert/3");
+ shared_ptr<Data> data3 = makeData(name3);
+ ims.insert(*data3);
+
+ shared_ptr<Interest> interest1 = makeInterest(name1);
+ shared_ptr<Interest> interest2 = makeInterest(name2);
+ shared_ptr<Interest> interest3 = makeInterest(name3);
+
+ ims.find(*interest1);
+ ims.find(*interest2);
+ ims.find(*interest3);
+ ims.find(*interest1);
+ ims.find(*interest3);
+ ims.find(*interest3);
+
+ ims.evictItem();
+ BOOST_CHECK_EQUAL(ims.size(), 2);
+
+ shared_ptr<const Data> found2 = ims.find(*interest2);
+ BOOST_CHECK(found2 == nullptr);
+
+ shared_ptr<const Data> found1 = ims.find(*interest1);
+ BOOST_CHECK_EQUAL(found1->getName(), name1);
+ shared_ptr<const Data> found3 = ims.find(*interest3);
+ BOOST_CHECK_EQUAL(found3->getName(), name3);
+
+ Name name4("/insert/4");
+ shared_ptr<Data> data4 = makeData(name4);
+ ims.insert(*data4);
+
+ shared_ptr<Interest> interest4 = makeInterest(name4);
+ ims.find(*interest4);
+ ims.find(*interest1);
+ ims.find(*interest3);
+
+ ims.evictItem();
+ BOOST_CHECK_EQUAL(ims.size(), 2);
+
+ shared_ptr<const Data> found4 = ims.find(*interest4);
+ BOOST_CHECK(found4 == nullptr);
+
+ found1 = ims.find(*interest1);
+ BOOST_CHECK_EQUAL(found1->getName(), name1);
+ found3 = ims.find(*interest3);
+ BOOST_CHECK_EQUAL(found3->getName(), name3);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestInMemoryStorageLru
+BOOST_AUTO_TEST_SUITE_END() // Ims
+
+} // namespace tests
+} // namespace ndn
diff --git a/tests/unit/ims/in-memory-storage-persistent.t.cpp b/tests/unit/ims/in-memory-storage-persistent.t.cpp
new file mode 100644
index 0000000..d63061f
--- /dev/null
+++ b/tests/unit/ims/in-memory-storage-persistent.t.cpp
@@ -0,0 +1,83 @@
+/* -*- 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 "ims/in-memory-storage-persistent.hpp"
+
+#include "boost-test.hpp"
+#include "make-interest-data.hpp"
+
+namespace ndn {
+namespace tests {
+
+using namespace ndn::tests;
+
+BOOST_AUTO_TEST_SUITE(Ims)
+BOOST_AUTO_TEST_SUITE(TestInMemoryStoragePersistent)
+
+BOOST_AUTO_TEST_CASE(GetLimit)
+{
+ InMemoryStoragePersistent ims;
+
+ BOOST_CHECK_EQUAL(ims.getLimit(), -1);
+}
+
+BOOST_AUTO_TEST_CASE(InsertAndDouble)
+{
+ InMemoryStoragePersistent ims;
+ size_t initialCapacity = ims.getCapacity();
+
+ for (size_t i = 0; i < initialCapacity + 1; i++) {
+ shared_ptr<Data> data = makeData(to_string(i));
+ data->setFreshnessPeriod(5000_ms);
+ signData(data);
+ ims.insert(*data);
+ }
+
+ BOOST_CHECK_EQUAL(ims.size(), initialCapacity + 1);
+
+ BOOST_CHECK_EQUAL(ims.getCapacity(), initialCapacity * 2);
+}
+
+BOOST_AUTO_TEST_CASE(EraseAndShrink)
+{
+ InMemoryStoragePersistent ims;
+
+ auto capacity = ims.getCapacity() * 2;
+ ims.setCapacity(capacity);
+
+ Name name("/1");
+ shared_ptr<Data> data = makeData(name);
+ data->setFreshnessPeriod(5000_ms);
+ signData(data);
+ ims.insert(*data);
+ BOOST_CHECK_EQUAL(ims.size(), 1);
+
+ ims.erase(name);
+
+ BOOST_CHECK_EQUAL(ims.size(), 0);
+ BOOST_CHECK_EQUAL(ims.getCapacity(), capacity / 2);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestInMemoryStoragePersistent
+BOOST_AUTO_TEST_SUITE_END() // Ims
+
+} // namespace tests
+} // namespace ndn
diff --git a/tests/unit/ims/in-memory-storage.t.cpp b/tests/unit/ims/in-memory-storage.t.cpp
new file mode 100644
index 0000000..d955d72
--- /dev/null
+++ b/tests/unit/ims/in-memory-storage.t.cpp
@@ -0,0 +1,1058 @@
+/* -*- 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 "ims/in-memory-storage.hpp"
+#include "ims/in-memory-storage-fifo.hpp"
+#include "ims/in-memory-storage-lfu.hpp"
+#include "ims/in-memory-storage-lru.hpp"
+#include "ims/in-memory-storage-persistent.hpp"
+#include "security/signature-sha256-with-rsa.hpp"
+#include "util/sha256.hpp"
+
+#include "boost-test.hpp"
+#include "make-interest-data.hpp"
+#include "../unit-test-time-fixture.hpp"
+
+#include <boost/mpl/vector.hpp>
+
+namespace ndn {
+namespace tests {
+
+using namespace ndn::tests;
+
+BOOST_AUTO_TEST_SUITE(Ims)
+BOOST_AUTO_TEST_SUITE(TestInMemoryStorage)
+
+using InMemoryStorages = boost::mpl::vector<InMemoryStoragePersistent,
+ InMemoryStorageFifo,
+ InMemoryStorageLfu,
+ InMemoryStorageLru>;
+
+BOOST_AUTO_TEST_CASE_TEMPLATE(Insertion, T, InMemoryStorages)
+{
+ T ims;
+
+ ims.insert(*makeData("/a"));
+ ims.insert(*makeData("/b"));
+ ims.insert(*makeData("/c"));
+ ims.insert(*makeData("/d"));
+
+ BOOST_CHECK_EQUAL(ims.size(), 4);
+}
+
+BOOST_AUTO_TEST_CASE_TEMPLATE(Insertion2, T, InMemoryStorages)
+{
+ T ims;
+
+ Name name("/a");
+
+ uint32_t content1 = 1;
+ shared_ptr<Data> data1 = makeData(name);
+ data1->setFreshnessPeriod(99999_ms);
+ data1->setContent(reinterpret_cast<const uint8_t*>(&content1), sizeof(content1));
+ signData(data1);
+ ims.insert(*data1);
+
+ uint32_t content2 = 2;
+ shared_ptr<Data> data2 = makeData(name);
+ data2->setFreshnessPeriod(99999_ms);
+ data2->setContent(reinterpret_cast<const uint8_t*>(&content2), sizeof(content2));
+ signData(data2);
+ ims.insert(*data2);
+
+ BOOST_CHECK_EQUAL(ims.size(), 2);
+}
+
+BOOST_AUTO_TEST_CASE_TEMPLATE(DuplicateInsertion, T, InMemoryStorages)
+{
+ T ims;
+
+ shared_ptr<Data> data0 = makeData("/insert/smth");
+ ims.insert(*data0);
+
+ shared_ptr<Data> data = makeData("/insert/duplicate");
+ ims.insert(*data);
+
+ ims.insert(*data);
+ BOOST_CHECK_EQUAL(ims.size(), 2);
+}
+
+BOOST_AUTO_TEST_CASE_TEMPLATE(DuplicateInsertion2, T, InMemoryStorages)
+{
+ T ims;
+
+ shared_ptr<Data> data = makeData("/insert/duplicate");
+ ims.insert(*data);
+
+ ims.insert(*data);
+ BOOST_CHECK_EQUAL(ims.size(), 1);
+
+ shared_ptr<Data> data2 = makeData("/insert/original");
+ ims.insert(*data2);
+ BOOST_CHECK_EQUAL(ims.size(), 2);
+}
+
+BOOST_AUTO_TEST_CASE_TEMPLATE(InsertAndFind, T, InMemoryStorages)
+{
+ T ims;
+
+ Name name("/insert/and/find");
+
+ shared_ptr<Data> data = makeData(name);
+ ims.insert(*data);
+
+ shared_ptr<Interest> interest = makeInterest(name);
+
+ shared_ptr<const Data> found = ims.find(*interest);
+ BOOST_CHECK(found != nullptr);
+ BOOST_CHECK_EQUAL(data->getName(), found->getName());
+}
+
+BOOST_AUTO_TEST_CASE_TEMPLATE(InsertAndNotFind, T, InMemoryStorages)
+{
+ T ims;
+
+ Name name("/insert/and/find");
+ shared_ptr<Data> data = makeData(name);
+ ims.insert(*data);
+
+ Name name2("/not/find");
+ shared_ptr<Interest> interest = makeInterest(name2);
+
+ shared_ptr<const Data> found = ims.find(*interest);
+
+ BOOST_CHECK_EQUAL(found.get(), static_cast<const Data*>(0));
+}
+
+BOOST_AUTO_TEST_CASE_TEMPLATE(InsertAndFindByName, T, InMemoryStorages)
+{
+ T ims;
+
+ Name name("/insert/and/find");
+
+ shared_ptr<Data> data = makeData(name);
+ ims.insert(*data);
+
+ shared_ptr<const Data> found = ims.find(name);
+ BOOST_CHECK(found != nullptr);
+ BOOST_CHECK_EQUAL(data->getName(), found->getName());
+}
+
+BOOST_AUTO_TEST_CASE_TEMPLATE(InsertAndFindByFullName, T, InMemoryStorages)
+{
+ T ims;
+
+ Name name("/insert/and/find");
+
+ shared_ptr<Data> data = makeData(name);
+ ims.insert(*data);
+
+ shared_ptr<const Data> found = ims.find(data->getFullName());
+ BOOST_CHECK(found != nullptr);
+ BOOST_CHECK_EQUAL(data->getFullName(), found->getFullName());
+}
+
+BOOST_AUTO_TEST_CASE_TEMPLATE(InsertAndNotFindByName, T, InMemoryStorages)
+{
+ T ims;
+
+ Name name("/insert/and/find");
+ shared_ptr<Data> data = makeData(name);
+ ims.insert(*data);
+
+ Name name2("/not/find");
+
+ shared_ptr<const Data> found = ims.find(name2);
+ BOOST_CHECK(found == nullptr);
+}
+
+BOOST_AUTO_TEST_CASE_TEMPLATE(InsertAndNotFindByFullName, T, InMemoryStorages)
+{
+ T ims;
+
+ Name name("/a");
+ uint32_t content1 = 1;
+ shared_ptr<Data> data1 = makeData(name);
+ data1->setContent(reinterpret_cast<const uint8_t*>(&content1), sizeof(content1));
+ signData(data1);
+ ims.insert(*data1);
+
+ uint32_t content2 = 2;
+ shared_ptr<Data> data2 = makeData(name);
+ data2->setContent(reinterpret_cast<const uint8_t*>(&content2), sizeof(content2));
+ signData(data2);
+
+ shared_ptr<const Data> found = ims.find(data2->getFullName());
+ BOOST_CHECK(found == nullptr);
+}
+
+BOOST_AUTO_TEST_CASE_TEMPLATE(InsertAndEraseByName, T, InMemoryStorages)
+{
+ T ims;
+
+ Name name("/insertandremovebyname");
+
+ uint32_t content1 = 1;
+ shared_ptr<Data> data1 = makeData(name);
+ data1->setFreshnessPeriod(99999_ms);
+ data1->setContent(reinterpret_cast<const uint8_t*>(&content1), sizeof(content1));
+ signData(data1);
+ ims.insert(*data1);
+
+ uint32_t content2 = 2;
+ shared_ptr<Data> data2 = makeData(name);
+ data2->setFreshnessPeriod(99999_ms);
+ data2->setContent(reinterpret_cast<const uint8_t*>(&content2), sizeof(content2));
+ signData(data2);
+ ims.insert(*data2);
+
+ shared_ptr<Data> data3 = makeData("/insertandremovebyname/1");
+ ims.insert(*data3);
+
+ shared_ptr<Data> data4 = makeData("/insertandremovebyname/2");
+ ims.insert(*data4);
+
+ BOOST_CHECK_EQUAL(ims.size(), 4);
+
+ ims.erase(data1->getFullName(), false);
+ BOOST_CHECK_EQUAL(ims.size(), 3);
+}
+
+BOOST_AUTO_TEST_CASE_TEMPLATE(InsertAndEraseByPrefix, T, InMemoryStorages)
+{
+ T ims;
+
+ shared_ptr<Data> data = makeData("/a");
+ ims.insert(*data);
+
+ shared_ptr<Data> data2 = makeData("/b");
+ ims.insert(*data2);
+
+ shared_ptr<Data> data3 = makeData("/c");
+ ims.insert(*data3);
+
+ shared_ptr<Data> data4 = makeData("/d");
+ ims.insert(*data4);
+
+ shared_ptr<Data> data5 = makeData("/c/c/1/2/3/4/5/6");
+ ims.insert(*data5);
+
+ shared_ptr<Data> data6 = makeData("/c/c/1/2/3");
+ ims.insert(*data6);
+
+ shared_ptr<Data> data7 = makeData("/c/c/1");
+ ims.insert(*data7);
+
+ BOOST_CHECK_EQUAL(ims.size(), 7);
+
+ Name name("/c");
+ ims.erase(name);
+ BOOST_CHECK_EQUAL(ims.size(), 3);
+ BOOST_CHECK_EQUAL(ims.getCapacity(), 16);
+}
+
+BOOST_AUTO_TEST_CASE_TEMPLATE(DigestCalculation, T, InMemoryStorages)
+{
+ shared_ptr<Data> data = makeData("/digest/compute");
+
+ ConstBufferPtr digest1 = util::Sha256::computeDigest(data->wireEncode().wire(), data->wireEncode().size());
+ BOOST_CHECK_EQUAL(digest1->size(), 32);
+
+ InMemoryStorageEntry entry;
+ entry.setData(*data);
+
+ BOOST_CHECK_EQUAL_COLLECTIONS(digest1->begin(), digest1->end(),
+ entry.getFullName()[-1].value_begin(),
+ entry.getFullName()[-1].value_end());
+}
+
+BOOST_AUTO_TEST_CASE_TEMPLATE(Iterator, T, InMemoryStorages)
+{
+ T ims;
+
+ BOOST_CONCEPT_ASSERT((boost::InputIterator<InMemoryStorage::const_iterator>));
+
+ for (int i = 0; i < 10; i++) {
+ std::ostringstream convert;
+ convert << i;
+ Name name("/" + convert.str());
+ shared_ptr<Data> data = makeData(name);
+ ims.insert(*data);
+ }
+
+ InMemoryStorage::const_iterator it = ims.begin();
+ InMemoryStorage::const_iterator tmp1 = it;
+ BOOST_REQUIRE(tmp1 == it);
+ InMemoryStorage::const_iterator tmp2 = tmp1++;
+ BOOST_REQUIRE(tmp2 != tmp1);
+ tmp2 = ++tmp1;
+ BOOST_REQUIRE(tmp2 == tmp1);
+
+ int i = 0;
+ for (; it != ims.end(); it++) {
+ std::ostringstream convert;
+ convert << i;
+ Name name("/" + convert.str());
+ BOOST_CHECK_EQUAL(it->getName(), name);
+ BOOST_CHECK_EQUAL((*it).getName(), name);
+ i++;
+ }
+}
+
+BOOST_AUTO_TEST_CASE_TEMPLATE(InsertCanonical, T, InMemoryStorages)
+{
+ T ims;
+
+ shared_ptr<Data> data = makeData("/a");
+ ims.insert(*data);
+
+ shared_ptr<Data> data2 = makeData("/b");
+ ims.insert(*data2);
+
+ shared_ptr<Data> data3 = makeData("/c");
+ ims.insert(*data3);
+
+ shared_ptr<Data> data4 = makeData("/d");
+ ims.insert(*data4);
+
+ shared_ptr<Data> data5 = makeData("/c/c/1/2/3/4/5/6");
+ ims.insert(*data5);
+
+ shared_ptr<Data> data6 = makeData("/c/c/1/2/3");
+ ims.insert(*data6);
+
+ shared_ptr<Data> data7 = makeData("/c/c/1");
+ ims.insert(*data7);
+
+ // avoid "test case [...] did not check any assertions" message from Boost.Test
+ BOOST_CHECK(true);
+}
+
+BOOST_AUTO_TEST_CASE_TEMPLATE(EraseCanonical, T, InMemoryStorages)
+{
+ T ims;
+
+ shared_ptr<Data> data = makeData("/a");
+ ims.insert(*data);
+
+ shared_ptr<Data> data2 = makeData("/b");
+ ims.insert(*data2);
+
+ shared_ptr<Data> data3 = makeData("/c");
+ ims.insert(*data3);
+
+ shared_ptr<Data> data4 = makeData("/d");
+ ims.insert(*data4);
+
+ shared_ptr<Data> data5 = makeData("/c/c/1/2/3/4/5/6");
+ ims.insert(*data5);
+
+ shared_ptr<Data> data6 = makeData("/c/c/1/2/3");
+ ims.insert(*data6);
+
+ shared_ptr<Data> data7 = makeData("/c/c/1");
+ ims.insert(*data7);
+
+ ConstBufferPtr digest1 = util::Sha256::computeDigest(data->wireEncode().wire(), data->wireEncode().size());
+
+ Name name("/a");
+ ims.erase(name);
+ BOOST_CHECK_EQUAL(ims.size(), 6);
+}
+
+BOOST_AUTO_TEST_CASE_TEMPLATE(ImplicitDigestSelector, T, InMemoryStorages)
+{
+ T ims;
+
+ Name name("/digest/works");
+ shared_ptr<Data> data = makeData(name);
+ ims.insert(*data);
+
+ shared_ptr<Data> data2 = makeData("/a");
+ ims.insert(*data2);
+
+ shared_ptr<Data> data3 = makeData("/z/z/z");
+ ims.insert(*data3);
+
+ ConstBufferPtr digest1 = util::Sha256::computeDigest(data->wireEncode().wire(), data->wireEncode().size());
+
+ shared_ptr<Interest> interest = makeInterest("");
+ interest->setName(Name(name).appendImplicitSha256Digest(digest1->data(), digest1->size()));
+ interest->setMinSuffixComponents(0);
+ interest->setMaxSuffixComponents(0);
+
+ shared_ptr<const Data> found = ims.find(*interest);
+ BOOST_REQUIRE(found != nullptr);
+ BOOST_CHECK_EQUAL(found->getName(), name);
+
+ shared_ptr<Interest> interest2 = makeInterest("");
+ uint8_t digest2[32] = {0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1};
+ interest2->setName(Name(name).appendImplicitSha256Digest(digest2, 32));
+ interest2->setMinSuffixComponents(0);
+ interest2->setMaxSuffixComponents(0);
+
+ shared_ptr<const Data> notfound = ims.find(*interest2);
+ BOOST_CHECK(notfound == nullptr);
+}
+
+BOOST_AUTO_TEST_CASE_TEMPLATE(ChildSelector, T, InMemoryStorages)
+{
+ T ims;
+
+ shared_ptr<Data> data = makeData("/a");
+ ims.insert(*data);
+
+ shared_ptr<Data> data2 = makeData("/b");
+ ims.insert(*data2);
+
+ shared_ptr<Data> data4 = makeData("/d");
+ ims.insert(*data4);
+
+ shared_ptr<Data> data5 = makeData("/c/c");
+ ims.insert(*data5);
+
+ shared_ptr<Data> data6 = makeData("/c/f");
+ ims.insert(*data6);
+
+ shared_ptr<Data> data7 = makeData("/c/n");
+ ims.insert(*data7);
+
+ shared_ptr<Interest> interest = makeInterest("/c", true);
+ interest->setChildSelector(1);
+
+ shared_ptr<const Data> found = ims.find(*interest);
+ BOOST_REQUIRE(found != nullptr);
+ BOOST_CHECK_EQUAL(found->getName(), "/c/n");
+
+ shared_ptr<Interest> interest2 = makeInterest("/c", true);
+ interest2->setChildSelector(0);
+
+ shared_ptr<const Data> found2 = ims.find(*interest2);
+ BOOST_REQUIRE(found2 != nullptr);
+ BOOST_CHECK_EQUAL(found2->getName(), "/c/c");
+}
+
+BOOST_AUTO_TEST_CASE_TEMPLATE(ChildSelector2, T, InMemoryStorages)
+{
+ T ims;
+
+ shared_ptr<Data> data = makeData("/a/b/1");
+ ims.insert(*data);
+
+ shared_ptr<Data> data2 = makeData("/a/b/2");
+ ims.insert(*data2);
+
+ shared_ptr<Data> data3 = makeData("/a/z/1");
+ ims.insert(*data3);
+
+ shared_ptr<Data> data4 = makeData("/a/z/2");
+ ims.insert(*data4);
+
+ shared_ptr<Interest> interest = makeInterest("/a", true);
+ interest->setChildSelector(1);
+
+ shared_ptr<const Data> found = ims.find(*interest);
+ BOOST_REQUIRE(found != nullptr);
+ BOOST_CHECK_EQUAL(found->getName(), "/a/z/1");
+}
+
+BOOST_AUTO_TEST_CASE_TEMPLATE(PublisherKeySelector, T, InMemoryStorages)
+{
+ T ims;
+
+ Name name("/insert/withkey");
+ shared_ptr<Data> data = makeData(name);
+ ims.insert(*data);
+
+ shared_ptr<Interest> interest = makeInterest(name);
+ Name keyName("/somewhere/key");
+
+ KeyLocator locator(keyName);
+ interest->setPublisherPublicKeyLocator(locator);
+
+ shared_ptr<const Data> found = ims.find(*interest);
+ BOOST_CHECK(found == nullptr);
+}
+
+BOOST_AUTO_TEST_CASE_TEMPLATE(PublisherKeySelector2, T, InMemoryStorages)
+{
+ T ims;
+ Name name("/insert/withkey");
+ shared_ptr<Data> data = makeData(name);
+ ims.insert(*data);
+
+ Name name2("/insert/withkey2");
+ shared_ptr<Data> data2 = make_shared<Data>(name2);
+
+ Name keyName("/somewhere/key");
+ const KeyLocator locator(keyName);
+
+ SignatureSha256WithRsa fakeSignature;
+ fakeSignature.setValue(makeEmptyBlock(tlv::SignatureValue));
+ fakeSignature.setKeyLocator(locator);
+ data2->setSignature(fakeSignature);
+ data2->wireEncode();
+
+ ims.insert(*data2);
+
+ shared_ptr<Interest> interest = makeInterest(name2);
+ interest->setPublisherPublicKeyLocator(locator);
+
+ shared_ptr<const Data> found = ims.find(*interest);
+ BOOST_CHECK(found != nullptr);
+ BOOST_CHECK_EQUAL(found->getName(), data2->getName());
+}
+
+BOOST_AUTO_TEST_CASE_TEMPLATE(MinMaxComponentsSelector, T, InMemoryStorages)
+{
+ T ims;
+
+ shared_ptr<Data> data = makeData("/a");
+ ims.insert(*data);
+
+ shared_ptr<Data> data2 = makeData("/b");
+ ims.insert(*data2);
+
+ shared_ptr<Data> data4 = makeData("/d");
+ ims.insert(*data4);
+
+ shared_ptr<Data> data5 = makeData("/c/c/1/2/3/4/5/6");
+ ims.insert(*data5);
+
+ shared_ptr<Data> data6 = makeData("/c/c/6/7/8/9");
+ ims.insert(*data6);
+
+ shared_ptr<Data> data7 = makeData("/c/c/1/2/3");
+ ims.insert(*data7);
+
+ shared_ptr<Data> data8 = makeData("/c/c/1");
+ ims.insert(*data8);
+
+ shared_ptr<Interest> interest = makeInterest("/c/c", true);
+ interest->setMinSuffixComponents(3);
+ interest->setChildSelector(0);
+
+ shared_ptr<const Data> found = ims.find(*interest);
+ BOOST_REQUIRE(found != nullptr);
+ BOOST_CHECK_EQUAL(found->getName(), "/c/c/1/2/3");
+
+ shared_ptr<Interest> interest2 = makeInterest("/c/c", true);
+ interest2->setMinSuffixComponents(4);
+ interest2->setChildSelector(1);
+
+ shared_ptr<const Data> found2 = ims.find(*interest2);
+ BOOST_REQUIRE(found2 != nullptr);
+ BOOST_CHECK_EQUAL(found2->getName(), "/c/c/6/7/8/9");
+
+ shared_ptr<Interest> interest3 = makeInterest("/c/c");
+ interest3->setMaxSuffixComponents(2);
+ interest3->setChildSelector(1);
+
+ shared_ptr<const Data> found3 = ims.find(*interest3);
+ BOOST_REQUIRE(found3 != nullptr);
+ BOOST_CHECK_EQUAL(found3->getName(), "/c/c/1");
+}
+
+BOOST_AUTO_TEST_CASE_TEMPLATE(ExcludeSelector, T, InMemoryStorages)
+{
+ T ims;
+
+ shared_ptr<Data> data = makeData("/a");
+ ims.insert(*data);
+
+ shared_ptr<Data> data2 = makeData("/b");
+ ims.insert(*data2);
+
+ shared_ptr<Data> data3 = makeData("/c/a");
+ ims.insert(*data3);
+
+ shared_ptr<Data> data4 = makeData("/d");
+ ims.insert(*data4);
+
+ shared_ptr<Data> data5 = makeData("/c/c");
+ ims.insert(*data5);
+
+ shared_ptr<Data> data6 = makeData("/c/f");
+ ims.insert(*data6);
+
+ shared_ptr<Data> data7 = makeData("/c/n");
+ ims.insert(*data7);
+
+ shared_ptr<Interest> interest = makeInterest("/c", true);
+ interest->setChildSelector(1);
+ Exclude e;
+ e.excludeOne(Name::Component("n"));
+ interest->setExclude(e);
+
+ shared_ptr<const Data> found = ims.find(*interest);
+ BOOST_REQUIRE(found != nullptr);
+ BOOST_CHECK_EQUAL(found->getName(), "/c/f");
+
+ shared_ptr<Interest> interest2 = makeInterest("/c", true);
+ interest2->setChildSelector(0);
+ Exclude e2;
+ e2.excludeOne(Name::Component("a"));
+ interest2->setExclude(e2);
+
+ shared_ptr<const Data> found2 = ims.find(*interest2);
+ BOOST_REQUIRE(found2 != nullptr);
+ BOOST_CHECK_EQUAL(found2->getName(), "/c/c");
+
+ shared_ptr<Interest> interest3 = makeInterest("/c", true);
+ interest3->setChildSelector(0);
+ Exclude e3;
+ e3.excludeOne(Name::Component("c"));
+ interest3->setExclude(e3);
+
+ shared_ptr<const Data> found3 = ims.find(*interest3);
+ BOOST_REQUIRE(found3 != nullptr);
+ BOOST_CHECK_EQUAL(found3->getName(), "/c/a");
+}
+
+using InMemoryStoragesLimited = boost::mpl::vector<InMemoryStorageFifo,
+ InMemoryStorageLfu,
+ InMemoryStorageLru>;
+
+BOOST_AUTO_TEST_CASE_TEMPLATE(SetCapacity, T, InMemoryStoragesLimited)
+{
+ T ims;
+
+ ims.setCapacity(18);
+ for (int i = 1; i < 19; ++i) {
+ ims.insert(*makeData(to_string(i)));
+ }
+ BOOST_CHECK_EQUAL(ims.size(), 18);
+
+ ims.setCapacity(16);
+ BOOST_CHECK_EQUAL(ims.size(), 16);
+}
+
+BOOST_AUTO_TEST_CASE_TEMPLATE(GetLimit, T, InMemoryStoragesLimited)
+{
+ T ims(10000);
+ BOOST_CHECK_EQUAL(ims.getLimit(), 10000);
+
+ T ims2(4);
+ BOOST_CHECK_EQUAL(ims2.getLimit(), 4);
+}
+
+BOOST_AUTO_TEST_CASE_TEMPLATE(InsertAndDouble, T, InMemoryStoragesLimited)
+{
+ T ims(40);
+ size_t initialCapacity = ims.getCapacity();
+
+ for (size_t i = 0; i < initialCapacity + 1; i++) {
+ shared_ptr<Data> data = makeData(to_string(i));
+ data->setFreshnessPeriod(5000_ms);
+ signData(data);
+ ims.insert(*data);
+ }
+
+ BOOST_CHECK_EQUAL(ims.size(), initialCapacity + 1);
+ BOOST_CHECK_EQUAL(ims.getCapacity(), initialCapacity * 2);
+}
+
+BOOST_AUTO_TEST_CASE_TEMPLATE(InsertAndEvict, T, InMemoryStoragesLimited)
+{
+ T ims(2);
+
+ Name name("/insert/1");
+ shared_ptr<Data> data = makeData(name);
+ ims.insert(*data);
+
+ Name name2("/insert/2");
+ shared_ptr<Data> data2 = makeData(name2);
+ ims.insert(*data2);
+
+ Name name3("/insert/3");
+ shared_ptr<Data> data3 = makeData(name3);
+ ims.insert(*data3);
+
+ BOOST_CHECK_EQUAL(ims.size(), 2);
+
+ shared_ptr<Interest> interest = makeInterest(name);
+ shared_ptr<const Data> found = ims.find(*interest);
+ BOOST_CHECK(found == nullptr);
+}
+
+// Find function is implemented at the base case, so it's sufficient to test for one derived class.
+class FindFixture : public tests::UnitTestTimeFixture
+{
+protected:
+ FindFixture()
+ : m_ims(io)
+ {
+ }
+
+ Name
+ insert(uint32_t id, const Name& name,
+ const time::milliseconds& freshWindow = InMemoryStorage::INFINITE_WINDOW)
+ {
+ shared_ptr<Data> data = makeData(name);
+ data->setFreshnessPeriod(99999_ms);
+ data->setContent(reinterpret_cast<const uint8_t*>(&id), sizeof(id));
+ signData(data);
+
+ m_ims.insert(*data, freshWindow);
+
+ return data->getFullName();
+ }
+
+ Interest&
+ startInterest(const Name& name)
+ {
+ m_interest = makeInterest(name, true);
+ return *m_interest;
+ }
+
+ uint32_t
+ find()
+ {
+ shared_ptr<const Data> found = m_ims.find(*m_interest);
+ if (found == 0) {
+ return 0;
+ }
+ const Block& content = found->getContent();
+ if (content.value_size() != sizeof(uint32_t)) {
+ return 0;
+ }
+ uint32_t id = 0;
+ std::memcpy(&id, content.value(), sizeof(id));
+ return id;
+ }
+
+protected:
+ InMemoryStoragePersistent m_ims;
+ shared_ptr<Interest> m_interest;
+};
+
+BOOST_FIXTURE_TEST_SUITE(Find, FindFixture)
+
+BOOST_AUTO_TEST_CASE(EmptyDataName)
+{
+ insert(1, "ndn:/");
+
+ startInterest("ndn:/");
+ BOOST_CHECK_EQUAL(find(), 1);
+}
+
+BOOST_AUTO_TEST_CASE(EmptyInterestName)
+{
+ insert(1, "ndn:/A");
+
+ startInterest("ndn:/");
+ BOOST_CHECK_EQUAL(find(), 1);
+}
+
+BOOST_AUTO_TEST_CASE(ExactName)
+{
+ insert(1, "ndn:/");
+ insert(2, "ndn:/A");
+ insert(3, "ndn:/A/B");
+ insert(4, "ndn:/A/C");
+ insert(5, "ndn:/D");
+
+ startInterest("ndn:/A");
+ BOOST_CHECK_EQUAL(find(), 2);
+}
+
+BOOST_AUTO_TEST_CASE(FullName)
+{
+ Name n1 = insert(1, "ndn:/A");
+ Name n2 = insert(2, "ndn:/A");
+
+ startInterest(n1);
+ BOOST_CHECK_EQUAL(find(), 1);
+
+ startInterest(n2);
+ BOOST_CHECK_EQUAL(find(), 2);
+}
+
+BOOST_AUTO_TEST_CASE(Leftmost)
+{
+ insert(1, "ndn:/A");
+ insert(2, "ndn:/B/p/1");
+ insert(3, "ndn:/B/p/2");
+ insert(4, "ndn:/B/q/1");
+ insert(5, "ndn:/B/q/2");
+ insert(6, "ndn:/C");
+
+ startInterest("ndn:/B");
+ BOOST_CHECK_EQUAL(find(), 2);
+}
+
+BOOST_AUTO_TEST_CASE(Rightmost)
+{
+ insert(1, "ndn:/A");
+ insert(2, "ndn:/B/p/1");
+ insert(3, "ndn:/B/p/2");
+ insert(4, "ndn:/B/q/1");
+ insert(5, "ndn:/B/q/2");
+ insert(6, "ndn:/C");
+
+ startInterest("ndn:/B")
+ .setChildSelector(1);
+ BOOST_CHECK_EQUAL(find(), 4);
+}
+
+BOOST_AUTO_TEST_CASE(MinSuffixComponents)
+{
+ insert(1, "ndn:/");
+ insert(2, "ndn:/A");
+ insert(3, "ndn:/B/1");
+ insert(4, "ndn:/C/1/2");
+ insert(5, "ndn:/D/1/2/3");
+ insert(6, "ndn:/E/1/2/3/4");
+
+ startInterest("ndn:/")
+ .setMinSuffixComponents(0);
+ BOOST_CHECK_EQUAL(find(), 1);
+
+ startInterest("ndn:/")
+ .setMinSuffixComponents(1);
+ BOOST_CHECK_EQUAL(find(), 1);
+
+ startInterest("ndn:/")
+ .setMinSuffixComponents(2);
+ BOOST_CHECK_EQUAL(find(), 2);
+
+ startInterest("ndn:/")
+ .setMinSuffixComponents(3);
+ BOOST_CHECK_EQUAL(find(), 3);
+
+ startInterest("ndn:/")
+ .setMinSuffixComponents(4);
+ BOOST_CHECK_EQUAL(find(), 4);
+
+ startInterest("ndn:/")
+ .setMinSuffixComponents(5);
+ BOOST_CHECK_EQUAL(find(), 5);
+
+ startInterest("ndn:/")
+ .setMinSuffixComponents(6);
+ BOOST_CHECK_EQUAL(find(), 6);
+
+ startInterest("ndn:/")
+ .setMinSuffixComponents(7);
+ BOOST_CHECK_EQUAL(find(), 0);
+}
+
+BOOST_AUTO_TEST_CASE(MaxSuffixComponents)
+{
+ insert(1, "ndn:/");
+ insert(2, "ndn:/A");
+ insert(3, "ndn:/B/2");
+ insert(4, "ndn:/C/2/3");
+ insert(5, "ndn:/D/2/3/4");
+ insert(6, "ndn:/E/2/3/4/5");
+
+ startInterest("ndn:/")
+ .setChildSelector(1)
+ .setMaxSuffixComponents(0);
+ BOOST_CHECK_EQUAL(find(), 0);
+
+ startInterest("ndn:/")
+ .setChildSelector(1)
+ .setMaxSuffixComponents(1);
+ BOOST_CHECK_EQUAL(find(), 1);
+
+ startInterest("ndn:/")
+ .setChildSelector(1)
+ .setMaxSuffixComponents(2);
+ BOOST_CHECK_EQUAL(find(), 2);
+
+ startInterest("ndn:/")
+ .setChildSelector(1)
+ .setMaxSuffixComponents(3);
+ BOOST_CHECK_EQUAL(find(), 3);
+
+ startInterest("ndn:/")
+ .setChildSelector(1)
+ .setMaxSuffixComponents(4);
+ BOOST_CHECK_EQUAL(find(), 4);
+
+ startInterest("ndn:/")
+ .setChildSelector(1)
+ .setMaxSuffixComponents(5);
+ BOOST_CHECK_EQUAL(find(), 5);
+
+ startInterest("ndn:/")
+ .setChildSelector(1)
+ .setMaxSuffixComponents(6);
+ BOOST_CHECK_EQUAL(find(), 6);
+
+ startInterest("ndn:/")
+ .setChildSelector(1)
+ .setMaxSuffixComponents(7);
+ BOOST_CHECK_EQUAL(find(), 6);
+}
+
+BOOST_AUTO_TEST_CASE(DigestOrder)
+{
+ insert(1, "ndn:/A");
+ insert(2, "ndn:/A");
+ // We don't know which comes first, but there must be some order
+
+ startInterest("ndn:/A")
+ .setChildSelector(0);
+ uint32_t leftmost = find();
+
+ startInterest("ndn:/A")
+ .setChildSelector(1);
+ uint32_t rightmost = find();
+
+ BOOST_CHECK_NE(leftmost, rightmost);
+}
+
+BOOST_AUTO_TEST_CASE(DigestExclude)
+{
+ insert(1, "ndn:/A");
+ Name n2 = insert(2, "ndn:/A");
+ insert(3, "ndn:/A/B");
+
+ uint8_t digest00[util::Sha256::DIGEST_SIZE];
+ std::fill_n(digest00, sizeof(digest00), 0x00);
+ uint8_t digestFF[util::Sha256::DIGEST_SIZE];
+ std::fill_n(digestFF, sizeof(digestFF), 0xFF);
+
+ Exclude excludeDigest;
+ excludeDigest.excludeRange(
+ name::Component::fromImplicitSha256Digest(digest00, sizeof(digest00)),
+ name::Component::fromImplicitSha256Digest(digestFF, sizeof(digestFF)));
+
+ startInterest("ndn:/A")
+ .setChildSelector(0)
+ .setExclude(excludeDigest);
+ BOOST_CHECK_EQUAL(find(), 3);
+
+ startInterest("ndn:/A")
+ .setChildSelector(1)
+ .setExclude(excludeDigest);
+ BOOST_CHECK_EQUAL(find(), 3);
+
+ Exclude excludeGeneric;
+ excludeGeneric.excludeAfter(name::Component(static_cast<uint8_t*>(nullptr), 0));
+
+ startInterest("ndn:/A")
+ .setChildSelector(0)
+ .setExclude(excludeGeneric);
+ int found1 = find();
+ BOOST_CHECK(found1 == 1 || found1 == 2);
+
+ startInterest("ndn:/A")
+ .setChildSelector(1)
+ .setExclude(excludeGeneric);
+ int found2 = find();
+ BOOST_CHECK(found2 == 1 || found2 == 2);
+
+ Exclude exclude2 = excludeGeneric;
+ exclude2.excludeOne(n2.get(-1));
+
+ startInterest("ndn:/A")
+ .setChildSelector(0)
+ .setExclude(exclude2);
+ BOOST_CHECK_EQUAL(find(), 1);
+
+ startInterest("ndn:/A")
+ .setChildSelector(1)
+ .setExclude(exclude2);
+ BOOST_CHECK_EQUAL(find(), 1);
+}
+
+BOOST_AUTO_TEST_CASE(MustBeFresh)
+{
+ Name data1Name = insert(1, "ndn:/A/1", 500_ms);
+ insert(2, "ndn:/A/2", 2500_ms);
+ insert(3, "ndn:/A/3", 3500_ms);
+ insert(4, "ndn:/A/4", 1500_ms);
+
+ // @0s, all Data are fresh
+ startInterest("ndn:/A/1")
+ .setMustBeFresh(true);
+ BOOST_CHECK_EQUAL(find(), 1);
+
+ startInterest("ndn:/A/1")
+ .setMustBeFresh(false);
+ BOOST_CHECK_EQUAL(find(), 1);
+
+ startInterest("ndn:/A")
+ .setMustBeFresh(true)
+ .setChildSelector(0);
+ BOOST_CHECK_EQUAL(find(), 1);
+
+ startInterest("ndn:/A")
+ .setMustBeFresh(true)
+ .setChildSelector(1);
+ BOOST_CHECK_EQUAL(find(), 4);
+
+ advanceClocks(1000_ms);
+ // @1s, /A/1 is stale
+ startInterest("ndn:/A/1")
+ .setMustBeFresh(true);
+ BOOST_CHECK_EQUAL(find(), 0);
+ startInterest("ndn:/A/1")
+ .setMustBeFresh(false);
+ BOOST_CHECK_EQUAL(find(), 1);
+
+ // MustBeFresh is ignored when full Name is specified
+ startInterest(data1Name)
+ .setMustBeFresh(true);
+ BOOST_CHECK_EQUAL(find(), 1);
+
+ startInterest("ndn:/A")
+ .setMustBeFresh(true)
+ .setChildSelector(0);
+ BOOST_CHECK_EQUAL(find(), 2);
+ startInterest("ndn:/A")
+ .setMustBeFresh(false)
+ .setChildSelector(0);
+ BOOST_CHECK_EQUAL(find(), 1);
+
+ advanceClocks(1000_ms);
+ // @2s, /A/1 and /A/4 are stale
+ startInterest("ndn:/A")
+ .setMustBeFresh(true)
+ .setChildSelector(1);
+ BOOST_CHECK_EQUAL(find(), 3);
+ startInterest("ndn:/A")
+ .setMustBeFresh(false)
+ .setChildSelector(1);
+ BOOST_CHECK_EQUAL(find(), 4);
+
+ advanceClocks(2000_ms);
+ // @4s, all Data are stale
+ startInterest("ndn:/A")
+ .setMustBeFresh(true)
+ .setChildSelector(0);
+ BOOST_CHECK_EQUAL(find(), 0);
+ startInterest("ndn:/A")
+ .setMustBeFresh(true)
+ .setChildSelector(1);
+ BOOST_CHECK_EQUAL(find(), 0);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // Find
+BOOST_AUTO_TEST_SUITE_END() // TestInMemoryStorage
+BOOST_AUTO_TEST_SUITE_END() // Ims
+
+} // namespace tests
+} // namespace ndn
diff --git a/tests/unit/interest-filter.t.cpp b/tests/unit/interest-filter.t.cpp
new file mode 100644
index 0000000..96f4ae9
--- /dev/null
+++ b/tests/unit/interest-filter.t.cpp
@@ -0,0 +1,76 @@
+/* -*- 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 "interest-filter.hpp"
+#include "data.hpp"
+#include "encoding/buffer-stream.hpp"
+#include "security/signature-sha256-with-rsa.hpp"
+#include "security/digest-sha256.hpp"
+#include "util/dummy-client-face.hpp"
+
+#include "boost-test.hpp"
+#include "make-interest-data.hpp"
+
+namespace ndn {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(TestInterestFilter)
+
+BOOST_AUTO_TEST_CASE(Matching)
+{
+ BOOST_CHECK_EQUAL(InterestFilter("/a").doesMatch("/a/b"), true);
+ BOOST_CHECK_EQUAL(InterestFilter("/a/b").doesMatch("/a/b"), true);
+ BOOST_CHECK_EQUAL(InterestFilter("/a/b/c").doesMatch("/a/b"), false);
+
+ BOOST_CHECK_EQUAL(InterestFilter("/a", "<b>").doesMatch("/a/b"), true);
+ BOOST_CHECK_EQUAL(InterestFilter("/a/b", "<b>").doesMatch("/a/b"), false);
+
+ BOOST_CHECK_EQUAL(InterestFilter("/a/b", "<b>").doesMatch("/a/b/c/b"), false);
+ BOOST_CHECK_EQUAL(InterestFilter("/a/b", "<>*<b>").doesMatch("/a/b/c/b"), true);
+
+ BOOST_CHECK_EQUAL(InterestFilter("/a", "<b>").doesMatch("/a/b/c/d"), false);
+ BOOST_CHECK_EQUAL(InterestFilter("/a", "<b><>*").doesMatch("/a/b/c/d"), true);
+ BOOST_CHECK_EQUAL(InterestFilter("/a", "<b><>*").doesMatch("/a/b"), true);
+ BOOST_CHECK_EQUAL(InterestFilter("/a", "<b><>+").doesMatch("/a/b"), false);
+ BOOST_CHECK_EQUAL(InterestFilter("/a", "<b><>+").doesMatch("/a/b/c"), true);
+}
+
+BOOST_AUTO_TEST_CASE(RegexConvertToName)
+{
+ util::DummyClientFace face;
+ face.setInterestFilter(InterestFilter("/Hello/World", "<><b><c>?"),
+ [] (const Name&, const Interest&) { BOOST_ERROR("unexpected Interest"); });
+ face.processEvents(1_ms);
+ BOOST_CHECK_THROW(face.receive(*makeInterest("/Hello/World/a/b/c")), InterestFilter::Error);
+}
+
+BOOST_AUTO_TEST_CASE(AllowLoopback)
+{
+ InterestFilter filter("/A");
+ BOOST_CHECK_EQUAL(filter.allowsLoopback(), true);
+ BOOST_CHECK_EQUAL(&filter.allowLoopback(false), &filter);
+ BOOST_CHECK_EQUAL(filter.allowsLoopback(), false);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestInterestFilter
+
+} // namespace tests
+} // namespace ndn
diff --git a/tests/unit/interest.t.cpp b/tests/unit/interest.t.cpp
new file mode 100644
index 0000000..16e2ba2
--- /dev/null
+++ b/tests/unit/interest.t.cpp
@@ -0,0 +1,664 @@
+/* -*- 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 "interest.hpp"
+#include "data.hpp"
+#include "security/digest-sha256.hpp"
+#include "security/signature-sha256-with-rsa.hpp"
+
+#include "boost-test.hpp"
+#include "identity-management-fixture.hpp"
+
+namespace ndn {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(TestInterest)
+
+// ---- constructor, encode, decode ----
+
+BOOST_AUTO_TEST_CASE(DefaultConstructor)
+{
+ Interest i;
+ BOOST_CHECK(!i.hasWire());
+ BOOST_CHECK_EQUAL(i.getName(), "/");
+ BOOST_CHECK_EQUAL(i.getCanBePrefix(), true);
+ BOOST_CHECK_EQUAL(i.getMustBeFresh(), false);
+ BOOST_CHECK(i.getForwardingHint().empty());
+ BOOST_CHECK(!i.hasNonce());
+ BOOST_CHECK_EQUAL(i.getInterestLifetime(), DEFAULT_INTEREST_LIFETIME);
+ BOOST_CHECK(!i.hasSelectors());
+}
+
+BOOST_AUTO_TEST_CASE(EncodeDecode02Basic)
+{
+ const uint8_t WIRE[] = {
+ 0x05, 0x1c, // Interest
+ 0x07, 0x14, // Name
+ 0x08, 0x05, 0x6c, 0x6f, 0x63, 0x61, 0x6c, // GenericNameComponent
+ 0x08, 0x03, 0x6e, 0x64, 0x6e, // GenericNameComponent
+ 0x08, 0x06, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, // GenericNameComponent
+ 0x0a, 0x04, // Nonce
+ 0x01, 0x00, 0x00, 0x00
+ };
+
+ Interest i1("/local/ndn/prefix");
+ i1.setCanBePrefix(true);
+ i1.setNonce(1);
+ Block wire1 = i1.wireEncode();
+ BOOST_CHECK_EQUAL_COLLECTIONS(wire1.begin(), wire1.end(), WIRE, WIRE + sizeof(WIRE));
+
+ Interest i2(wire1);
+ BOOST_CHECK_EQUAL(i2.getName(), "/local/ndn/prefix");
+ BOOST_CHECK(i2.getSelectors().empty());
+ BOOST_CHECK_EQUAL(i2.getNonce(), 1);
+ BOOST_CHECK_EQUAL(i2.getInterestLifetime(), DEFAULT_INTEREST_LIFETIME);
+
+ BOOST_CHECK_EQUAL(i1, i2);
+}
+
+BOOST_AUTO_TEST_CASE(EncodeDecode02Full)
+{
+ const uint8_t WIRE[] = {
+ 0x05, 0x31, // Interest
+ 0x07, 0x14, // Name
+ 0x08, 0x05, 0x6c, 0x6f, 0x63, 0x61, 0x6c, // GenericNameComponent
+ 0x08, 0x03, 0x6e, 0x64, 0x6e, // GenericNameComponent
+ 0x08, 0x06, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, // GenericNameComponent
+ 0x09, 0x03, // Selectors
+ 0x0d, 0x01, 0x01, // MinSuffixComponents
+ 0x0a, 0x04, // Nonce
+ 0x01, 0x00, 0x00, 0x00,
+ 0x0c, 0x02, // InterestLifetime
+ 0x03, 0xe8,
+ 0x1e, 0x0a, // ForwardingHint
+ 0x1f, 0x08, // Delegation
+ 0x1e, 0x01, 0x01, // Preference=1
+ 0x07, 0x03, 0x08, 0x01, 0x41 // Name=/A
+ };
+
+ Interest i1;
+ i1.setName("/local/ndn/prefix");
+ i1.setCanBePrefix(true);
+ i1.setMinSuffixComponents(1);
+ i1.setNonce(1);
+ i1.setInterestLifetime(1000_ms);
+ i1.setForwardingHint({{1, "/A"}});
+ Block wire1 = i1.wireEncode();
+ BOOST_CHECK_EQUAL_COLLECTIONS(wire1.begin(), wire1.end(), WIRE, WIRE + sizeof(WIRE));
+
+ Interest i2(wire1);
+ BOOST_CHECK_EQUAL(i2.getName(), "/local/ndn/prefix");
+ BOOST_CHECK_EQUAL(i2.getMinSuffixComponents(), 1);
+ BOOST_CHECK_EQUAL(i2.getNonce(), 1);
+ BOOST_CHECK_EQUAL(i2.getInterestLifetime(), 1000_ms);
+ BOOST_CHECK_EQUAL(i2.getForwardingHint(), DelegationList({{1, "/A"}}));
+
+ BOOST_CHECK_EQUAL(i1, i2);
+}
+
+BOOST_AUTO_TEST_CASE(EncodeDecode03Basic)
+{
+ const uint8_t WIRE[] = {
+ 0x05, 0x22, // Interest
+ 0x07, 0x14, // Name
+ 0x08, 0x05, 0x6c, 0x6f, 0x63, 0x61, 0x6c, // GenericNameComponent
+ 0x08, 0x03, 0x6e, 0x64, 0x6e, // GenericNameComponent
+ 0x08, 0x06, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, // GenericNameComponent
+ 0x0a, 0x04, // Nonce
+ 0x01, 0x00, 0x00, 0x00,
+ 0x23, 0x04, // Parameters
+ 0xc0, 0xc1, 0xc2, 0xc3};
+
+ Interest i1;
+ i1.setName("/local/ndn/prefix");
+ i1.setCanBePrefix(false);
+ i1.setNonce(1);
+ i1.setParameters("2304C0C1C2C3"_block);
+ Block wire1 = i1.wireEncode();
+ BOOST_CHECK_EQUAL_COLLECTIONS(wire1.begin(), wire1.end(), WIRE, WIRE + sizeof(WIRE));
+
+ Interest i2(wire1);
+ BOOST_CHECK_EQUAL(i2.getName(), "/local/ndn/prefix");
+ BOOST_CHECK_EQUAL(i2.getCanBePrefix(), false);
+ BOOST_CHECK_EQUAL(i2.getMustBeFresh(), false);
+ BOOST_CHECK(i2.getForwardingHint().empty());
+ BOOST_CHECK_EQUAL(i2.getNonce(), 1);
+ BOOST_CHECK_EQUAL(i2.getInterestLifetime(), DEFAULT_INTEREST_LIFETIME);
+ BOOST_CHECK(i2.hasParameters());
+ BOOST_CHECK_EQUAL(i2.getParameters(), "2304C0C1C2C3"_block);
+ BOOST_CHECK(i2.getPublisherPublicKeyLocator().empty());
+}
+
+BOOST_AUTO_TEST_CASE(EncodeDecode03Full)
+{
+ const uint8_t WIRE[] = {
+ 0x05, 0x37, // Interest
+ 0x07, 0x14, // Name
+ 0x08, 0x05, 0x6c, 0x6f, 0x63, 0x61, 0x6c, // GenericNameComponent
+ 0x08, 0x03, 0x6e, 0x64, 0x6e, // GenericNameComponent
+ 0x08, 0x06, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, // GenericNameComponent
+ 0x21, 0x00, // CanBePrefix
+ 0x12, 0x00, // MustBeFresh
+ 0x1e, 0x0b, // ForwardingHint
+ 0x1f, 0x09, // Delegation List
+ 0x1e, 0x02,
+ 0x3e, 0x15,
+ 0x07, 0x03,
+ 0x08, 0x01, 0x48,
+ 0x0a, 0x04, // Nonce
+ 0x4a, 0xcb, 0x1e, 0x4c,
+ 0x0c, 0x02, // Interest Lifetime
+ 0x76, 0xa1,
+ 0x23, 0x04, // Parameters
+ 0xc0, 0xc1, 0xc2, 0xc3};
+ Interest i1;
+ i1.setName("/local/ndn/prefix");
+ i1.setMustBeFresh(true);
+ i1.setCanBePrefix(true);
+ i1.setForwardingHint(DelegationList({{15893, "/H"}}));
+ i1.setNonce(0x4c1ecb4a);
+ i1.setInterestLifetime(30369_ms);
+ i1.setParameters("2304C0C1C2C3"_block);
+ i1.setMinSuffixComponents(1); // v0.2-only elements will not be encoded
+ i1.setExclude(Exclude().excludeAfter(name::Component("J"))); // v0.2-only elements will not be encoded
+ Block wire1 = i1.wireEncode();
+ BOOST_CHECK_EQUAL_COLLECTIONS(wire1.begin(), wire1.end(), WIRE, WIRE + sizeof(WIRE));
+
+ Interest i2(wire1);
+ BOOST_CHECK_EQUAL(i2.getName(), "/local/ndn/prefix");
+ BOOST_CHECK_EQUAL(i2.getCanBePrefix(), true);
+ BOOST_CHECK_EQUAL(i2.getMustBeFresh(), true);
+ BOOST_CHECK_EQUAL(i2.getForwardingHint(), DelegationList({{15893, "/H"}}));
+ BOOST_CHECK(i2.hasNonce());
+ BOOST_CHECK_EQUAL(i2.getNonce(), 0x4c1ecb4a);
+ BOOST_CHECK_EQUAL(i2.getInterestLifetime(), 30369_ms);
+ BOOST_CHECK_EQUAL(i2.getParameters(), "2304C0C1C2C3"_block);
+ BOOST_CHECK_EQUAL(i2.getMinSuffixComponents(), -1); // Default because minSuffixComponents was not encoded
+ BOOST_CHECK(i2.getExclude().empty()); // Exclude was not encoded
+}
+
+class Decode03Fixture
+{
+protected:
+ Decode03Fixture()
+ {
+ // initialize all elements to non-empty, to verify wireDecode clears them
+ i.setName("/A");
+ i.setForwardingHint({{10309, "/F"}});
+ i.setNonce(0x03d645a8);
+ i.setInterestLifetime(18554_ms);
+ i.setPublisherPublicKeyLocator(Name("/K"));
+ i.setParameters("2304A0A1A2A3"_block);
+ }
+
+protected:
+ Interest i;
+};
+
+BOOST_FIXTURE_TEST_SUITE(Decode03, Decode03Fixture)
+
+BOOST_AUTO_TEST_CASE(Minimal)
+{
+ i.wireDecode("0505 0703080149"_block);
+ BOOST_CHECK_EQUAL(i.getName(), "/I");
+ BOOST_CHECK_EQUAL(i.getCanBePrefix(), false);
+ BOOST_CHECK_EQUAL(i.getMustBeFresh(), false);
+ BOOST_CHECK(i.getForwardingHint().empty());
+ BOOST_CHECK(i.hasNonce()); // a random nonce is generated
+ BOOST_CHECK_EQUAL(i.getInterestLifetime(), DEFAULT_INTEREST_LIFETIME);
+ BOOST_CHECK(i.getPublisherPublicKeyLocator().empty());
+ BOOST_CHECK(!i.hasParameters());
+
+ BOOST_CHECK(!i.hasWire()); // nonce generation resets wire encoding
+
+ // modify then re-encode as v0.2 format
+ i.setNonce(0x54657c95);
+ BOOST_CHECK_EQUAL(i.wireEncode(), "0510 0703080149 09030E0101 0A04957C6554"_block);
+}
+
+BOOST_AUTO_TEST_CASE(Full)
+{
+ i.wireDecode("0531 0703080149 FC00 2100 FC00 1200 "
+ "FC00 1E0B(1F09 1E023E15 0703080148) FC00 0A044ACB1E4C "
+ "FC00 0C0276A1 FC00 2201D6 FC00"_block);
+ BOOST_CHECK_EQUAL(i.getName(), "/I");
+ BOOST_CHECK_EQUAL(i.getCanBePrefix(), true);
+ BOOST_CHECK_EQUAL(i.getMustBeFresh(), true);
+ BOOST_CHECK_EQUAL(i.getForwardingHint(), DelegationList({{15893, "/H"}}));
+ BOOST_CHECK(i.hasNonce());
+ BOOST_CHECK_EQUAL(i.getNonce(), 0x4c1ecb4a);
+ BOOST_CHECK_EQUAL(i.getInterestLifetime(), 30369_ms);
+ // HopLimit=214 is not stored
+
+ // encode without modification: retain original wire encoding
+ BOOST_CHECK_EQUAL(i.wireEncode().value_size(), 49);
+
+ // modify then re-encode as v0.2 format
+ i.setName("/J");
+ BOOST_CHECK_EQUAL(i.wireEncode(),
+ "0520 070308014A 09021200 0A044ACB1E4C 0C0276A1 1E0B(1F09 1E023E15 0703080148)"_block);
+}
+
+BOOST_AUTO_TEST_CASE(CriticalElementOutOfOrder)
+{
+ BOOST_CHECK_THROW(i.wireDecode(
+ "0529 2100 0703080149 1200 1E0B(1F09 1E023E15 0703080148) "
+ "0A044ACB1E4C 0C0276A1 2201D6 2304C0C1C2C3"_block),
+ tlv::Error);
+ BOOST_CHECK_THROW(i.wireDecode(
+ "0529 0703080149 1200 2100 1E0B(1F09 1E023E15 0703080148) "
+ "0A044ACB1E4C 0C0276A1 2201D6 2304C0C1C2C3"_block),
+ tlv::Error);
+ BOOST_CHECK_THROW(i.wireDecode(
+ "0529 0703080149 2100 1E0B(1F09 1E023E15 0703080148) 1200 "
+ "0A044ACB1E4C 0C0276A1 2201D6 2304C0C1C2C3"_block),
+ tlv::Error);
+ BOOST_CHECK_THROW(i.wireDecode(
+ "0529 0703080149 2100 1200 0A044ACB1E4C "
+ "1E0B(1F09 1E023E15 0703080148) 0C0276A1 2201D6 2304C0C1C2C3"_block),
+ tlv::Error);
+ BOOST_CHECK_THROW(i.wireDecode(
+ "0529 0703080149 2100 1200 1E0B(1F09 1E023E15 0703080148) "
+ "0C0276A1 0A044ACB1E4C 2201D6 2304C0C1C2C3"_block),
+ tlv::Error);
+ BOOST_CHECK_THROW(i.wireDecode(
+ "0529 0703080149 2100 1200 1E0B(1F09 1E023E15 0703080148) "
+ "0A044ACB1E4C 2201D6 0C0276A1 2304C0C1C2C3"_block),
+ tlv::Error);
+ BOOST_CHECK_THROW(i.wireDecode(
+ "052F 0703080149 2100 1200 1E0B(1F09 1E023E15 0703080148) "
+ "0A044ACB1E4C 0C0276A1 2201D6 2304C0C1C2C3 2304C0C1C2C3"_block),
+ tlv::Error);
+}
+
+BOOST_AUTO_TEST_CASE(HopLimitOutOfOrder)
+{
+ // HopLimit is non-critical, its out-of-order appearances are ignored
+ i.wireDecode("0514 0703080149 2201D6 2200 2304C0C1C2C3 22020101"_block);
+ BOOST_CHECK_EQUAL(i.getName(), "/I");
+ // HopLimit=214 is not stored
+ BOOST_CHECK_EQUAL(i.getParameters(), "2304C0C1C2C3"_block);
+}
+
+BOOST_AUTO_TEST_CASE(NameMissing)
+{
+ BOOST_CHECK_THROW(i.wireDecode("0500"_block), tlv::Error);
+ BOOST_CHECK_THROW(i.wireDecode("0502 1200"_block), tlv::Error);
+}
+
+BOOST_AUTO_TEST_CASE(NameEmpty)
+{
+ BOOST_CHECK_THROW(i.wireDecode("0502 0700"_block), tlv::Error);
+}
+
+BOOST_AUTO_TEST_CASE(BadCanBePrefix)
+{
+ BOOST_CHECK_THROW(i.wireDecode("0508 0703080149 210102"_block), tlv::Error);
+}
+
+BOOST_AUTO_TEST_CASE(BadMustBeFresh)
+{
+ BOOST_CHECK_THROW(i.wireDecode("0508 0703080149 120102"_block), tlv::Error);
+}
+
+BOOST_AUTO_TEST_CASE(BadNonce)
+{
+ BOOST_CHECK_THROW(i.wireDecode("0507 0703080149 0A00"_block), tlv::Error);
+ BOOST_CHECK_THROW(i.wireDecode("050A 0703080149 0A0304C263"_block), tlv::Error);
+ BOOST_CHECK_THROW(i.wireDecode("050C 0703080149 0A05EFA420B262"_block), tlv::Error);
+}
+
+BOOST_AUTO_TEST_CASE(UnrecognizedNonCriticalElementBeforeName)
+{
+ BOOST_CHECK_THROW(i.wireDecode("0507 FC00 0703080149"_block), tlv::Error);
+}
+
+BOOST_AUTO_TEST_CASE(UnrecognizedCriticalElement)
+{
+ BOOST_CHECK_THROW(i.wireDecode("0507 0703080149 FB00"_block), tlv::Error);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // Decode03
+
+// ---- matching ----
+
+BOOST_AUTO_TEST_CASE(MatchesData)
+{
+ Interest interest;
+ interest.setName("ndn:/A")
+ .setMinSuffixComponents(2)
+ .setMaxSuffixComponents(2)
+ .setPublisherPublicKeyLocator(KeyLocator("ndn:/B"))
+ .setExclude(Exclude().excludeAfter(name::Component("J")));
+
+ Data data("ndn:/A/D");
+ SignatureSha256WithRsa signature(KeyLocator("ndn:/B"));
+ signature.setValue(encoding::makeEmptyBlock(tlv::SignatureValue));
+ data.setSignature(signature);
+ data.wireEncode();
+ BOOST_CHECK_EQUAL(interest.matchesData(data), true);
+
+ Data data1 = data;
+ data1.setName("ndn:/A"); // violates MinSuffixComponents
+ data1.wireEncode();
+ BOOST_CHECK_EQUAL(interest.matchesData(data1), false);
+
+ Interest interest1 = interest;
+ interest1.setMinSuffixComponents(1);
+ BOOST_CHECK_EQUAL(interest1.matchesData(data1), true);
+
+ Data data2 = data;
+ data2.setName("ndn:/A/E/F"); // violates MaxSuffixComponents
+ data2.wireEncode();
+ BOOST_CHECK_EQUAL(interest.matchesData(data2), false);
+
+ Interest interest2 = interest;
+ interest2.setMaxSuffixComponents(3);
+ BOOST_CHECK_EQUAL(interest2.matchesData(data2), true);
+
+ Data data3 = data;
+ SignatureSha256WithRsa signature3(KeyLocator("ndn:/G")); // violates PublisherPublicKeyLocator
+ signature3.setValue(encoding::makeEmptyBlock(tlv::SignatureValue));
+ data3.setSignature(signature3);
+ data3.wireEncode();
+ BOOST_CHECK_EQUAL(interest.matchesData(data3), false);
+
+ Interest interest3 = interest;
+ interest3.setPublisherPublicKeyLocator(KeyLocator("ndn:/G"));
+ BOOST_CHECK_EQUAL(interest3.matchesData(data3), true);
+
+ Data data4 = data;
+ DigestSha256 signature4; // violates PublisherPublicKeyLocator
+ signature4.setValue(encoding::makeEmptyBlock(tlv::SignatureValue));
+ data4.setSignature(signature4);
+ data4.wireEncode();
+ BOOST_CHECK_EQUAL(interest.matchesData(data4), false);
+
+ Interest interest4 = interest;
+ interest4.setPublisherPublicKeyLocator(KeyLocator());
+ BOOST_CHECK_EQUAL(interest4.matchesData(data4), true);
+
+ Data data5 = data;
+ data5.setName("ndn:/A/J"); // violates Exclude
+ data5.wireEncode();
+ BOOST_CHECK_EQUAL(interest.matchesData(data5), false);
+
+ Interest interest5 = interest;
+ interest5.setExclude(Exclude().excludeAfter(name::Component("K")));
+ BOOST_CHECK_EQUAL(interest5.matchesData(data5), true);
+
+ Data data6 = data;
+ data6.setName("ndn:/H/I"); // violates Name
+ data6.wireEncode();
+ BOOST_CHECK_EQUAL(interest.matchesData(data6), false);
+
+ Data data7 = data;
+ data7.setName("ndn:/A/B");
+ data7.wireEncode();
+
+ Interest interest7("/A/B/sha256digest=54008e240a7eea2714a161dfddf0dd6ced223b3856e9da96792151e180f3b128");
+ BOOST_CHECK_EQUAL(interest7.matchesData(data7), true);
+
+ Interest interest7b("/A/B/sha256digest=0000000000000000000000000000000000000000000000000000000000000000");
+ BOOST_CHECK_EQUAL(interest7b.matchesData(data7), false); // violates implicit digest
+}
+
+BOOST_AUTO_TEST_CASE_EXPECTED_FAILURES(MatchesInterest, 1)
+BOOST_AUTO_TEST_CASE(MatchesInterest)
+{
+ Interest interest;
+ interest
+ .setName("/A")
+ .setMinSuffixComponents(2)
+ .setMaxSuffixComponents(2)
+ .setPublisherPublicKeyLocator(KeyLocator("/B"))
+ .setExclude(Exclude().excludeAfter(name::Component("J")))
+ .setNonce(10)
+ .setInterestLifetime(5_s)
+ .setForwardingHint({{1, "/H"}});
+
+ Interest other;
+ BOOST_CHECK_EQUAL(interest.matchesInterest(other), false);
+
+ other.setName(interest.getName());
+ BOOST_CHECK_EQUAL(interest.matchesInterest(other), false);
+
+ other.setSelectors(interest.getSelectors());
+ BOOST_CHECK_EQUAL(interest.matchesInterest(other), false); // will match until #3162 implemented
+
+ other.setForwardingHint({{1, "/H"}});
+ BOOST_CHECK_EQUAL(interest.matchesInterest(other), true);
+
+ other.setNonce(200);
+ BOOST_CHECK_EQUAL(interest.matchesInterest(other), true);
+
+ other.setInterestLifetime(5_h);
+ BOOST_CHECK_EQUAL(interest.matchesInterest(other), true);
+}
+
+// ---- field accessors ----
+
+BOOST_AUTO_TEST_CASE(CanBePrefix)
+{
+ Interest i;
+ BOOST_CHECK_EQUAL(i.getCanBePrefix(), true);
+ i.setCanBePrefix(false);
+ BOOST_CHECK_EQUAL(i.getCanBePrefix(), false);
+ BOOST_CHECK_EQUAL(i.getSelectors().getMaxSuffixComponents(), 1);
+ i.setCanBePrefix(true);
+ BOOST_CHECK_EQUAL(i.getCanBePrefix(), true);
+ BOOST_CHECK_EQUAL(i.getSelectors().getMaxSuffixComponents(), -1);
+}
+
+BOOST_AUTO_TEST_CASE(MustBeFresh)
+{
+ Interest i;
+ BOOST_CHECK_EQUAL(i.getMustBeFresh(), false);
+ i.setMustBeFresh(true);
+ BOOST_CHECK_EQUAL(i.getMustBeFresh(), true);
+ BOOST_CHECK_EQUAL(i.getSelectors().getMustBeFresh(), true);
+ i.setMustBeFresh(false);
+ BOOST_CHECK_EQUAL(i.getMustBeFresh(), false);
+ BOOST_CHECK_EQUAL(i.getSelectors().getMustBeFresh(), false);
+}
+
+BOOST_AUTO_TEST_CASE(ModifyForwardingHint)
+{
+ Interest i;
+ i.setCanBePrefix(false);
+ i.setForwardingHint({{1, "/A"}});
+ i.wireEncode();
+ BOOST_CHECK(i.hasWire());
+
+ i.modifyForwardingHint([] (DelegationList& fh) { fh.insert(2, "/B"); });
+ BOOST_CHECK(!i.hasWire());
+ BOOST_CHECK_EQUAL(i.getForwardingHint(), DelegationList({{1, "/A"}, {2, "/B"}}));
+}
+
+BOOST_AUTO_TEST_CASE(GetNonce)
+{
+ unique_ptr<Interest> i1, i2;
+
+ // getNonce automatically assigns a random Nonce.
+ // It's possible to assign the same Nonce to two Interest, but it's unlikely to get 100 pairs of
+ // same Nonces in a row.
+ int nIterations = 0;
+ uint32_t nonce1 = 0, nonce2 = 0;
+ do {
+ i1 = make_unique<Interest>();
+ nonce1 = i1->getNonce();
+ i2 = make_unique<Interest>();
+ nonce2 = i2->getNonce();
+ }
+ while (nonce1 == nonce2 && ++nIterations < 100);
+ BOOST_CHECK_NE(nonce1, nonce2);
+ BOOST_CHECK(i1->hasNonce());
+ BOOST_CHECK(i2->hasNonce());
+
+ // Once a Nonce is assigned, it should not change.
+ BOOST_CHECK_EQUAL(i1->getNonce(), nonce1);
+}
+
+BOOST_AUTO_TEST_CASE(SetNonce)
+{
+ Interest i1("/A");
+ i1.setCanBePrefix(false);
+ i1.setNonce(1);
+ i1.wireEncode();
+ BOOST_CHECK_EQUAL(i1.getNonce(), 1);
+
+ Interest i2(i1);
+ BOOST_CHECK_EQUAL(i2.getNonce(), 1);
+
+ i2.setNonce(2);
+ BOOST_CHECK_EQUAL(i2.getNonce(), 2);
+ BOOST_CHECK_EQUAL(i1.getNonce(), 1); // should not affect i1 Nonce (Bug #4168)
+}
+
+BOOST_AUTO_TEST_CASE(RefreshNonce)
+{
+ Interest i;
+ BOOST_CHECK(!i.hasNonce());
+ i.refreshNonce();
+ BOOST_CHECK(!i.hasNonce());
+
+ i.setNonce(1);
+ BOOST_CHECK(i.hasNonce());
+ i.refreshNonce();
+ BOOST_CHECK(i.hasNonce());
+ BOOST_CHECK_NE(i.getNonce(), 1);
+}
+
+BOOST_AUTO_TEST_CASE(SetInterestLifetime)
+{
+ BOOST_CHECK_THROW(Interest("/A", time::milliseconds(-1)), std::invalid_argument);
+ BOOST_CHECK_NO_THROW(Interest("/A", 0_ms));
+
+ Interest i("/local/ndn/prefix");
+ i.setNonce(1);
+ BOOST_CHECK_EQUAL(i.getInterestLifetime(), DEFAULT_INTEREST_LIFETIME);
+ BOOST_CHECK_THROW(i.setInterestLifetime(time::milliseconds(-1)), std::invalid_argument);
+ BOOST_CHECK_EQUAL(i.getInterestLifetime(), DEFAULT_INTEREST_LIFETIME);
+ i.setInterestLifetime(0_ms);
+ BOOST_CHECK_EQUAL(i.getInterestLifetime(), 0_ms);
+ i.setInterestLifetime(1_ms);
+ BOOST_CHECK_EQUAL(i.getInterestLifetime(), 1_ms);
+}
+
+BOOST_AUTO_TEST_CASE(SetParameters)
+{
+ const uint8_t PARAMETERS1[] = {0xc1};
+ const uint8_t PARAMETERS2[] = {0xc2};
+
+ Interest i;
+ BOOST_CHECK(!i.hasParameters());
+ i.setParameters("2300"_block);
+ BOOST_CHECK(i.hasParameters());
+ i.unsetParameters();
+ BOOST_CHECK(!i.hasParameters());
+
+ i.setParameters("2301C0"_block); // Block overload
+ BOOST_CHECK_EQUAL(i.getParameters(), "2301C0"_block);
+ i.setParameters(PARAMETERS1, sizeof(PARAMETERS1)); // raw buffer overload
+ BOOST_CHECK_EQUAL(i.getParameters(), "2301C1"_block);
+ i.setParameters(make_shared<Buffer>(PARAMETERS2, sizeof(PARAMETERS2))); // ConstBufferPtr overload
+ BOOST_CHECK_EQUAL(i.getParameters(), "2301C2"_block);
+ i.setParameters("8001C1"_block); // Block of non-Parameters type
+ BOOST_CHECK_EQUAL(i.getParameters(), "23038001C1"_block);
+}
+
+// ---- operators ----
+
+BOOST_AUTO_TEST_CASE(Equality)
+{
+ Interest a;
+ Interest b;
+
+ // if nonce is not set, it would be set to a random value
+ a.setNonce(1);
+ b.setNonce(1);
+
+ BOOST_CHECK_EQUAL(a == b, true);
+ BOOST_CHECK_EQUAL(a != b, false);
+
+ // compare Name
+ a.setName("/A");
+ BOOST_CHECK_EQUAL(a == b, false);
+ BOOST_CHECK_EQUAL(a != b, true);
+
+ b.setName("/B");
+ BOOST_CHECK_EQUAL(a == b, false);
+ BOOST_CHECK_EQUAL(a != b, true);
+
+ b.setName("/A");
+ BOOST_CHECK_EQUAL(a == b, true);
+ BOOST_CHECK_EQUAL(a != b, false);
+
+ // compare Selectors
+ a.setChildSelector(1);
+ BOOST_CHECK_EQUAL(a == b, false);
+ BOOST_CHECK_EQUAL(a != b, true);
+
+ b.setChildSelector(1);
+ BOOST_CHECK_EQUAL(a == b, true);
+ BOOST_CHECK_EQUAL(a != b, false);
+
+ // compare Nonce
+ a.setNonce(100);
+ BOOST_CHECK_EQUAL(a == b, false);
+ BOOST_CHECK_EQUAL(a != b, true);
+
+ b.setNonce(100);
+ BOOST_CHECK_EQUAL(a == b, true);
+ BOOST_CHECK_EQUAL(a != b, false);
+
+ // compare InterestLifetime
+ a.setInterestLifetime(10_s);
+ BOOST_CHECK_EQUAL(a == b, false);
+ BOOST_CHECK_EQUAL(a != b, true);
+
+ b.setInterestLifetime(10_s);
+ BOOST_CHECK_EQUAL(a == b, true);
+ BOOST_CHECK_EQUAL(a != b, false);
+
+ // compare ForwardingHint
+ a.setForwardingHint({{1, "/H"}});
+ BOOST_CHECK_EQUAL(a == b, false);
+ BOOST_CHECK_EQUAL(a != b, true);
+
+ b.setForwardingHint({{1, "/H"}});
+ BOOST_CHECK_EQUAL(a == b, true);
+ BOOST_CHECK_EQUAL(a != b, false);
+
+ // compare Parameters
+ a.setParameters("2304C0C1C2C3"_block);
+ BOOST_CHECK_EQUAL(a == b, false);
+ BOOST_CHECK_EQUAL(a != b, true);
+
+ b.setParameters("2304C0C1C2C3"_block);
+ BOOST_CHECK_EQUAL(a == b, true);
+ BOOST_CHECK_EQUAL(a != b, false);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestInterest
+
+} // namespace tests
+} // namespace ndn
diff --git a/tests/unit/key-locator.t.cpp b/tests/unit/key-locator.t.cpp
new file mode 100644
index 0000000..ce3c6ae
--- /dev/null
+++ b/tests/unit/key-locator.t.cpp
@@ -0,0 +1,187 @@
+/* -*- 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 "key-locator.hpp"
+#include "encoding/block-helpers.hpp"
+
+#include "boost-test.hpp"
+#include <boost/lexical_cast.hpp>
+
+namespace ndn {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(TestKeyLocator)
+
+BOOST_AUTO_TEST_CASE(TypeNone)
+{
+ KeyLocator a;
+ BOOST_CHECK_EQUAL(a.getType(), KeyLocator::KeyLocator_None);
+ BOOST_CHECK_THROW(a.getName(), KeyLocator::Error);
+ BOOST_CHECK_THROW(a.getKeyDigest(), KeyLocator::Error);
+
+ Block wire;
+ BOOST_REQUIRE_NO_THROW(wire = a.wireEncode());
+
+ // These octets are obtained by the snippet below.
+ // This check is intended to detect unexpected encoding change in the future.
+ // for (auto it = wire.begin(); it != wire.end(); ++it) {
+ // printf("0x%02x, ", *it);
+ // }
+ static const uint8_t expected[] = {
+ 0x1c, 0x00
+ };
+ BOOST_CHECK_EQUAL_COLLECTIONS(expected, expected + sizeof(expected),
+ wire.begin(), wire.end());
+
+ KeyLocator b(wire);
+ BOOST_CHECK_EQUAL(a, b);
+ BOOST_CHECK_EQUAL(b.getType(), KeyLocator::KeyLocator_None);
+ BOOST_CHECK_THROW(b.getName(), KeyLocator::Error);
+ BOOST_CHECK_THROW(b.getKeyDigest(), KeyLocator::Error);
+
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(b), "None");
+}
+
+BOOST_AUTO_TEST_CASE(TypeName)
+{
+ KeyLocator a;
+ a.setName("/N");
+ BOOST_CHECK_EQUAL(a.getType(), KeyLocator::KeyLocator_Name);
+ BOOST_CHECK_EQUAL(a.getName(), Name("/N"));
+ BOOST_CHECK_THROW(a.getKeyDigest(), KeyLocator::Error);
+
+ Block wire;
+ BOOST_REQUIRE_NO_THROW(wire = a.wireEncode());
+
+ // These octets are obtained by the snippet below.
+ // This check is intended to detect unexpected encoding change in the future.
+ // for (auto it = wire.begin(); it != wire.end(); ++it) {
+ // printf("0x%02x, ", *it);
+ // }
+ static const uint8_t expected[] = {
+ 0x1c, 0x05, 0x07, 0x03, 0x08, 0x01, 0x4e
+ };
+ BOOST_CHECK_EQUAL_COLLECTIONS(expected, expected + sizeof(expected),
+ wire.begin(), wire.end());
+
+ KeyLocator b(wire);
+ BOOST_CHECK_EQUAL(a, b);
+ BOOST_CHECK_EQUAL(b.getType(), KeyLocator::KeyLocator_Name);
+ BOOST_CHECK_EQUAL(b.getName(), Name("/N"));
+ BOOST_CHECK_THROW(b.getKeyDigest(), KeyLocator::Error);
+
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(b), "Name=/N");
+}
+
+BOOST_AUTO_TEST_CASE(TypeKeyDigest)
+{
+ std::string digestOctets = "\x12\x34\x56\x78\x9a\xbc\xde\xf1\x23\x45";
+ ConstBufferPtr digestBuffer = make_shared<Buffer>(digestOctets.c_str(), digestOctets.size());
+ Block expectedDigestBlock = makeBinaryBlock(tlv::KeyDigest, digestOctets.c_str(), digestOctets.size());
+
+ KeyLocator a;
+ a.setKeyDigest(digestBuffer);
+ BOOST_CHECK_EQUAL(a.getType(), KeyLocator::KeyLocator_KeyDigest);
+ BOOST_CHECK_EQUAL(a.getKeyDigest(), expectedDigestBlock);
+ BOOST_CHECK_THROW(a.getName(), KeyLocator::Error);
+
+ Block wire;
+ BOOST_REQUIRE_NO_THROW(wire = a.wireEncode());
+
+ // These octets are obtained by the snippet below.
+ // This check is intended to detect unexpected encoding change in the future.
+ // for (auto it = wire.begin(); it != wire.end(); ++it) {
+ // printf("0x%02x, ", *it);
+ // }
+ static const uint8_t expected[] = {
+ 0x1c, 0x0c, 0x1d, 0x0a, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf1, 0x23, 0x45
+ };
+ BOOST_CHECK_EQUAL_COLLECTIONS(expected, expected + sizeof(expected),
+ wire.begin(), wire.end());
+
+ KeyLocator b(wire);
+ BOOST_CHECK_EQUAL(a, b);
+ BOOST_CHECK_EQUAL(b.getType(), KeyLocator::KeyLocator_KeyDigest);
+ BOOST_CHECK_EQUAL(b.getKeyDigest(), expectedDigestBlock);
+ BOOST_CHECK_THROW(b.getName(), KeyLocator::Error);
+
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(b), "KeyDigest=123456789A...");
+
+ std::string shortDigest = "\xbc\xde\xf1";
+ b.setKeyDigest(make_shared<Buffer>(shortDigest.c_str(), shortDigest.size()));
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(b), "KeyDigest=BCDEF1");
+}
+
+BOOST_AUTO_TEST_CASE(Equality)
+{
+ KeyLocator a;
+ KeyLocator b;
+ BOOST_CHECK_EQUAL(a == b, true);
+ BOOST_CHECK_EQUAL(a != b, false);
+
+ a.setName("ndn:/A");
+ BOOST_CHECK_EQUAL(a == b, false);
+ BOOST_CHECK_EQUAL(a != b, true);
+
+ b.setName("ndn:/B");
+ BOOST_CHECK_EQUAL(a == b, false);
+ BOOST_CHECK_EQUAL(a != b, true);
+
+ b.setName("ndn:/A");
+ BOOST_CHECK_EQUAL(a == b, true);
+ BOOST_CHECK_EQUAL(a != b, false);
+
+ char digestOctets[] = "\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD";
+ ConstBufferPtr digestBuffer = make_shared<Buffer>(digestOctets, 8);
+
+ a.setKeyDigest(digestBuffer);
+ BOOST_CHECK_EQUAL(a == b, false);
+ BOOST_CHECK_EQUAL(a != b, true);
+
+ b.setKeyDigest(digestBuffer);
+ BOOST_CHECK_EQUAL(a == b, true);
+ BOOST_CHECK_EQUAL(a != b, false);
+}
+
+BOOST_AUTO_TEST_CASE(UnknownType)
+{
+ static const uint8_t wireOctets[] = {
+ 0x1c, 0x03, 0x7f, 0x01, 0xcc
+ };
+ Block wire(wireOctets, sizeof(wireOctets));
+ KeyLocator a(wire);
+ BOOST_CHECK_EQUAL(a.getType(), KeyLocator::KeyLocator_Unknown);
+
+ KeyLocator b(wire);
+ BOOST_CHECK_EQUAL(a == b, true);
+ BOOST_CHECK_EQUAL(a != b, false);
+
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(b), "Unknown");
+
+ b.setName("/N");
+ BOOST_CHECK_EQUAL(a == b, false);
+ BOOST_CHECK_EQUAL(a != b, true);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestKeyLocator
+
+} // namespace tests
+} // namespace ndn
diff --git a/tests/unit/link.t.cpp b/tests/unit/link.t.cpp
new file mode 100644
index 0000000..347fb67
--- /dev/null
+++ b/tests/unit/link.t.cpp
@@ -0,0 +1,178 @@
+/* -*- 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 "link.hpp"
+
+#include "boost-test.hpp"
+#include "make-interest-data.hpp"
+
+namespace ndn {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(TestLink)
+
+const uint8_t GOOD_LINK[] = {
+0x06, 0xda, // Data
+ 0x07, 0x14, // Name
+ 0x08, 0x05,
+ 0x6c, 0x6f, 0x63, 0x61, 0x6c,
+ 0x08, 0x03,
+ 0x6e, 0x64, 0x6e,
+ 0x08, 0x06,
+ 0x70, 0x72, 0x65, 0x66, 0x69, 0x78,
+ 0x14, 0x07, // MetaInfo
+ 0x18, 0x01, // ContentType
+ 0x01,
+ 0x19, 0x02, // FreshnessPeriod
+ 0x27, 0x10,
+ 0x15, 0x1a, // Content
+ 0x1f, 0x0c, // LinkDelegation
+ 0x1e, 0x01, // LinkPreference
+ 0x0a,
+ 0x07, 0x07, // Name
+ 0x08, 0x05,
+ 0x6c, 0x6f, 0x63, 0x61, 0x6c,
+ 0x1f, 0x0a, // LinkDelegation
+ 0x1e, 0x01, // LinkPreference
+ 0x14,
+ 0x07, 0x05, // Name
+ 0x08, 0x03,
+ 0x6e, 0x64, 0x6e,
+ 0x16, 0x1b, // SignatureInfo
+ 0x1b, 0x01, // SignatureType
+ 0x01,
+ 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,
+ 0x17, 0x80, // SignatureValue
+ 0x2f, 0xd6, 0xf1, 0x6e, 0x80, 0x6f, 0x10, 0xbe, 0xb1, 0x6f, 0x3e, 0x31, 0xec,
+ 0xe3, 0xb9, 0xea, 0x83, 0x30, 0x40, 0x03, 0xfc, 0xa0, 0x13, 0xd9, 0xb3, 0xc6,
+ 0x25, 0x16, 0x2d, 0xa6, 0x58, 0x41, 0x69, 0x62, 0x56, 0xd8, 0xb3, 0x6a, 0x38,
+ 0x76, 0x56, 0xea, 0x61, 0xb2, 0x32, 0x70, 0x1c, 0xb6, 0x4d, 0x10, 0x1d, 0xdc,
+ 0x92, 0x8e, 0x52, 0xa5, 0x8a, 0x1d, 0xd9, 0x96, 0x5e, 0xc0, 0x62, 0x0b, 0xcf,
+ 0x3a, 0x9d, 0x7f, 0xca, 0xbe, 0xa1, 0x41, 0x71, 0x85, 0x7a, 0x8b, 0x5d, 0xa9,
+ 0x64, 0xd6, 0x66, 0xb4, 0xe9, 0x8d, 0x0c, 0x28, 0x43, 0xee, 0xa6, 0x64, 0xe8,
+ 0x55, 0xf6, 0x1c, 0x19, 0x0b, 0xef, 0x99, 0x25, 0x1e, 0xdc, 0x78, 0xb3, 0xa7,
+ 0xaa, 0x0d, 0x14, 0x58, 0x30, 0xe5, 0x37, 0x6a, 0x6d, 0xdb, 0x56, 0xac, 0xa3,
+ 0xfc, 0x90, 0x7a, 0xb8, 0x66, 0x9c, 0x0e, 0xf6, 0xb7, 0x64, 0xd1
+};
+
+BOOST_AUTO_TEST_SUITE(EncodeDecode)
+
+BOOST_AUTO_TEST_CASE(Decode)
+{
+ Link link(Block(GOOD_LINK, sizeof(GOOD_LINK)));
+ BOOST_CHECK_EQUAL(link.getName(), Name("/local/ndn/prefix"));
+ BOOST_CHECK_EQUAL(link.getDelegationList(),
+ DelegationList({{10, Name("/local")}, {20, Name("/ndn")}}));
+}
+
+BOOST_AUTO_TEST_CASE(DecodeBadContentType)
+{
+ Data linkData(Block(GOOD_LINK, sizeof(GOOD_LINK)));
+ linkData.setContentType(tlv::ContentType_Key);
+ Block badLink = linkData.wireEncode();
+
+ BOOST_CHECK_THROW((Link(badLink)), Link::Error);
+ Link link;
+ BOOST_CHECK_THROW(link.wireDecode(badLink), Link::Error);
+}
+
+BOOST_AUTO_TEST_CASE(Encode)
+{
+ Link link1("/test", {{10, "/test1"}, {20, "/test2"}, {100, "/test3"}});
+ signData(link1);
+ Block wire = link1.wireEncode();
+
+ Link link2(wire);
+ BOOST_CHECK_EQUAL(link2.getName(), "/test");
+ BOOST_CHECK_EQUAL(link2.getDelegationList(),
+ DelegationList({{10, "/test1"}, {20, "/test2"}, {100, "/test3"}}));
+}
+
+BOOST_AUTO_TEST_SUITE_END() // EncodeDecode
+
+BOOST_AUTO_TEST_SUITE(Modify)
+
+BOOST_AUTO_TEST_CASE(SetDelegationList)
+{
+ Link link("/test");
+ BOOST_CHECK_EQUAL(link.getDelegationList(), DelegationList());
+
+ link.setDelegationList(DelegationList({{10, "/test1"}, {20, "/test2"}}));
+ BOOST_CHECK_EQUAL(link.getDelegationList(), DelegationList({{10, "/test1"}, {20, "/test2"}}));
+}
+
+BOOST_AUTO_TEST_CASE(AddDelegation)
+{
+ Link link1("/test", {{10, "/test1"}, {20, "/test2"}, {100, "/test3"}});
+ BOOST_CHECK_EQUAL(link1.getDelegationList(),
+ DelegationList({{10, "/test1"}, {20, "/test2"}, {100, "/test3"}}));
+
+ link1.addDelegation(30, "/test4");
+ BOOST_CHECK_EQUAL(link1.getDelegationList(),
+ DelegationList({{10, "/test1"}, {20, "/test2"}, {30, "/test4"}, {100, "/test3"}}));
+
+ link1.addDelegation(40, "/test2");
+ BOOST_CHECK_EQUAL(link1.getDelegationList(),
+ DelegationList({{10, "/test1"}, {30, "/test4"}, {40, "/test2"}, {100, "/test3"}}));
+
+ signData(link1);
+ Link link2(link1.wireEncode());
+ BOOST_CHECK_EQUAL(link2.getDelegationList(),
+ DelegationList({{10, "/test1"}, {30, "/test4"}, {40, "/test2"}, {100, "/test3"}}));
+}
+
+BOOST_AUTO_TEST_CASE(RemoveDelegation)
+{
+ Link link1("/test", {{10, "/test1"}, {20, "/test2"}, {100, "/test3"}});
+ BOOST_CHECK_EQUAL(link1.getDelegationList(),
+ DelegationList({{10, "/test1"}, {20, "/test2"}, {100, "/test3"}}));
+
+ link1.removeDelegation("/test4"); // non-existent
+ BOOST_CHECK_EQUAL(link1.getDelegationList(),
+ DelegationList({{10, "/test1"}, {20, "/test2"}, {100, "/test3"}}));
+
+ link1.removeDelegation("/test2");
+ BOOST_CHECK_EQUAL(link1.getDelegationList(),
+ DelegationList({{10, "/test1"}, {100, "/test3"}}));
+
+ signData(link1);
+ Link link2(link1.wireEncode());
+ BOOST_CHECK_EQUAL(link2.getDelegationList(),
+ DelegationList({{10, "/test1"}, {100, "/test3"}}));
+
+ link1.removeDelegation("/test1");
+ link1.removeDelegation("/test3");
+ BOOST_CHECK_EQUAL(link1.getDelegationList(), DelegationList());
+}
+
+BOOST_AUTO_TEST_SUITE_END() // Modify
+
+BOOST_AUTO_TEST_SUITE_END() // TestLink
+
+} // namespace tests
+} // namespace ndn
diff --git a/tests/unit/lp/cache-policy.t.cpp b/tests/unit/lp/cache-policy.t.cpp
new file mode 100644
index 0000000..6f1050d
--- /dev/null
+++ b/tests/unit/lp/cache-policy.t.cpp
@@ -0,0 +1,106 @@
+/* -*- 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 Eric Newberry <enewberry@email.arizona.edu>
+ */
+
+#include "lp/cache-policy.hpp"
+
+#include "boost-test.hpp"
+
+namespace ndn {
+namespace lp {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Lp)
+BOOST_AUTO_TEST_SUITE(TestCachePolicy)
+
+BOOST_AUTO_TEST_CASE(Encode)
+{
+ CachePolicy policy;
+ policy.setPolicy(CachePolicyType::NO_CACHE);
+
+ Block wire;
+ BOOST_REQUIRE_NO_THROW(wire = policy.wireEncode());
+
+ // Sample encoded value obtained with:
+ // for (Buffer::const_iterator it = wire.begin(); it != wire.end(); ++it) {
+ // printf("0x%02x, ", *it);
+ // }
+
+ // Contains CachePolicyType::NO_CACHE
+ static const uint8_t expectedBlock[] = {
+ 0xfd, 0x03, 0x34, 0x05, 0xfd, 0x03, 0x35, 0x01, 0x01
+ };
+
+ BOOST_CHECK_EQUAL_COLLECTIONS(expectedBlock, expectedBlock + sizeof(expectedBlock),
+ wire.begin(), wire.end());
+
+ BOOST_REQUIRE_NO_THROW(policy.wireDecode(wire));
+}
+
+BOOST_AUTO_TEST_CASE(DecodeUnknownPolicyError)
+{
+ static const uint8_t expectedBlock[] = {
+ 0xfd, 0x03, 0x34, 0x08, 0xfd, 0x03, 0x35, 0x04, 0xff, 0xff, 0xff, 0xff
+ };
+
+ CachePolicy policy;
+ Block wire(expectedBlock, sizeof(expectedBlock));
+ BOOST_REQUIRE_THROW(policy.wireDecode(wire), CachePolicy::Error);
+}
+
+BOOST_AUTO_TEST_CASE(DecodeMissingPolicyError)
+{
+ static const uint8_t inputBlock[] = {
+ 0xfd, 0x03, 0x34, 0x00
+ };
+
+ CachePolicy policy;
+ Block wire(inputBlock, sizeof(inputBlock));
+ BOOST_REQUIRE_THROW(policy.wireDecode(wire), CachePolicy::Error);
+}
+
+BOOST_AUTO_TEST_CASE(DecodeInvalidPolicyError)
+{
+ static const uint8_t inputBlock[] = {
+ 0xfd, 0x03, 0x34, 0x05, 0xfd, 0x03, 0x35, 0x01, 0x00
+ };
+
+ CachePolicy policy;
+ Block wire(inputBlock, sizeof(inputBlock));
+ BOOST_REQUIRE_THROW(policy.wireDecode(wire), CachePolicy::Error);
+}
+
+BOOST_AUTO_TEST_CASE(Policy)
+{
+ CachePolicy policy;
+ BOOST_CHECK_EQUAL(policy.getPolicy(), CachePolicyType::NONE);
+
+ policy.setPolicy(CachePolicyType::NO_CACHE);
+ BOOST_CHECK_EQUAL(policy.getPolicy(), CachePolicyType::NO_CACHE);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestCachePolicy
+BOOST_AUTO_TEST_SUITE_END() // Lp
+
+} // namespace tests
+} // namespace lp
+} // namespace ndn
diff --git a/tests/unit/lp/nack-header.t.cpp b/tests/unit/lp/nack-header.t.cpp
new file mode 100644
index 0000000..0645f40
--- /dev/null
+++ b/tests/unit/lp/nack-header.t.cpp
@@ -0,0 +1,117 @@
+/* -*- 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 Eric Newberry <enewberry@email.arizona.edu>
+ */
+
+#include "lp/nack-header.hpp"
+
+#include "boost-test.hpp"
+
+namespace ndn {
+namespace lp {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Lp)
+BOOST_AUTO_TEST_SUITE(TestNackHeader)
+
+BOOST_AUTO_TEST_CASE(IsLessSevere)
+{
+ BOOST_CHECK_EQUAL(isLessSevere(NackReason::NONE, NackReason::NONE), false);
+ BOOST_CHECK_EQUAL(isLessSevere(NackReason::CONGESTION, NackReason::CONGESTION), false);
+
+ BOOST_CHECK_EQUAL(isLessSevere(NackReason::CONGESTION, NackReason::NONE), true);
+ BOOST_CHECK_EQUAL(isLessSevere(NackReason::NONE, NackReason::CONGESTION), false);
+
+ BOOST_CHECK_EQUAL(isLessSevere(NackReason::CONGESTION, NackReason::NO_ROUTE), true);
+ BOOST_CHECK_EQUAL(isLessSevere(NackReason::NO_ROUTE, NackReason::CONGESTION), false);
+}
+
+BOOST_AUTO_TEST_CASE(Encode)
+{
+ NackHeader header;
+ header.setReason(NackReason::DUPLICATE);
+
+ Block wire;
+ BOOST_REQUIRE_NO_THROW(wire = header.wireEncode());
+
+ // Sample encoded value obtained with:
+ // for (Buffer::const_iterator it = wire.begin(); it != wire.end(); ++it) {
+ // printf("0x%02x, ", *it);
+ // }
+
+ // Contains NackReason::DUPLICATE
+ static const uint8_t expectedBlock[] = {
+ 0xfd, 0x03, 0x20, 0x05, 0xfd, 0x03, 0x21, 0x01, 0x64,
+ };
+
+ BOOST_CHECK_EQUAL_COLLECTIONS(expectedBlock, expectedBlock + sizeof(expectedBlock),
+ wire.begin(), wire.end());
+
+ BOOST_REQUIRE_NO_THROW(header.wireDecode(wire));
+}
+
+BOOST_AUTO_TEST_CASE(DecodeUnknownReasonCode)
+{
+ static const uint8_t expectedBlock[] = {
+ 0xfd, 0x03, 0x20, 0x08, 0xfd, 0x03, 0x21, 0x04, 0xff, 0xff, 0xff, 0xff,
+ };
+
+ NackHeader header;
+ Block wire(expectedBlock, sizeof(expectedBlock));
+ BOOST_REQUIRE_NO_THROW(header.wireDecode(wire));
+ Block wireEncoded;
+ BOOST_REQUIRE_NO_THROW(wireEncoded = header.wireEncode());
+ BOOST_CHECK_EQUAL_COLLECTIONS(expectedBlock, expectedBlock + sizeof(expectedBlock),
+ wireEncoded.begin(), wireEncoded.end());
+ BOOST_CHECK_EQUAL(header.getReason(), NackReason::NONE);
+}
+
+BOOST_AUTO_TEST_CASE(DecodeOmitReason)
+{
+ static const uint8_t expectedBlock[] = {
+ 0xfd, 0x03, 0x20, 0x00,
+ };
+
+ NackHeader header;
+ Block wire(expectedBlock, sizeof(expectedBlock));
+ BOOST_REQUIRE_NO_THROW(header.wireDecode(wire));
+ Block wireEncoded;
+ BOOST_REQUIRE_NO_THROW(wireEncoded = header.wireEncode());
+ BOOST_CHECK_EQUAL_COLLECTIONS(expectedBlock, expectedBlock + sizeof(expectedBlock),
+ wireEncoded.begin(), wireEncoded.end());
+ BOOST_CHECK_EQUAL(header.getReason(), NackReason::NONE);
+}
+
+BOOST_AUTO_TEST_CASE(Reason)
+{
+ NackHeader header;
+ BOOST_CHECK_EQUAL(header.getReason(), NackReason::NONE);
+
+ header.setReason(NackReason::DUPLICATE);
+ BOOST_CHECK_EQUAL(header.getReason(), NackReason::DUPLICATE);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestNackHeader
+BOOST_AUTO_TEST_SUITE_END() // Lp
+
+} // namespace tests
+} // namespace lp
+} // namespace ndn
diff --git a/tests/unit/lp/nack.t.cpp b/tests/unit/lp/nack.t.cpp
new file mode 100644
index 0000000..436a649
--- /dev/null
+++ b/tests/unit/lp/nack.t.cpp
@@ -0,0 +1,65 @@
+/* -*- 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 Eric Newberry <enewberry@email.arizona.edu>
+ */
+
+#include "lp/nack.hpp"
+
+#include "boost-test.hpp"
+
+namespace ndn {
+namespace lp {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Lp)
+BOOST_AUTO_TEST_SUITE(TestNack)
+
+BOOST_AUTO_TEST_CASE(Members)
+{
+ Name name("ndn:/test");
+ Interest interest(name);
+ Nack nack(interest);
+
+ BOOST_CHECK_EQUAL(nack.getInterest().getName(), name);
+
+ NackHeader header;
+ header.setReason(NackReason::CONGESTION);
+ nack.setHeader(header);
+ BOOST_CHECK_EQUAL(nack.getHeader().getReason(), header.getReason());
+
+ BOOST_CHECK_EQUAL(nack.getHeader().getReason(), nack.getReason());
+
+ nack.setReason(NackReason::DUPLICATE);
+ BOOST_CHECK_EQUAL(nack.getReason(), NackReason::DUPLICATE);
+
+ nack.setReason(NackReason::NO_ROUTE);
+ BOOST_CHECK_EQUAL(nack.getReason(), NackReason::NO_ROUTE);
+
+ Nack nack2(interest);
+ BOOST_CHECK_EQUAL(nack2.getReason(), NackReason::NONE);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestNack
+BOOST_AUTO_TEST_SUITE_END() // Lp
+
+} // namespace tests
+} // namespace lp
+} // namespace ndn
diff --git a/tests/unit/lp/packet.t.cpp b/tests/unit/lp/packet.t.cpp
new file mode 100644
index 0000000..1583772
--- /dev/null
+++ b/tests/unit/lp/packet.t.cpp
@@ -0,0 +1,453 @@
+/* -*- 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 "lp/packet.hpp"
+#include "prefix-announcement.hpp"
+#include "security/signature-sha256-with-rsa.hpp"
+
+#include "boost-test.hpp"
+#include "identity-management-fixture.hpp"
+
+namespace ndn {
+namespace lp {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Lp)
+BOOST_AUTO_TEST_SUITE(TestPacket)
+
+BOOST_AUTO_TEST_CASE(FieldAccess)
+{
+ Packet packet;
+
+ BOOST_CHECK(packet.empty());
+ BOOST_CHECK(!packet.has<FragIndexField>());
+ BOOST_CHECK_EQUAL(0, packet.count<FragIndexField>());
+
+ packet.set<FragIndexField>(1234);
+ BOOST_CHECK(!packet.empty());
+ BOOST_CHECK(packet.has<FragIndexField>());
+ BOOST_CHECK_THROW(packet.add<FragIndexField>(5678), std::length_error);
+ BOOST_CHECK_EQUAL(1, packet.count<FragIndexField>());
+ BOOST_CHECK_EQUAL(1234, packet.get<FragIndexField>(0));
+ BOOST_CHECK_THROW(packet.get<FragIndexField>(1), std::out_of_range);
+ BOOST_CHECK_THROW(packet.remove<FragIndexField>(1), std::out_of_range);
+
+ packet.remove<FragIndexField>(0);
+ BOOST_CHECK_EQUAL(0, packet.count<FragIndexField>());
+
+ packet.add<FragIndexField>(832);
+ std::vector<uint64_t> fragIndexes = packet.list<FragIndexField>();
+ BOOST_CHECK_EQUAL(1, fragIndexes.size());
+ BOOST_CHECK_EQUAL(832, fragIndexes.at(0));
+
+ packet.clear<FragIndexField>();
+ BOOST_CHECK_EQUAL(0, packet.count<FragIndexField>());
+ BOOST_CHECK(packet.empty());
+}
+
+/// \todo test field access methods with a REPEATABLE field
+
+BOOST_AUTO_TEST_CASE(EncodeFragment)
+{
+ static const uint8_t expectedBlock[] = {
+ 0x64, 0x0e, // LpPacket
+ 0x51, 0x08, // Sequence
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xe8,
+ 0x50, 0x02, // Fragment
+ 0x03, 0xe8,
+ };
+
+ Buffer buf(2);
+ buf[0] = 0x03;
+ buf[1] = 0xe8;
+
+ Packet packet;
+ packet.add<FragmentField>(std::make_pair(buf.begin(), buf.end()));
+ packet.add<SequenceField>(1000);
+ Block wire = packet.wireEncode();
+ BOOST_CHECK_EQUAL_COLLECTIONS(expectedBlock, expectedBlock + sizeof(expectedBlock),
+ wire.begin(), wire.end());
+}
+
+BOOST_AUTO_TEST_CASE(EncodeSubTlv)
+{
+ static const uint8_t expectedBlock[] = {
+ 0x64, 0x09, // LpPacket
+ 0xfd, 0x03, 0x20, 0x05, // Nack
+ 0xfd, 0x03, 0x21, 0x01, // NackReason
+ 0x64,
+ };
+
+ NackHeader nack;
+ nack.setReason(NackReason::DUPLICATE);
+
+ Packet packet;
+ BOOST_CHECK_NO_THROW(packet.add<NackField>(nack));
+ Block wire;
+ BOOST_REQUIRE_NO_THROW(wire = packet.wireEncode());
+ BOOST_CHECK_EQUAL_COLLECTIONS(expectedBlock, expectedBlock + sizeof(expectedBlock),
+ wire.begin(), wire.end());
+}
+
+BOOST_AUTO_TEST_CASE(EncodeZeroLengthTlv)
+{
+ static const uint8_t expectedBlock[] = {
+ 0x64, 0x04, // LpPacket
+ 0xfd, 0x03, 0x4c, 0x00, // NonDiscovery
+ };
+
+ Packet packet1, packet2;
+ BOOST_CHECK_NO_THROW(packet1.set<NonDiscoveryField>(EmptyValue{}));
+ Block wire;
+ BOOST_REQUIRE_NO_THROW(wire = packet1.wireEncode());
+ BOOST_CHECK_EQUAL_COLLECTIONS(expectedBlock, expectedBlock + sizeof(expectedBlock),
+ wire.begin(), wire.end());
+
+ BOOST_CHECK_NO_THROW(packet2.add<NonDiscoveryField>(EmptyValue{}));
+ BOOST_REQUIRE_NO_THROW(wire = packet2.wireEncode());
+ BOOST_CHECK_EQUAL_COLLECTIONS(expectedBlock, expectedBlock + sizeof(expectedBlock),
+ wire.begin(), wire.end());
+}
+
+BOOST_AUTO_TEST_CASE(EncodeSortOrder)
+{
+ static const uint8_t expectedBlock[] = {
+ 0x64, 0x2e, // LpPacket
+ 0x52, 0x01, // FragIndex
+ 0x00,
+ 0x53, 0x01, // FragCount
+ 0x01,
+ 0xfd, 0x03, 0x44, 0x08, // Ack
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+ 0xfd, 0x03, 0x44, 0x08, // Ack
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04,
+ 0xfd, 0x03, 0x44, 0x08, // Ack
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
+ 0x50, 0x02, // Fragment
+ 0x03, 0xe8,
+ };
+
+ Buffer frag(2);
+ frag[0] = 0x03;
+ frag[1] = 0xe8;
+
+ Packet packet;
+ BOOST_CHECK_NO_THROW(packet.add<FragmentField>(std::make_pair(frag.begin(), frag.end())));
+ BOOST_CHECK_NO_THROW(packet.add<FragIndexField>(0));
+ BOOST_CHECK_NO_THROW(packet.add<AckField>(2));
+ BOOST_REQUIRE_NO_THROW(packet.wireEncode());
+ BOOST_CHECK_NO_THROW(packet.add<FragCountField>(1));
+ BOOST_REQUIRE_NO_THROW(packet.wireEncode());
+ BOOST_CHECK_NO_THROW(packet.add<AckField>(4));
+ BOOST_REQUIRE_NO_THROW(packet.wireEncode());
+ BOOST_CHECK_NO_THROW(packet.add<AckField>(3));
+ BOOST_REQUIRE_NO_THROW(packet.wireEncode());
+ Block wire;
+ BOOST_REQUIRE_NO_THROW(wire = packet.wireEncode());
+ BOOST_CHECK_EQUAL_COLLECTIONS(expectedBlock, expectedBlock + sizeof(expectedBlock),
+ wire.begin(), wire.end());
+}
+
+BOOST_AUTO_TEST_CASE(DecodeNormal)
+{
+ static const uint8_t inputBlock[] = {
+ 0x64, 0x0a, // LpPacket
+ 0x52, 0x01, // FragIndex
+ 0x00,
+ 0x53, 0x01, // FragCount
+ 0x01,
+ 0x50, 0x02, // Fragment
+ 0x03, 0xe8,
+ };
+
+ Packet packet;
+ Block wire(inputBlock, sizeof(inputBlock));
+ BOOST_CHECK_NO_THROW(packet.wireDecode(wire));
+ BOOST_CHECK_EQUAL(1, packet.count<FragmentField>());
+ BOOST_CHECK_EQUAL(1, packet.count<FragIndexField>());
+ BOOST_CHECK_EQUAL(1, packet.count<FragCountField>());
+ Buffer::const_iterator first, last;
+ BOOST_REQUIRE_NO_THROW(std::tie(first, last) = packet.get<FragmentField>(0));
+ BOOST_CHECK_EQUAL(2, last - first);
+ BOOST_CHECK_EQUAL(0x03, *first);
+ BOOST_CHECK_EQUAL(0xe8, *(last - 1));
+ BOOST_CHECK_EQUAL(0, packet.get<FragIndexField>(0));
+ BOOST_CHECK_EQUAL(1, packet.get<FragCountField>(0));
+}
+
+BOOST_AUTO_TEST_CASE(DecodeIdle)
+{
+ static const uint8_t inputBlock[] = {
+ 0x64, 0x06, // LpPacket
+ 0x52, 0x01, // FragIndex
+ 0x00,
+ 0x53, 0x01, // FragCount
+ 0x01,
+ };
+
+ Packet packet;
+ Block wire(inputBlock, sizeof(inputBlock));
+ BOOST_CHECK_NO_THROW(packet.wireDecode(wire));
+ BOOST_CHECK_EQUAL(0, packet.count<FragmentField>());
+ BOOST_CHECK_EQUAL(1, packet.count<FragIndexField>());
+ BOOST_CHECK_EQUAL(1, packet.count<FragCountField>());
+ BOOST_CHECK_EQUAL(0, packet.get<FragIndexField>(0));
+ BOOST_CHECK_EQUAL(1, packet.get<FragCountField>(0));
+}
+
+BOOST_AUTO_TEST_CASE(DecodeFragment)
+{
+ static const uint8_t inputBlock[] = {
+ 0x64, 0x04, // LpPacket
+ 0x50, 0x02, // Fragment
+ 0x03, 0xe8,
+ };
+
+ Packet packet;
+ Block wire(inputBlock, sizeof(inputBlock));
+ BOOST_CHECK_NO_THROW(packet.wireDecode(wire));
+ BOOST_CHECK_EQUAL(1, packet.count<FragmentField>());
+ BOOST_CHECK_EQUAL(0, packet.count<FragIndexField>());
+ Buffer::const_iterator first, last;
+ BOOST_REQUIRE_NO_THROW(std::tie(first, last) = packet.get<FragmentField>(0));
+ BOOST_CHECK_EQUAL(2, last - first);
+ BOOST_CHECK_EQUAL(0x03, *first);
+ BOOST_CHECK_EQUAL(0xe8, *(last - 1));
+}
+
+BOOST_AUTO_TEST_CASE(DecodeNonDiscoveryHeader)
+{
+ static const uint8_t inputBlock[] = {
+ 0x64, 0x04, // LpPacket
+ 0xfd, 0x03, 0x4c, 0x00, // NonDiscovery
+ };
+
+ Packet packet;
+ Block wire(inputBlock, sizeof(inputBlock));
+ BOOST_CHECK_NO_THROW(packet.wireDecode(wire));
+ BOOST_CHECK_EQUAL(true, packet.has<NonDiscoveryField>());
+ BOOST_CHECK_NO_THROW(packet.get<NonDiscoveryField>());
+}
+
+BOOST_AUTO_TEST_CASE(DecodeEmpty)
+{
+ static const uint8_t inputBlock[] = {
+ 0x64, 0x00, // LpPacket
+ };
+
+ Packet packet;
+ Block wire(inputBlock, sizeof(inputBlock));
+ BOOST_CHECK_NO_THROW(packet.wireDecode(wire));
+ BOOST_CHECK_EQUAL(0, packet.count<FragmentField>());
+ BOOST_CHECK_EQUAL(0, packet.count<FragIndexField>());
+ BOOST_CHECK_EQUAL(false, packet.has<NonDiscoveryField>());
+}
+
+BOOST_AUTO_TEST_CASE(DecodeRepeatedNonRepeatableHeader)
+{
+ static const uint8_t inputBlock[] = {
+ 0x64, 0x06, // LpPacket
+ 0x52, 0x01, // FragIndex
+ 0x00,
+ 0x52, 0x01, // FragIndex
+ 0x01,
+ };
+
+ Packet packet;
+ Block wire(inputBlock, sizeof(inputBlock));
+ BOOST_CHECK_THROW(packet.wireDecode(wire), Packet::Error);
+}
+
+BOOST_AUTO_TEST_CASE(DecodeRepeatedRepeatableHeader)
+{
+ static const uint8_t inputBlock[] = {
+ 0x64, 0x0f, // LpPacket
+ 0xfd, 0x03, 0x44, 0x01, // Ack
+ 0x01,
+ 0xfd, 0x03, 0x44, 0x01, // Ack
+ 0x03,
+ 0xfd, 0x03, 0x44, 0x01, // Ack
+ 0x02,
+ };
+
+ Packet packet;
+ Block wire(inputBlock, sizeof(inputBlock));
+ BOOST_CHECK_NO_THROW(packet.wireDecode(wire));
+ BOOST_REQUIRE_EQUAL(packet.count<AckField>(), 3);
+ BOOST_CHECK_EQUAL(packet.get<AckField>(), 1);
+ BOOST_CHECK_EQUAL(packet.get<AckField>(0), 1);
+ BOOST_CHECK_EQUAL(packet.get<AckField>(1), 3);
+ BOOST_CHECK_EQUAL(packet.get<AckField>(2), 2);
+}
+
+BOOST_AUTO_TEST_CASE(DecodeRepeatedFragment)
+{
+ static const uint8_t inputBlock[] = {
+ 0x64, 0x08, // LpPacket
+ 0x50, 0x02, // Fragment
+ 0x03, 0xe8,
+ 0x50, 0x02, // Fragment
+ 0x03, 0xe9,
+ };
+
+ Packet packet;
+ Block wire(inputBlock, sizeof(inputBlock));
+ BOOST_CHECK_THROW(packet.wireDecode(wire), Packet::Error);
+}
+
+BOOST_AUTO_TEST_CASE(DecodeWrongOrderAmongHeaders)
+{
+ static const uint8_t inputBlock[] = {
+ 0x64, 0x0a, // LpPacket
+ 0x53, 0x01, // FragCount
+ 0x01,
+ 0x52, 0x01, // FragIndex
+ 0x00,
+ 0x50, 0x02, // Fragment
+ 0x03, 0xe8,
+ };
+
+ Packet packet;
+ Block wire(inputBlock, sizeof(inputBlock));
+ BOOST_CHECK_THROW(packet.wireDecode(wire), Packet::Error);
+}
+
+BOOST_AUTO_TEST_CASE(DecodeWrongOrderFragment)
+{
+ static const uint8_t inputBlock[] = {
+ 0x64, 0x0a, // LpPacket
+ 0x52, 0x01, // FragIndex
+ 0x00,
+ 0x50, 0x02, // Fragment
+ 0x03, 0xe8,
+ 0x53, 0x01, // FragCount
+ 0x01,
+ };
+
+ Packet packet;
+ Block wire(inputBlock, sizeof(inputBlock));
+ BOOST_CHECK_THROW(packet.wireDecode(wire), Packet::Error);
+}
+
+BOOST_AUTO_TEST_CASE(DecodeIgnoredHeader)
+{
+ static const uint8_t inputBlock[] = {
+ 0x64, 0x0c, // LpPacket
+ 0x52, 0x01, // FragIndex
+ 0x00,
+ 0xfd, 0x03, 0x24, 0x01, // unknown TLV-TYPE 804 (ignored)
+ 0x02,
+ 0x50, 0x02, // Fragment
+ 0x03, 0xe8,
+ };
+
+ Packet packet;
+ Block wire(inputBlock, sizeof(inputBlock));
+ BOOST_CHECK_NO_THROW(packet.wireDecode(wire));
+ BOOST_CHECK_EQUAL(1, packet.count<FragmentField>());
+ BOOST_CHECK_EQUAL(1, packet.count<FragIndexField>());
+}
+
+BOOST_AUTO_TEST_CASE(DecodeUnrecognizedHeader)
+{
+ static const uint8_t inputBlock[] = {
+ 0x64, 0x0c, // LpPacket
+ 0x52, 0x01, // FragIndex
+ 0x00,
+ 0xfd, 0x03, 0x22, 0x01, // unknown TLV-TYPE 802 (cannot ignore)
+ 0x02,
+ 0x50, 0x02, // Fragment
+ 0x03, 0xe8,
+ };
+
+ Packet packet;
+ Block wire(inputBlock, sizeof(inputBlock));
+ BOOST_CHECK_THROW(packet.wireDecode(wire), Packet::Error);
+}
+
+BOOST_AUTO_TEST_CASE(DecodeBareNetworkLayerPacket)
+{
+ static const uint8_t inputBlock[] = {
+ 0x05, 0x0a, // Interest
+ 0x07, 0x02, // Name
+ 0x03, 0xe8,
+ 0x0a, 0x04, // Nonce
+ 0x01, 0x02, 0x03, 0x04,
+ };
+
+ Packet packet;
+ Block wire(inputBlock, sizeof(inputBlock));
+ BOOST_CHECK_NO_THROW(packet.wireDecode(wire));
+ BOOST_CHECK_EQUAL(1, packet.count<FragmentField>());
+
+ Block encoded;
+ BOOST_CHECK_NO_THROW(encoded = packet.wireEncode());
+ BOOST_CHECK_EQUAL_COLLECTIONS(inputBlock, inputBlock + sizeof(inputBlock),
+ encoded.begin(), encoded.end());
+}
+
+BOOST_AUTO_TEST_CASE(DecodeUnrecognizedTlvType)
+{
+ Packet packet;
+ Block wire = encoding::makeEmptyBlock(ndn::tlv::Name);
+ BOOST_CHECK_THROW(packet.wireDecode(wire), Packet::Error);
+}
+
+BOOST_FIXTURE_TEST_CASE(DecodePrefixAnnouncement, ndn::tests::IdentityManagementFixture)
+{
+ // Construct Data which prefix announcement is attached to
+ Data data0("/edu/ua/cs/news/index.html");
+ ndn::SignatureSha256WithRsa fakeSignature;
+ fakeSignature.setValue(ndn::encoding::makeEmptyBlock(ndn::tlv::SignatureValue));
+ data0.setSignature(fakeSignature);
+
+ Block wire;
+ wire = data0.wireEncode();
+ Packet packet0;
+ packet0.wireDecode(wire);
+
+ // Construct Prefix Announcement
+ PrefixAnnouncement pa;
+ pa.setAnnouncedName("/net/example");
+ pa.setExpiration(5_min);
+ pa.setValidityPeriod(security::ValidityPeriod(time::fromIsoString("20181030T000000"),
+ time::fromIsoString("20181124T235959")));
+ pa.toData(m_keyChain, signingWithSha256(), 1);
+ PrefixAnnouncementHeader pah0(pa);
+ BOOST_CHECK_NO_THROW(packet0.add<PrefixAnnouncementField>(pah0));
+ Block encoded;
+ BOOST_CHECK_NO_THROW(encoded = packet0.wireEncode());
+
+ // check decoding
+ Packet packet1;
+ BOOST_CHECK_NO_THROW(packet1.wireDecode(encoded));
+ BOOST_CHECK_EQUAL(true, packet1.has<PrefixAnnouncementField>());
+ PrefixAnnouncementHeader pah1;
+ BOOST_CHECK_NO_THROW(pah1 = packet1.get<PrefixAnnouncementField>());
+ BOOST_CHECK_EQUAL(pah1.getPrefixAnn()->getAnnouncedName(), "/net/example");
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestPacket
+BOOST_AUTO_TEST_SUITE_END() // Lp
+
+} // namespace tests
+} // namespace lp
+} // namespace ndn
diff --git a/tests/unit/lp/prefix-announcement-header.t.cpp b/tests/unit/lp/prefix-announcement-header.t.cpp
new file mode 100644
index 0000000..38a75f3
--- /dev/null
+++ b/tests/unit/lp/prefix-announcement-header.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 "lp/prefix-announcement-header.hpp"
+#include "lp/tlv.hpp"
+#include "security/signature-sha256-with-rsa.hpp"
+
+#include "boost-test.hpp"
+#include "identity-management-fixture.hpp"
+
+namespace ndn {
+namespace lp {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Lp)
+BOOST_FIXTURE_TEST_SUITE(TestPrefixAnnouncementHeader, ndn::tests::IdentityManagementFixture)
+
+BOOST_AUTO_TEST_CASE(EncodeDecode)
+{
+ EncodingEstimator estimator;
+ PrefixAnnouncementHeader header;
+ BOOST_CHECK_THROW(header.wireEncode(estimator), PrefixAnnouncementHeader::Error);
+ BOOST_CHECK_THROW(PrefixAnnouncementHeader{PrefixAnnouncement()}, PrefixAnnouncementHeader::Error);
+
+ PrefixAnnouncement pa;
+ pa.setAnnouncedName("/net/example");
+ pa.setExpiration(1_h);
+ const Data& data = pa.toData(m_keyChain, signingWithSha256(), 1);
+ Block encodedData = data.wireEncode();
+
+ Block expectedBlock(tlv::PrefixAnnouncement);
+ expectedBlock.push_back(encodedData);
+ expectedBlock.encode();
+
+ PrefixAnnouncementHeader pah0(pa);
+ size_t estimatedSize = pah0.wireEncode(estimator);
+ EncodingBuffer buffer(estimatedSize, 0);
+ pah0.wireEncode(buffer);
+ Block wire = buffer.block();
+ BOOST_CHECK_EQUAL(expectedBlock, wire);
+
+ PrefixAnnouncementHeader pah1;
+ pah1.wireDecode(wire);
+ BOOST_CHECK_EQUAL(*pah0.getPrefixAnn(), *pah1.getPrefixAnn());
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestPrefixAnnouncementHeader
+BOOST_AUTO_TEST_SUITE_END() // Lp
+
+} // namespace tests
+} // namespace lp
+} // namespace ndn
diff --git a/tests/unit/meta-info.t.cpp b/tests/unit/meta-info.t.cpp
new file mode 100644
index 0000000..2a2dac3
--- /dev/null
+++ b/tests/unit/meta-info.t.cpp
@@ -0,0 +1,164 @@
+/* -*- 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 "meta-info.hpp"
+#include "data.hpp"
+
+#include "boost-test.hpp"
+
+namespace ndn {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(TestMetaInfo)
+
+BOOST_AUTO_TEST_CASE_EXPECTED_FAILURES(EncodeDecodeEquality, 1)
+BOOST_AUTO_TEST_CASE(EncodeDecodeEquality)
+{
+ // default values
+ MetaInfo a("1406 type=180100 freshness=190100"_block);
+ BOOST_CHECK_EQUAL(a.getType(), tlv::ContentType_Blob);
+ BOOST_CHECK_EQUAL(a.getFreshnessPeriod(), 0_ms);
+ BOOST_CHECK(!a.getFinalBlock());
+ BOOST_CHECK_EQUAL(a, a);
+
+ MetaInfo b;
+ BOOST_CHECK_NE(a, b);
+ b.setType(a.getType());
+ b.setFreshnessPeriod(a.getFreshnessPeriod());
+ b.setFinalBlock(a.getFinalBlock());
+ BOOST_CHECK_EQUAL(b.wireEncode(), "1400"_block);
+ BOOST_CHECK_EQUAL(a, b); // expected failure #4569
+
+ // non-default values
+ Block wire2 = "140C type=180101 freshness=190266B2 finalblock=1A03080141"_block;
+ a.wireDecode(wire2);
+ BOOST_CHECK_EQUAL(a.getType(), tlv::ContentType_Link);
+ BOOST_CHECK_EQUAL(a.getFreshnessPeriod(), 26290_ms);
+ BOOST_CHECK_EQUAL(*a.getFinalBlock(), name::Component("A"));
+ BOOST_CHECK_NE(a, b);
+
+ b.setType(a.getType());
+ b.setFreshnessPeriod(a.getFreshnessPeriod());
+ b.setFinalBlock(a.getFinalBlock());
+ BOOST_CHECK_EQUAL(b.wireEncode(), wire2);
+ BOOST_CHECK_EQUAL(a, b);
+
+ // FinalBlockId is typed name component
+ Block wire3 = "1405 finalblock=1A03DD0141"_block;
+ a.wireDecode(wire3);
+ BOOST_CHECK_EQUAL(a.getType(), tlv::ContentType_Blob);
+ BOOST_CHECK_EQUAL(a.getFreshnessPeriod(), 0_ms);
+ BOOST_CHECK_EQUAL(*a.getFinalBlock(), name::Component::fromEscapedString("221=A"));
+ BOOST_CHECK_NE(a, b);
+
+ b.setType(a.getType());
+ b.setFreshnessPeriod(a.getFreshnessPeriod());
+ b.setFinalBlock(a.getFinalBlock());
+ BOOST_CHECK_EQUAL(b.wireEncode(), wire3);
+ BOOST_CHECK_EQUAL(a, b);
+}
+
+BOOST_AUTO_TEST_CASE(AppMetaInfo)
+{
+ MetaInfo info1;
+ info1.setType(196);
+ info1.setFreshnessPeriod(3600_ms);
+ info1.setFinalBlock(name::Component("/att/final"));
+
+ uint32_t ints[5] = {128, 129, 130, 131, 132};
+ std::string ss[5] = {"h", "hello", "hello, world", "hello, world, alex",
+ "hello, world, alex, I am Xiaoke Jiang"};
+
+ for (size_t i = 0; i < 5; i++) {
+ uint32_t type = 128 + i * 10;
+ info1.addAppMetaInfo(makeNonNegativeIntegerBlock(type, ints[i]));
+ type += 5;
+ info1.addAppMetaInfo(makeStringBlock(type, ss[i]));
+ }
+
+ BOOST_CHECK(info1.findAppMetaInfo(252) == nullptr);
+
+ info1.addAppMetaInfo(makeNonNegativeIntegerBlock(252, 1000));
+ BOOST_CHECK(info1.findAppMetaInfo(252) != nullptr);
+
+ info1.addAppMetaInfo(makeNonNegativeIntegerBlock(252, 1000));
+ BOOST_CHECK(info1.findAppMetaInfo(252) != nullptr);
+
+ info1.removeAppMetaInfo(252);
+ BOOST_CHECK(info1.findAppMetaInfo(252) != nullptr);
+
+ info1.removeAppMetaInfo(252);
+ BOOST_CHECK(info1.findAppMetaInfo(252) == nullptr);
+
+ // // These octets are obtained by the snippet below.
+ // // This check is intended to detect unexpected encoding change in the future.
+ // const Block& wire = info1.wireEncode();
+ // for (Buffer::const_iterator it = wire.begin(); it != wire.end(); ++it) {
+ // printf("0x%02x, ", *it);
+ // }
+ const uint8_t METAINFO[] = {0x14, 0x77, 0x18, 0x01, 0xc4, 0x19, 0x02, 0x0e, 0x10, 0x1a, 0x0c,
+ 0x08, 0x0a, 0x2f, 0x61, 0x74, 0x74, 0x2f, 0x66, 0x69, 0x6e, 0x61,
+ 0x6c, 0x80, 0x01, 0x80, 0x85, 0x01, 0x68, 0x8a, 0x01, 0x81, 0x8f,
+ 0x05, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x94, 0x01, 0x82, 0x99, 0x0c,
+ 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x2c, 0x20, 0x77, 0x6f, 0x72, 0x6c,
+ 0x64, 0x9e, 0x01, 0x83, 0xa3, 0x12, 0x68, 0x65, 0x6c, 0x6c, 0x6f,
+ 0x2c, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x2c, 0x20, 0x61, 0x6c,
+ 0x65, 0x78, 0xa8, 0x01, 0x84, 0xad, 0x25, 0x68, 0x65, 0x6c, 0x6c,
+ 0x6f, 0x2c, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x2c, 0x20, 0x61,
+ 0x6c, 0x65, 0x78, 0x2c, 0x20, 0x49, 0x20, 0x61, 0x6d, 0x20, 0x58,
+ 0x69, 0x61, 0x6f, 0x6b, 0x65, 0x20, 0x4a, 0x69, 0x61, 0x6e, 0x67};
+
+ BOOST_REQUIRE_EQUAL_COLLECTIONS(info1.wireEncode().begin(), info1.wireEncode().end(),
+ METAINFO, METAINFO + sizeof(METAINFO));
+
+ MetaInfo info2;
+ info2.wireDecode(Block(METAINFO, sizeof(METAINFO)));
+
+ for (size_t i = 0; i < 5; i++) {
+ uint32_t tlvType = 128 + i * 10;
+ const Block* block = info2.findAppMetaInfo(tlvType);
+ BOOST_REQUIRE(block != nullptr);
+ BOOST_CHECK_EQUAL(readNonNegativeInteger(*block), ints[i]);
+ tlvType += 5;
+
+ block = info2.findAppMetaInfo(tlvType);
+ BOOST_REQUIRE(block != nullptr);
+
+ std::string s3(reinterpret_cast<const char*>(block->value()), block->value_size());
+ BOOST_CHECK_EQUAL(s3, ss[i]);
+ }
+}
+
+BOOST_AUTO_TEST_CASE(AppMetaInfoTypeRange)
+{
+ MetaInfo info;
+
+ BOOST_CHECK_NO_THROW(info.addAppMetaInfo(makeNonNegativeIntegerBlock(128, 1000)));
+ BOOST_CHECK_NO_THROW(info.addAppMetaInfo(makeNonNegativeIntegerBlock(252, 1000)));
+
+ BOOST_CHECK_THROW(info.addAppMetaInfo(makeNonNegativeIntegerBlock(127, 1000)), MetaInfo::Error);
+ BOOST_CHECK_THROW(info.addAppMetaInfo(makeNonNegativeIntegerBlock(253, 1000)), MetaInfo::Error);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestMetaInfo
+
+} // namespace tests
+} // namespace ndn
diff --git a/tests/unit/mgmt/control-response.t.cpp b/tests/unit/mgmt/control-response.t.cpp
new file mode 100644
index 0000000..86aa780
--- /dev/null
+++ b/tests/unit/mgmt/control-response.t.cpp
@@ -0,0 +1,61 @@
+/* -*- 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 "mgmt/control-response.hpp"
+
+#include "boost-test.hpp"
+
+namespace ndn {
+namespace mgmt {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Mgmt)
+BOOST_AUTO_TEST_SUITE(TestControlResponse)
+
+static const uint8_t WIRE[] = {
+ 0x65, 0x17, // ControlResponse
+ 0x66, 0x02, // StatusCode
+ 0x01, 0x94,
+ 0x67, 0x11, // StatusText
+ 0x4e, 0x6f, 0x74, 0x68, 0x69, 0x6e, 0x67, 0x20, 0x6e, 0x6f, 0x74, 0x20,
+ 0x66, 0x6f, 0x75, 0x6e, 0x64};
+
+BOOST_AUTO_TEST_CASE(Encode)
+{
+ ControlResponse cr(404, "Nothing not found");
+ const Block& wire = cr.wireEncode();
+ BOOST_CHECK_EQUAL_COLLECTIONS(WIRE, WIRE + sizeof(WIRE),
+ wire.begin(), wire.end());
+}
+
+BOOST_AUTO_TEST_CASE(Decode)
+{
+ ControlResponse cr(Block(WIRE, sizeof(WIRE)));
+ BOOST_CHECK_EQUAL(cr.getCode(), 404);
+ BOOST_CHECK_EQUAL(cr.getText(), "Nothing not found");
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestControlResponse
+BOOST_AUTO_TEST_SUITE_END() // Mgmt
+
+} // namespace tests
+} // namespace mgmt
+} // namespace ndn
diff --git a/tests/unit/mgmt/dispatcher.t.cpp b/tests/unit/mgmt/dispatcher.t.cpp
new file mode 100644
index 0000000..c75af4d
--- /dev/null
+++ b/tests/unit/mgmt/dispatcher.t.cpp
@@ -0,0 +1,494 @@
+/* -*- 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 "mgmt/dispatcher.hpp"
+#include "mgmt/nfd/control-parameters.hpp"
+#include "util/dummy-client-face.hpp"
+
+#include "boost-test.hpp"
+#include "make-interest-data.hpp"
+#include "../identity-management-time-fixture.hpp"
+
+namespace ndn {
+namespace mgmt {
+namespace tests {
+
+using namespace ndn::tests;
+
+class DispatcherFixture : public IdentityManagementTimeFixture
+{
+public:
+ DispatcherFixture()
+ : face(io, m_keyChain, {true, true})
+ , dispatcher(face, m_keyChain, security::SigningInfo())
+ , storage(dispatcher.m_storage)
+ {
+ }
+
+public:
+ util::DummyClientFace face;
+ mgmt::Dispatcher dispatcher;
+ InMemoryStorageFifo& storage;
+};
+
+class VoidParameters : public mgmt::ControlParameters
+{
+public:
+ explicit
+ VoidParameters(const Block& wire)
+ {
+ wireDecode(wire);
+ }
+
+ Block
+ wireEncode() const final
+ {
+ return Block(128);
+ }
+
+ void
+ wireDecode(const Block& wire) final
+ {
+ if (wire.type() != 128)
+ BOOST_THROW_EXCEPTION(tlv::Error("Expecting TLV type 128"));
+ }
+};
+
+static Authorization
+makeTestAuthorization()
+{
+ return [] (const Name& prefix,
+ const Interest& interest,
+ const ControlParameters* params,
+ AcceptContinuation accept,
+ RejectContinuation reject) {
+ if (interest.getName()[-1] == name::Component("valid")) {
+ accept("");
+ }
+ else {
+ if (interest.getName()[-1] == name::Component("silent")) {
+ reject(RejectReply::SILENT);
+ }
+ else {
+ reject(RejectReply::STATUS403);
+ }
+ }
+ };
+}
+
+BOOST_AUTO_TEST_SUITE(Mgmt)
+BOOST_FIXTURE_TEST_SUITE(TestDispatcher, DispatcherFixture)
+
+BOOST_AUTO_TEST_CASE(Basic)
+{
+ BOOST_CHECK_NO_THROW(dispatcher
+ .addControlCommand<VoidParameters>("test/1", makeAcceptAllAuthorization(),
+ bind([] { return true; }),
+ bind([]{})));
+ BOOST_CHECK_NO_THROW(dispatcher
+ .addControlCommand<VoidParameters>("test/2", makeAcceptAllAuthorization(),
+ bind([] { return true; }),
+ bind([]{})));
+
+ BOOST_CHECK_THROW(dispatcher
+ .addControlCommand<VoidParameters>("test", makeAcceptAllAuthorization(),
+ bind([] { return true; }),
+ bind([]{})),
+ std::out_of_range);
+
+ BOOST_CHECK_NO_THROW(dispatcher.addStatusDataset("status/1",
+ makeAcceptAllAuthorization(), bind([]{})));
+ BOOST_CHECK_NO_THROW(dispatcher.addStatusDataset("status/2",
+ makeAcceptAllAuthorization(), bind([]{})));
+ BOOST_CHECK_THROW(dispatcher.addStatusDataset("status",
+ makeAcceptAllAuthorization(), bind([]{})),
+ std::out_of_range);
+
+ BOOST_CHECK_NO_THROW(dispatcher.addNotificationStream("stream/1"));
+ BOOST_CHECK_NO_THROW(dispatcher.addNotificationStream("stream/2"));
+ BOOST_CHECK_THROW(dispatcher.addNotificationStream("stream"), std::out_of_range);
+
+
+ BOOST_CHECK_NO_THROW(dispatcher.addTopPrefix("/root/1"));
+ BOOST_CHECK_NO_THROW(dispatcher.addTopPrefix("/root/2"));
+ BOOST_CHECK_THROW(dispatcher.addTopPrefix("/root"), std::out_of_range);
+
+ BOOST_CHECK_THROW(dispatcher
+ .addControlCommand<VoidParameters>("test/3", makeAcceptAllAuthorization(),
+ bind([] { return true; }),
+ bind([]{})),
+ std::domain_error);
+
+ BOOST_CHECK_THROW(dispatcher.addStatusDataset("status/3",
+ makeAcceptAllAuthorization(), bind([]{})),
+ std::domain_error);
+
+ BOOST_CHECK_THROW(dispatcher.addNotificationStream("stream/3"), std::domain_error);
+}
+
+BOOST_AUTO_TEST_CASE(AddRemoveTopPrefix)
+{
+ std::map<std::string, size_t> nCallbackCalled;
+ dispatcher
+ .addControlCommand<VoidParameters>("test/1", makeAcceptAllAuthorization(),
+ bind([] { return true; }),
+ bind([&nCallbackCalled] { ++nCallbackCalled["test/1"]; }));
+
+ dispatcher
+ .addControlCommand<VoidParameters>("test/2", makeAcceptAllAuthorization(),
+ bind([] { return true; }),
+ bind([&nCallbackCalled] { ++nCallbackCalled["test/2"]; }));
+
+ face.receive(*makeInterest("/root/1/test/1/%80%00"));
+ advanceClocks(1_ms);
+ BOOST_CHECK_EQUAL(nCallbackCalled["test/1"], 0);
+ BOOST_CHECK_EQUAL(nCallbackCalled["test/2"], 0);
+
+ dispatcher.addTopPrefix("/root/1");
+ advanceClocks(1_ms);
+
+ face.receive(*makeInterest("/root/1/test/1/%80%00"));
+ advanceClocks(1_ms);
+ BOOST_CHECK_EQUAL(nCallbackCalled["test/1"], 1);
+ BOOST_CHECK_EQUAL(nCallbackCalled["test/2"], 0);
+
+ face.receive(*makeInterest("/root/1/test/2/%80%00"));
+ advanceClocks(1_ms);
+ BOOST_CHECK_EQUAL(nCallbackCalled["test/1"], 1);
+ BOOST_CHECK_EQUAL(nCallbackCalled["test/2"], 1);
+
+ face.receive(*makeInterest("/root/2/test/1/%80%00"));
+ face.receive(*makeInterest("/root/2/test/2/%80%00"));
+ advanceClocks(1_ms);
+ BOOST_CHECK_EQUAL(nCallbackCalled["test/1"], 1);
+ BOOST_CHECK_EQUAL(nCallbackCalled["test/2"], 1);
+
+ dispatcher.addTopPrefix("/root/2");
+ advanceClocks(1_ms);
+
+ face.receive(*makeInterest("/root/1/test/1/%80%00"));
+ advanceClocks(1_ms);
+ BOOST_CHECK_EQUAL(nCallbackCalled["test/1"], 2);
+
+ face.receive(*makeInterest("/root/2/test/1/%80%00"));
+ advanceClocks(1_ms);
+ BOOST_CHECK_EQUAL(nCallbackCalled["test/1"], 3);
+
+ dispatcher.removeTopPrefix("/root/1");
+ advanceClocks(1_ms);
+
+ face.receive(*makeInterest("/root/1/test/1/%80%00"));
+ advanceClocks(1_ms);
+ BOOST_CHECK_EQUAL(nCallbackCalled["test/1"], 3);
+
+ face.receive(*makeInterest("/root/2/test/1/%80%00"));
+ advanceClocks(1_ms);
+ BOOST_CHECK_EQUAL(nCallbackCalled["test/1"], 4);
+}
+
+BOOST_AUTO_TEST_CASE(ControlCommand)
+{
+ size_t nCallbackCalled = 0;
+ dispatcher
+ .addControlCommand<VoidParameters>("test",
+ makeTestAuthorization(),
+ bind([] { return true; }),
+ bind([&nCallbackCalled] { ++nCallbackCalled; }));
+
+ dispatcher.addTopPrefix("/root");
+ advanceClocks(1_ms);
+ face.sentData.clear();
+
+ face.receive(*makeInterest("/root/test/%80%00")); // returns 403
+ face.receive(*makeInterest("/root/test/%80%00/invalid")); // returns 403
+ face.receive(*makeInterest("/root/test/%80%00/silent")); // silently ignored
+ face.receive(*makeInterest("/root/test/.../invalid")); // silently ignored (wrong format)
+ face.receive(*makeInterest("/root/test/.../valid")); // silently ignored (wrong format)
+ advanceClocks(1_ms, 20);
+ BOOST_CHECK_EQUAL(nCallbackCalled, 0);
+ BOOST_CHECK_EQUAL(face.sentData.size(), 2);
+
+ BOOST_CHECK_EQUAL(face.sentData[0].getContentType(), tlv::ContentType_Blob);
+ BOOST_CHECK_EQUAL(ControlResponse(face.sentData[0].getContent().blockFromValue()).getCode(), 403);
+ BOOST_CHECK_EQUAL(face.sentData[1].getContentType(), tlv::ContentType_Blob);
+ BOOST_CHECK_EQUAL(ControlResponse(face.sentData[1].getContent().blockFromValue()).getCode(), 403);
+
+ face.receive(*makeInterest("/root/test/%80%00/valid"));
+ advanceClocks(1_ms, 10);
+ BOOST_CHECK_EQUAL(nCallbackCalled, 1);
+}
+
+class StatefulParameters : public mgmt::ControlParameters
+{
+public:
+ explicit
+ StatefulParameters(const Block& wire)
+ {
+ wireDecode(wire);
+ }
+
+ Block
+ wireEncode() const final
+ {
+ return Block();
+ }
+
+ void
+ wireDecode(const Block& wire) final
+ {
+ m_state = EXPECTED_STATE;
+ }
+
+ bool
+ check() const
+ {
+ return m_state == EXPECTED_STATE;
+ }
+
+private:
+ static constexpr int EXPECTED_STATE = 12602;
+ int m_state = 0;
+};
+
+BOOST_AUTO_TEST_CASE(ControlCommandAsyncAuthorization) // Bug 4059
+{
+ AcceptContinuation authorizationAccept;
+ auto authorization =
+ [&authorizationAccept] (const Name& prefix, const Interest& interest, const ControlParameters* params,
+ AcceptContinuation accept, RejectContinuation reject) {
+ authorizationAccept = accept;
+ };
+
+ auto validateParameters =
+ [] (const ControlParameters& params) {
+ return dynamic_cast<const StatefulParameters&>(params).check();
+ };
+
+ size_t nCallbackCalled = 0;
+ dispatcher
+ .addControlCommand<StatefulParameters>("test",
+ authorization,
+ validateParameters,
+ bind([&nCallbackCalled] { ++nCallbackCalled; }));
+
+ dispatcher.addTopPrefix("/root");
+ advanceClocks(1_ms);
+
+ face.receive(*makeInterest("/root/test/%80%00"));
+ BOOST_CHECK_EQUAL(nCallbackCalled, 0);
+ BOOST_REQUIRE(authorizationAccept != nullptr);
+
+ advanceClocks(1_ms);
+ authorizationAccept("");
+ advanceClocks(1_ms);
+ BOOST_CHECK_EQUAL(nCallbackCalled, 1);
+}
+
+BOOST_AUTO_TEST_CASE(StatusDataset)
+{
+ const uint8_t smallBuf[] = {0x81, 0x01, 0x01};
+ const Block smallBlock(smallBuf, sizeof(smallBuf));
+ Block largeBlock;
+ {
+ EncodingBuffer encoder;
+ for (size_t i = 0; i < 2500; ++i) {
+ encoder.prependByte(1);
+ }
+ encoder.prependVarNumber(2500);
+ encoder.prependVarNumber(129);
+ largeBlock = encoder.block();
+ }
+
+ dispatcher.addStatusDataset("test/small",
+ makeTestAuthorization(),
+ [&smallBlock] (const Name& prefix, const Interest& interest,
+ StatusDatasetContext& context) {
+ context.append(smallBlock);
+ context.append(smallBlock);
+ context.append(smallBlock);
+ context.end();
+ });
+
+ dispatcher.addStatusDataset("test/large",
+ makeTestAuthorization(),
+ [&largeBlock] (const Name& prefix, const Interest& interest,
+ StatusDatasetContext& context) {
+ context.append(largeBlock);
+ context.append(largeBlock);
+ context.append(largeBlock);
+ context.end();
+ });
+
+ dispatcher.addStatusDataset("test/reject",
+ makeTestAuthorization(),
+ [] (const Name& prefix, const Interest& interest,
+ StatusDatasetContext& context) {
+ context.reject();
+ });
+
+ dispatcher.addTopPrefix("/root");
+ advanceClocks(1_ms);
+ face.sentData.clear();
+
+ face.receive(*makeInterest("/root/test/small/%80%00")); // returns 403
+ face.receive(*makeInterest("/root/test/small/%80%00/invalid")); // returns 403
+ face.receive(*makeInterest("/root/test/small/%80%00/silent")); // silently ignored
+ advanceClocks(1_ms, 20);
+ BOOST_CHECK_EQUAL(face.sentData.size(), 2);
+
+ BOOST_CHECK_EQUAL(face.sentData[0].getContentType(), tlv::ContentType_Blob);
+ BOOST_CHECK_EQUAL(ControlResponse(face.sentData[0].getContent().blockFromValue()).getCode(), 403);
+ BOOST_CHECK_EQUAL(face.sentData[1].getContentType(), tlv::ContentType_Blob);
+ BOOST_CHECK_EQUAL(ControlResponse(face.sentData[1].getContent().blockFromValue()).getCode(), 403);
+
+ face.sentData.clear();
+
+ auto interestSmall = *makeInterest("/root/test/small/valid", true);
+ face.receive(interestSmall);
+ advanceClocks(1_ms, 10);
+
+ // one data packet is generated and sent to both places
+ BOOST_CHECK_EQUAL(face.sentData.size(), 1);
+ BOOST_CHECK_EQUAL(storage.size(), 1);
+
+ auto fetchedData = storage.find(interestSmall);
+ BOOST_REQUIRE(fetchedData != nullptr);
+ BOOST_CHECK_EQUAL(face.sentData[0].wireEncode(), fetchedData->wireEncode());
+
+ face.receive(*makeInterest(Name("/root/test/small/valid").appendVersion(10))); // should be ignored
+ face.receive(*makeInterest(Name("/root/test/small/valid").appendSegment(20))); // should be ignored
+ advanceClocks(1_ms, 10);
+ BOOST_CHECK_EQUAL(face.sentData.size(), 1);
+ BOOST_CHECK_EQUAL(storage.size(), 1);
+
+ Block content = face.sentData[0].getContent();
+ BOOST_CHECK_NO_THROW(content.parse());
+
+ BOOST_REQUIRE_EQUAL(content.elements().size(), 3);
+ BOOST_CHECK_EQUAL(content.elements()[0], smallBlock);
+ BOOST_CHECK_EQUAL(content.elements()[1], smallBlock);
+ BOOST_CHECK_EQUAL(content.elements()[2], smallBlock);
+
+ storage.erase("/", true); // clear the storage
+ face.sentData.clear();
+ face.receive(*makeInterest("/root/test/large/valid"));
+ advanceClocks(1_ms, 10);
+
+ // two data packets are generated, the first one will be sent to both places
+ // while the second one will only be inserted into the in-memory storage
+ BOOST_CHECK_EQUAL(face.sentData.size(), 1);
+ BOOST_CHECK_EQUAL(storage.size(), 2);
+
+ // segment0 should be sent through the face
+ const auto& component = face.sentData[0].getName().at(-1);
+ BOOST_CHECK(component.isSegment());
+ BOOST_CHECK_EQUAL(component.toSegment(), 0);
+
+ std::vector<Data> dataInStorage;
+ std::copy(storage.begin(), storage.end(), std::back_inserter(dataInStorage));
+
+ // the Data sent through the face should be the same as the first Data in the storage
+ BOOST_CHECK_EQUAL(face.sentData[0].getName(), dataInStorage[0].getName());
+ BOOST_CHECK_EQUAL(face.sentData[0].getContent(), dataInStorage[0].getContent());
+
+ content = [&dataInStorage] () -> Block {
+ EncodingBuffer encoder;
+ size_t valueLength = encoder.prependByteArray(dataInStorage[1].getContent().value(),
+ dataInStorage[1].getContent().value_size());
+ valueLength += encoder.prependByteArray(dataInStorage[0].getContent().value(),
+ dataInStorage[0].getContent().value_size());
+ encoder.prependVarNumber(valueLength);
+ encoder.prependVarNumber(tlv::Content);
+ return encoder.block();
+ }();
+
+ BOOST_CHECK_NO_THROW(content.parse());
+ BOOST_REQUIRE_EQUAL(content.elements().size(), 3);
+ BOOST_CHECK_EQUAL(content.elements()[0], largeBlock);
+ BOOST_CHECK_EQUAL(content.elements()[1], largeBlock);
+ BOOST_CHECK_EQUAL(content.elements()[2], largeBlock);
+
+ storage.erase("/", true);// clear the storage
+ face.sentData.clear();
+ face.receive(*makeInterest("/root/test/reject/%80%00/valid")); // returns nack
+ advanceClocks(1_ms);
+ BOOST_CHECK_EQUAL(face.sentData.size(), 1);
+ BOOST_CHECK_EQUAL(face.sentData[0].getContentType(), tlv::ContentType_Nack);
+ BOOST_CHECK_EQUAL(ControlResponse(face.sentData[0].getContent().blockFromValue()).getCode(), 400);
+ BOOST_CHECK_EQUAL(storage.size(), 0); // the nack packet will not be inserted into the in-memory storage
+}
+
+BOOST_AUTO_TEST_CASE(NotificationStream)
+{
+ const uint8_t buf[] = {0x82, 0x01, 0x02};
+ const Block block(buf, sizeof(buf));
+ auto post = dispatcher.addNotificationStream("test");
+
+ post(block);
+ advanceClocks(1_ms);
+ BOOST_CHECK_EQUAL(face.sentData.size(), 0);
+
+ dispatcher.addTopPrefix("/root");
+ advanceClocks(1_ms);
+ face.sentData.clear();
+
+ post(block);
+ advanceClocks(1_ms);
+ BOOST_CHECK_EQUAL(face.sentData.size(), 1);
+ BOOST_CHECK_EQUAL(storage.size(), 1);
+
+ post(block);
+ post(block);
+ post(block);
+ advanceClocks(1_ms, 10);
+
+ BOOST_REQUIRE_EQUAL(face.sentData.size(), 4);
+ BOOST_CHECK_EQUAL(face.sentData[0].getName(), "/root/test/%FE%00");
+ BOOST_CHECK_EQUAL(face.sentData[1].getName(), "/root/test/%FE%01");
+ BOOST_CHECK_EQUAL(face.sentData[2].getName(), "/root/test/%FE%02");
+ BOOST_CHECK_EQUAL(face.sentData[3].getName(), "/root/test/%FE%03");
+
+ BOOST_CHECK_EQUAL(face.sentData[0].getContent().blockFromValue(), block);
+ BOOST_CHECK_EQUAL(face.sentData[1].getContent().blockFromValue(), block);
+ BOOST_CHECK_EQUAL(face.sentData[2].getContent().blockFromValue(), block);
+ BOOST_CHECK_EQUAL(face.sentData[3].getContent().blockFromValue(), block);
+
+ // each version of notification will be sent to both places
+ std::vector<Data> dataInStorage;
+ std::copy(storage.begin(), storage.end(), std::back_inserter(dataInStorage));
+ BOOST_REQUIRE_EQUAL(dataInStorage.size(), 4);
+ BOOST_CHECK_EQUAL(dataInStorage[0].getName(), "/root/test/%FE%00");
+ BOOST_CHECK_EQUAL(dataInStorage[1].getName(), "/root/test/%FE%01");
+ BOOST_CHECK_EQUAL(dataInStorage[2].getName(), "/root/test/%FE%02");
+ BOOST_CHECK_EQUAL(dataInStorage[3].getName(), "/root/test/%FE%03");
+
+ BOOST_CHECK_EQUAL(dataInStorage[0].getContent().blockFromValue(), block);
+ BOOST_CHECK_EQUAL(dataInStorage[1].getContent().blockFromValue(), block);
+ BOOST_CHECK_EQUAL(dataInStorage[2].getContent().blockFromValue(), block);
+ BOOST_CHECK_EQUAL(dataInStorage[3].getContent().blockFromValue(), block);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestDispatcher
+BOOST_AUTO_TEST_SUITE_END() // Mgmt
+
+} // namespace tests
+} // namespace mgmt
+} // namespace ndn
diff --git a/tests/unit/mgmt/nfd/channel-status.t.cpp b/tests/unit/mgmt/nfd/channel-status.t.cpp
new file mode 100644
index 0000000..b3afde8
--- /dev/null
+++ b/tests/unit/mgmt/nfd/channel-status.t.cpp
@@ -0,0 +1,86 @@
+/* -*- 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 "mgmt/nfd/channel-status.hpp"
+
+#include "boost-test.hpp"
+#include <boost/lexical_cast.hpp>
+
+namespace ndn {
+namespace nfd {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Mgmt)
+BOOST_AUTO_TEST_SUITE(Nfd)
+BOOST_AUTO_TEST_SUITE(TestChannelStatus)
+
+BOOST_AUTO_TEST_CASE(Encode)
+{
+ ChannelStatus status1;
+ status1.setLocalUri("udp4://192.168.2.1");
+ Block wire = status1.wireEncode();
+
+ // These octets are obtained by the snippet below.
+ // This check is intended to detect unexpected encoding change in the future.
+ // for (Buffer::const_iterator it = wire.begin(); it != wire.end(); ++it) {
+ // printf("0x%02x, ", *it);
+ // }
+ static const uint8_t expected[] = {
+ 0x82, 0x14, 0x81, 0x12, 0x75, 0x64, 0x70, 0x34, 0x3a, 0x2f, 0x2f, 0x31, 0x39, 0x32,
+ 0x2e, 0x31, 0x36, 0x38, 0x2e, 0x32, 0x2e, 0x31
+ };
+ BOOST_CHECK_EQUAL_COLLECTIONS(expected, expected + sizeof(expected),
+ wire.begin(), wire.end());
+
+ ChannelStatus status2(wire);
+ BOOST_CHECK_EQUAL(status1, status2);
+}
+
+BOOST_AUTO_TEST_CASE(Equality)
+{
+ ChannelStatus status1, status2;
+
+ status1.setLocalUri("udp4://127.0.0.1:6363");
+ status2 = status1;
+ BOOST_CHECK_EQUAL(status1, status2);
+
+ status2.setLocalUri("dev://eth0");
+ BOOST_CHECK_NE(status1, status2);
+}
+
+BOOST_AUTO_TEST_CASE(Print)
+{
+ ChannelStatus status;
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(status),
+ "Channel(LocalUri: )");
+
+ status.setLocalUri("udp4://127.0.0.1:6363");
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(status),
+ "Channel(LocalUri: udp4://127.0.0.1:6363)");
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestChannelStatus
+BOOST_AUTO_TEST_SUITE_END() // Nfd
+BOOST_AUTO_TEST_SUITE_END() // Mgmt
+
+} // namespace tests
+} // namespace nfd
+} // namespace ndn
diff --git a/tests/unit/mgmt/nfd/command-options.t.cpp b/tests/unit/mgmt/nfd/command-options.t.cpp
new file mode 100644
index 0000000..e8fe76e
--- /dev/null
+++ b/tests/unit/mgmt/nfd/command-options.t.cpp
@@ -0,0 +1,79 @@
+/* -*- 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 "mgmt/nfd/command-options.hpp"
+#include "security/signing-helpers.hpp"
+
+#include "boost-test.hpp"
+
+namespace ndn {
+namespace nfd {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Mgmt)
+BOOST_AUTO_TEST_SUITE(Nfd)
+BOOST_AUTO_TEST_SUITE(TestCommandOptions)
+
+BOOST_AUTO_TEST_CASE(Timeout)
+{
+ CommandOptions co;
+ BOOST_CHECK_EQUAL(co.getTimeout(), CommandOptions::DEFAULT_TIMEOUT);
+
+ co.setTimeout(7414_ms);
+ BOOST_CHECK_EQUAL(co.getTimeout(), 7414_ms);
+
+ BOOST_CHECK_THROW(co.setTimeout(time::milliseconds::zero()), std::out_of_range);
+ BOOST_CHECK_THROW(co.setTimeout(time::milliseconds(-1)), std::out_of_range);
+ BOOST_CHECK_EQUAL(co.getTimeout(), 7414_ms); // unchanged after throw
+
+ co.setTimeout(1_ms);
+ BOOST_CHECK_EQUAL(co.getTimeout(), 1_ms);
+}
+
+BOOST_AUTO_TEST_CASE(Prefix)
+{
+ CommandOptions co;
+ BOOST_CHECK_EQUAL(co.getPrefix(), CommandOptions::DEFAULT_PREFIX);
+
+ co.setPrefix(Name()); // empty Name is okay
+ BOOST_CHECK_EQUAL(co.getPrefix(), Name());
+
+ co.setPrefix("ndn:/localhop/net/example/nfd");
+ BOOST_CHECK_EQUAL(co.getPrefix(), Name("ndn:/localhop/net/example/nfd"));
+}
+
+BOOST_AUTO_TEST_CASE(SigningInfo)
+{
+ CommandOptions co;
+ BOOST_CHECK_EQUAL(co.getSigningInfo().getSignerType(), security::SigningInfo::SIGNER_TYPE_NULL);
+
+ co.setSigningInfo(signingByIdentity("ndn:/tmp/identity"));
+ BOOST_CHECK_EQUAL(co.getSigningInfo().getSignerType(), security::SigningInfo::SIGNER_TYPE_ID);
+ BOOST_CHECK_EQUAL(co.getSigningInfo().getSignerName(), "ndn:/tmp/identity");
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestCommandOptions
+BOOST_AUTO_TEST_SUITE_END() // Nfd
+BOOST_AUTO_TEST_SUITE_END() // Mgmt
+
+} // namespace tests
+} // namespace nfd
+} // namespace ndn
diff --git a/tests/unit/mgmt/nfd/control-command.t.cpp b/tests/unit/mgmt/nfd/control-command.t.cpp
new file mode 100644
index 0000000..4db3752
--- /dev/null
+++ b/tests/unit/mgmt/nfd/control-command.t.cpp
@@ -0,0 +1,479 @@
+/* -*- 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 "mgmt/nfd/control-command.hpp"
+
+#include "boost-test.hpp"
+
+namespace ndn {
+namespace nfd {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Mgmt)
+BOOST_AUTO_TEST_SUITE(Nfd)
+BOOST_AUTO_TEST_SUITE(TestControlCommand)
+
+BOOST_AUTO_TEST_CASE(FaceCreateRequest)
+{
+ FaceCreateCommand command;
+
+ // good with required fields only
+ ControlParameters p1;
+ p1.setUri("tcp4://192.0.2.1:6363");
+ BOOST_CHECK_NO_THROW(command.validateRequest(p1));
+ BOOST_CHECK(Name("/PREFIX/faces/create").isPrefixOf(command.getRequestName("/PREFIX", p1)));
+
+ // good with optional fields
+ ControlParameters p2(p1);
+ p2.setLocalUri("tcp4://192.0.2.2:32114")
+ .setFacePersistency(FACE_PERSISTENCY_PERMANENT)
+ .setBaseCongestionMarkingInterval(100_ms)
+ .setDefaultCongestionThreshold(10000)
+ .setMtu(8192)
+ .setFlags(0x3)
+ .setMask(0x1);
+ BOOST_CHECK_NO_THROW(command.validateRequest(p1));
+
+ // Uri is required
+ ControlParameters p3;
+ BOOST_CHECK_THROW(command.validateRequest(p3), ControlCommand::ArgumentError);
+
+ // Name is forbidden
+ ControlParameters p4(p1);
+ p4.setName("/example");
+ BOOST_CHECK_THROW(command.validateRequest(p4), ControlCommand::ArgumentError);
+
+ // Flags and Mask must be specified together
+ ControlParameters p5(p1);
+ p5.setFlags(0x3);
+ BOOST_CHECK_THROW(command.validateRequest(p5), ControlCommand::ArgumentError);
+
+ ControlParameters p6(p1);
+ p6.setMask(0x1);
+ BOOST_CHECK_THROW(command.validateRequest(p6), ControlCommand::ArgumentError);
+}
+
+BOOST_AUTO_TEST_CASE(FaceCreateResponse)
+{
+ FaceCreateCommand command;
+
+ // good
+ ControlParameters p1;
+ p1.setFaceId(3208)
+ .setUri("tcp4://192.0.2.1:6363")
+ .setLocalUri("tcp4://192.0.2.2:32114")
+ .setFacePersistency(FACE_PERSISTENCY_PERMANENT)
+ .setBaseCongestionMarkingInterval(500_ns)
+ .setDefaultCongestionThreshold(12345)
+ .setMtu(2048)
+ .setFlags(0x3);
+ BOOST_CHECK_NO_THROW(command.validateResponse(p1));
+
+ // Name is forbidden
+ ControlParameters p2(p1);
+ p2.setName("/example");
+ BOOST_CHECK_THROW(command.validateResponse(p2), ControlCommand::ArgumentError);
+
+ // Mask is forbidden
+ ControlParameters p3(p1);
+ p3.setMask(0x1);
+ BOOST_CHECK_THROW(command.validateResponse(p3), ControlCommand::ArgumentError);
+
+ // FaceId must be valid
+ ControlParameters p4(p1);
+ p4.setFaceId(INVALID_FACE_ID);
+ BOOST_CHECK_THROW(command.validateResponse(p4), ControlCommand::ArgumentError);
+
+ // LocalUri is required
+ ControlParameters p5(p1);
+ p5.unsetLocalUri();
+ BOOST_CHECK_THROW(command.validateResponse(p5), ControlCommand::ArgumentError);
+}
+
+BOOST_AUTO_TEST_CASE(FaceUpdate)
+{
+ FaceUpdateCommand command;
+
+ ControlParameters p1;
+ p1.setFaceId(0);
+ BOOST_CHECK_NO_THROW(command.validateRequest(p1));
+ p1.setFaceId(INVALID_FACE_ID);
+ BOOST_CHECK_THROW(command.validateResponse(p1), ControlCommand::ArgumentError);
+
+ p1.setFaceId(1);
+ BOOST_CHECK_NO_THROW(command.validateRequest(p1));
+ BOOST_CHECK_THROW(command.validateResponse(p1), ControlCommand::ArgumentError);
+ command.applyDefaultsToRequest(p1);
+ BOOST_CHECK_EQUAL(p1.getFaceId(), 1);
+
+ ControlParameters p2;
+ p2.setFaceId(1)
+ .setFacePersistency(FACE_PERSISTENCY_PERSISTENT)
+ .setBaseCongestionMarkingInterval(765_ns)
+ .setDefaultCongestionThreshold(54321)
+ .setFlagBit(BIT_LOCAL_FIELDS_ENABLED, false);
+ BOOST_CHECK_NO_THROW(command.validateRequest(p2));
+ BOOST_CHECK_THROW(command.validateResponse(p2), ControlCommand::ArgumentError); // Mask forbidden but present
+
+ // Flags without Mask
+ p2.unsetMask();
+ BOOST_CHECK_THROW(command.validateRequest(p2), ControlCommand::ArgumentError);
+ BOOST_CHECK_NO_THROW(command.validateResponse(p2));
+
+ p2.setFlagBit(BIT_LOCAL_FIELDS_ENABLED, false);
+ p2.unsetFaceId();
+ BOOST_CHECK_NO_THROW(command.validateRequest(p2));
+
+ ControlParameters p3;
+ p3.setFaceId(1)
+ .setName("/ndn/name");
+ BOOST_CHECK_THROW(command.validateRequest(p3), ControlCommand::ArgumentError);
+ BOOST_CHECK_THROW(command.validateResponse(p3), ControlCommand::ArgumentError);
+
+ ControlParameters p4;
+ p4.setFaceId(1)
+ .setUri("tcp4://192.0.2.1");
+ BOOST_CHECK_THROW(command.validateRequest(p4), ControlCommand::ArgumentError);
+ BOOST_CHECK_THROW(command.validateResponse(p4), ControlCommand::ArgumentError);
+
+ ControlParameters p5;
+ BOOST_CHECK_NO_THROW(command.validateRequest(p5));
+ BOOST_CHECK_THROW(command.validateResponse(p5), ControlCommand::ArgumentError);
+ BOOST_CHECK(!p5.hasFaceId());
+ command.applyDefaultsToRequest(p5);
+ BOOST_REQUIRE(p5.hasFaceId());
+ BOOST_CHECK_NO_THROW(command.validateRequest(p5));
+ BOOST_CHECK_THROW(command.validateResponse(p5), ControlCommand::ArgumentError);
+ BOOST_CHECK_EQUAL(p5.getFaceId(), 0);
+
+ ControlParameters p6;
+ p6.setFaceId(1)
+ .setMtu(1024);
+ BOOST_CHECK_THROW(command.validateRequest(p6), ControlCommand::ArgumentError);
+ BOOST_CHECK_THROW(command.validateResponse(p6), ControlCommand::ArgumentError);
+}
+
+BOOST_AUTO_TEST_CASE(FaceDestroy)
+{
+ FaceDestroyCommand command;
+
+ ControlParameters p1;
+ p1.setUri("tcp4://192.0.2.1")
+ .setFaceId(4);
+ BOOST_CHECK_THROW(command.validateRequest(p1), ControlCommand::ArgumentError);
+ BOOST_CHECK_THROW(command.validateResponse(p1), ControlCommand::ArgumentError);
+
+ ControlParameters p2;
+ p2.setFaceId(INVALID_FACE_ID);
+ BOOST_CHECK_THROW(command.validateRequest(p2), ControlCommand::ArgumentError);
+ BOOST_CHECK_THROW(command.validateResponse(p2), ControlCommand::ArgumentError);
+
+ ControlParameters p3;
+ p3.setFaceId(6);
+ BOOST_CHECK_NO_THROW(command.validateRequest(p3));
+ BOOST_CHECK_NO_THROW(command.validateResponse(p3));
+ Name n3;
+ BOOST_CHECK_NO_THROW(n3 = command.getRequestName("/PREFIX", p3));
+ BOOST_CHECK(Name("ndn:/PREFIX/faces/destroy").isPrefixOf(n3));
+}
+
+BOOST_AUTO_TEST_CASE(FibAddNextHop)
+{
+ FibAddNextHopCommand command;
+
+ ControlParameters p1;
+ p1.setName("ndn:/")
+ .setFaceId(22);
+ BOOST_CHECK_NO_THROW(command.validateRequest(p1));
+ BOOST_CHECK_THROW(command.validateResponse(p1), ControlCommand::ArgumentError);
+ Name n1;
+ BOOST_CHECK_NO_THROW(n1 = command.getRequestName("/PREFIX", p1));
+ BOOST_CHECK(Name("ndn:/PREFIX/fib/add-nexthop").isPrefixOf(n1));
+
+ ControlParameters p2;
+ p2.setName("ndn:/example")
+ .setFaceId(0)
+ .setCost(6);
+ BOOST_CHECK_NO_THROW(command.validateRequest(p2));
+ p2.setFaceId(INVALID_FACE_ID);
+ BOOST_CHECK_THROW(command.validateResponse(p2), ControlCommand::ArgumentError);
+
+ command.applyDefaultsToRequest(p1);
+ BOOST_REQUIRE(p1.hasCost());
+ BOOST_CHECK_EQUAL(p1.getCost(), 0);
+
+ p1.unsetFaceId();
+ BOOST_CHECK_NO_THROW(command.validateRequest(p1));
+ command.applyDefaultsToRequest(p1);
+ BOOST_REQUIRE(p1.hasFaceId());
+ BOOST_CHECK_EQUAL(p1.getFaceId(), 0);
+}
+
+BOOST_AUTO_TEST_CASE(FibRemoveNextHop)
+{
+ FibRemoveNextHopCommand command;
+
+ ControlParameters p1;
+ p1.setName("ndn:/")
+ .setFaceId(22);
+ BOOST_CHECK_NO_THROW(command.validateRequest(p1));
+ BOOST_CHECK_NO_THROW(command.validateResponse(p1));
+ Name n1;
+ BOOST_CHECK_NO_THROW(n1 = command.getRequestName("/PREFIX", p1));
+ BOOST_CHECK(Name("ndn:/PREFIX/fib/remove-nexthop").isPrefixOf(n1));
+
+ ControlParameters p2;
+ p2.setName("ndn:/example")
+ .setFaceId(0);
+ BOOST_CHECK_NO_THROW(command.validateRequest(p2));
+ p2.setFaceId(INVALID_FACE_ID);
+ BOOST_CHECK_THROW(command.validateResponse(p2), ControlCommand::ArgumentError);
+
+ p1.unsetFaceId();
+ BOOST_CHECK_NO_THROW(command.validateRequest(p1));
+ command.applyDefaultsToRequest(p1);
+ BOOST_REQUIRE(p1.hasFaceId());
+ BOOST_CHECK_EQUAL(p1.getFaceId(), 0);
+}
+
+BOOST_AUTO_TEST_CASE(CsConfigRequest)
+{
+ CsConfigCommand command;
+
+ // good empty request
+ ControlParameters p1;
+ command.validateRequest(p1);
+ BOOST_CHECK(Name("/PREFIX/cs/config").isPrefixOf(command.getRequestName("/PREFIX", p1)));
+
+ // good full request
+ ControlParameters p2;
+ p2.setCapacity(1574);
+ p2.setFlagBit(BIT_CS_ENABLE_ADMIT, true);
+ p2.setFlagBit(BIT_CS_ENABLE_SERVE, true);
+ command.validateRequest(p2);
+
+ // bad request: Flags but no Mask
+ ControlParameters p3(p2);
+ p3.unsetMask();
+ BOOST_CHECK_THROW(command.validateRequest(p3), ControlCommand::ArgumentError);
+
+ // bad request: Mask but no Flags
+ ControlParameters p4(p2);
+ p4.unsetFlags();
+ BOOST_CHECK_THROW(command.validateRequest(p4), ControlCommand::ArgumentError);
+
+ // bad request: forbidden field
+ ControlParameters p5(p2);
+ p5.setName("/example");
+ BOOST_CHECK_THROW(command.validateRequest(p5), ControlCommand::ArgumentError);
+}
+
+BOOST_AUTO_TEST_CASE(CsConfigResponse)
+{
+ CsConfigCommand command;
+
+ // bad empty response
+ ControlParameters p1;
+ BOOST_CHECK_THROW(command.validateResponse(p1), ControlCommand::ArgumentError);
+
+ // bad response: Mask not allowed
+ ControlParameters p2;
+ p2.setCapacity(1574);
+ p2.setFlagBit(BIT_CS_ENABLE_ADMIT, true);
+ p2.setFlagBit(BIT_CS_ENABLE_SERVE, true);
+ BOOST_CHECK_THROW(command.validateResponse(p2), ControlCommand::ArgumentError);
+
+ // good response
+ ControlParameters p3(p2);
+ p3.unsetMask();
+ command.validateResponse(p3);
+}
+
+BOOST_AUTO_TEST_CASE(CsEraseRequest)
+{
+ CsEraseCommand command;
+
+ // good no-limit request
+ ControlParameters p1;
+ p1.setName("/u4LYPNU8Q");
+ command.validateRequest(p1);
+ BOOST_CHECK(Name("/PREFIX/cs/erase").isPrefixOf(command.getRequestName("/PREFIX", p1)));
+
+ // good limited request
+ ControlParameters p2;
+ p2.setName("/IMw1RaLF");
+ p2.setCount(177);
+ command.validateRequest(p2);
+
+ // bad request: zero entry
+ ControlParameters p3;
+ p3.setName("/ahMID1jcib");
+ p3.setCount(0);
+ BOOST_CHECK_THROW(command.validateRequest(p3), ControlCommand::ArgumentError);
+
+ // bad request: forbidden field
+ ControlParameters p4(p2);
+ p4.setCapacity(278);
+ BOOST_CHECK_THROW(command.validateRequest(p3), ControlCommand::ArgumentError);
+}
+
+BOOST_AUTO_TEST_CASE(CsEraseResponse)
+{
+ CsEraseCommand command;
+
+ // good normal response
+ ControlParameters p1;
+ p1.setName("/TwiIwCdR");
+ p1.setCount(1);
+ command.validateResponse(p1);
+
+ // good limit exceeded request
+ ControlParameters p2;
+ p2.setName("/NMsiy44pr");
+ p2.setCapacity(360);
+ p2.setCount(360);
+ command.validateResponse(p2);
+
+ // good zero-entry response
+ ControlParameters p3;
+ p3.setName("/5f1LRPh1L");
+ p3.setCount(0);
+ command.validateResponse(p3);
+
+ // bad request: missing Count
+ ControlParameters p4(p1);
+ p4.unsetCount();
+ BOOST_CHECK_THROW(command.validateResponse(p4), ControlCommand::ArgumentError);
+
+ // bad request: zero capacity
+ ControlParameters p5(p1);
+ p5.setCapacity(0);
+ BOOST_CHECK_THROW(command.validateResponse(p5), ControlCommand::ArgumentError);
+}
+
+BOOST_AUTO_TEST_CASE(StrategyChoiceSet)
+{
+ StrategyChoiceSetCommand command;
+
+ ControlParameters p1;
+ p1.setName("ndn:/")
+ .setStrategy("ndn:/strategy/P");
+ BOOST_CHECK_NO_THROW(command.validateRequest(p1));
+ BOOST_CHECK_NO_THROW(command.validateResponse(p1));
+ Name n1;
+ BOOST_CHECK_NO_THROW(n1 = command.getRequestName("/PREFIX", p1));
+ BOOST_CHECK(Name("ndn:/PREFIX/strategy-choice/set").isPrefixOf(n1));
+
+ ControlParameters p2;
+ p2.setName("ndn:/example");
+ BOOST_CHECK_THROW(command.validateRequest(p2), ControlCommand::ArgumentError);
+ BOOST_CHECK_THROW(command.validateResponse(p2), ControlCommand::ArgumentError);
+}
+
+BOOST_AUTO_TEST_CASE(StrategyChoiceUnset)
+{
+ StrategyChoiceUnsetCommand command;
+
+ ControlParameters p1;
+ p1.setName("ndn:/example");
+ BOOST_CHECK_NO_THROW(command.validateRequest(p1));
+ BOOST_CHECK_NO_THROW(command.validateResponse(p1));
+ Name n1;
+ BOOST_CHECK_NO_THROW(n1 = command.getRequestName("/PREFIX", p1));
+ BOOST_CHECK(Name("ndn:/PREFIX/strategy-choice/unset").isPrefixOf(n1));
+
+ ControlParameters p2;
+ p2.setName("ndn:/example")
+ .setStrategy("ndn:/strategy/P");
+ BOOST_CHECK_THROW(command.validateRequest(p2), ControlCommand::ArgumentError);
+ BOOST_CHECK_THROW(command.validateResponse(p2), ControlCommand::ArgumentError);
+
+ ControlParameters p3;
+ p3.setName("ndn:/");
+ BOOST_CHECK_THROW(command.validateRequest(p3), ControlCommand::ArgumentError);
+ BOOST_CHECK_THROW(command.validateResponse(p3), ControlCommand::ArgumentError);
+}
+
+BOOST_AUTO_TEST_CASE(RibRegister)
+{
+ RibRegisterCommand command;
+
+ ControlParameters p1;
+ p1.setName("ndn:/");
+ BOOST_CHECK_NO_THROW(command.validateRequest(p1));
+ BOOST_CHECK_THROW(command.validateResponse(p1), ControlCommand::ArgumentError);
+ Name n1;
+ BOOST_CHECK_NO_THROW(n1 = command.getRequestName("/PREFIX", p1));
+ BOOST_CHECK(Name("ndn:/PREFIX/rib/register").isPrefixOf(n1));
+
+ command.applyDefaultsToRequest(p1);
+ BOOST_REQUIRE(p1.hasOrigin());
+ BOOST_CHECK_EQUAL(p1.getOrigin(), ROUTE_ORIGIN_APP);
+ BOOST_REQUIRE(p1.hasCost());
+ BOOST_CHECK_EQUAL(p1.getCost(), 0);
+ BOOST_REQUIRE(p1.hasFlags());
+ BOOST_CHECK_EQUAL(p1.getFlags(), static_cast<uint64_t>(ROUTE_FLAG_CHILD_INHERIT));
+ BOOST_CHECK_EQUAL(p1.hasExpirationPeriod(), false);
+
+ ControlParameters p2;
+ p2.setName("ndn:/example")
+ .setFaceId(2)
+ .setCost(6);
+ BOOST_CHECK_NO_THROW(command.validateRequest(p2));
+ command.applyDefaultsToRequest(p2);
+ BOOST_CHECK_EQUAL(p2.hasExpirationPeriod(), false);
+ BOOST_CHECK_NO_THROW(command.validateResponse(p2));
+}
+
+BOOST_AUTO_TEST_CASE(RibUnregister)
+{
+ RibUnregisterCommand command;
+
+ ControlParameters p1;
+ p1.setName("ndn:/")
+ .setFaceId(22)
+ .setOrigin(ROUTE_ORIGIN_STATIC);
+ BOOST_CHECK_NO_THROW(command.validateRequest(p1));
+ BOOST_CHECK_NO_THROW(command.validateResponse(p1));
+ Name n1;
+ BOOST_CHECK_NO_THROW(n1 = command.getRequestName("/PREFIX", p1));
+ BOOST_CHECK(Name("ndn:/PREFIX/rib/unregister").isPrefixOf(n1));
+
+ ControlParameters p2;
+ p2.setName("ndn:/example")
+ .setFaceId(0)
+ .setOrigin(ROUTE_ORIGIN_APP);
+ BOOST_CHECK_NO_THROW(command.validateRequest(p2));
+ p2.setFaceId(INVALID_FACE_ID);
+ BOOST_CHECK_THROW(command.validateResponse(p2), ControlCommand::ArgumentError);
+
+ p2.unsetFaceId();
+ BOOST_CHECK_NO_THROW(command.validateRequest(p2));
+ BOOST_CHECK_THROW(command.validateResponse(p2), ControlCommand::ArgumentError);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestControlCommand
+BOOST_AUTO_TEST_SUITE_END() // Nfd
+BOOST_AUTO_TEST_SUITE_END() // Mgmt
+
+} // namespace tests
+} // namespace nfd
+} // namespace ndn
diff --git a/tests/unit/mgmt/nfd/control-parameters.t.cpp b/tests/unit/mgmt/nfd/control-parameters.t.cpp
new file mode 100644
index 0000000..6030060
--- /dev/null
+++ b/tests/unit/mgmt/nfd/control-parameters.t.cpp
@@ -0,0 +1,247 @@
+/* -*- 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 "mgmt/nfd/control-parameters.hpp"
+
+#include "boost-test.hpp"
+
+namespace ndn {
+namespace nfd {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Mgmt)
+BOOST_AUTO_TEST_SUITE(Nfd)
+BOOST_AUTO_TEST_SUITE(TestControlParameters)
+
+BOOST_AUTO_TEST_CASE(Fields)
+{
+ ControlParameters input;
+
+ ControlParameters decoded(input.wireEncode());
+ BOOST_CHECK_EQUAL(decoded.hasName(), false);
+ BOOST_CHECK_EQUAL(decoded.hasFaceId(), false);
+ BOOST_CHECK_EQUAL(decoded.hasUri(), false);
+ BOOST_CHECK_EQUAL(decoded.hasLocalUri(), false);
+ BOOST_CHECK_EQUAL(decoded.hasOrigin(), false);
+ BOOST_CHECK_EQUAL(decoded.hasCost(), false);
+ BOOST_CHECK_EQUAL(decoded.hasCapacity(), false);
+ BOOST_CHECK_EQUAL(decoded.hasCount(), false);
+ BOOST_CHECK_EQUAL(decoded.hasFlags(), false);
+ BOOST_CHECK_EQUAL(decoded.hasMask(), false);
+ BOOST_CHECK_EQUAL(decoded.hasStrategy(), false);
+ BOOST_CHECK_EQUAL(decoded.hasExpirationPeriod(), false);
+ BOOST_CHECK_EQUAL(decoded.hasFacePersistency(), false);
+
+ input.setName("/name");
+ input.setFaceId(2634);
+ input.setUri("udp4://192.0.2.1:6363");
+ input.setLocalUri("udp4://192.0.2.2:6363");
+ input.setOrigin(ROUTE_ORIGIN_NLSR);
+ input.setCost(1388);
+ input.setCapacity(2632);
+ input.setCount(3100);
+ input.setFlags(0xAFC4);
+ input.setMask(0xF7A1);
+ input.setStrategy("/strategy-name");
+ input.setExpirationPeriod(1800000_ms);
+ input.setFacePersistency(FacePersistency::FACE_PERSISTENCY_PERSISTENT);
+
+ decoded.wireDecode(input.wireEncode());
+ BOOST_CHECK_EQUAL(decoded.hasName(), true);
+ BOOST_CHECK_EQUAL(decoded.hasFaceId(), true);
+ BOOST_CHECK_EQUAL(decoded.hasUri(), true);
+ BOOST_CHECK_EQUAL(decoded.hasLocalUri(), true);
+ BOOST_CHECK_EQUAL(decoded.hasOrigin(), true);
+ BOOST_CHECK_EQUAL(decoded.hasCost(), true);
+ BOOST_CHECK_EQUAL(decoded.hasCapacity(), true);
+ BOOST_CHECK_EQUAL(decoded.hasCount(), true);
+ BOOST_CHECK_EQUAL(decoded.hasFlags(), true);
+ BOOST_CHECK_EQUAL(decoded.hasMask(), true);
+ BOOST_CHECK_EQUAL(decoded.hasStrategy(), true);
+ BOOST_CHECK_EQUAL(decoded.hasExpirationPeriod(), true);
+ BOOST_CHECK_EQUAL(decoded.hasFacePersistency(), true);
+
+ BOOST_CHECK_EQUAL(decoded.getName(), "/name");
+ BOOST_CHECK_EQUAL(decoded.getFaceId(), 2634);
+ BOOST_CHECK_EQUAL(decoded.getUri(), "udp4://192.0.2.1:6363");
+ BOOST_CHECK_EQUAL(decoded.getLocalUri(), "udp4://192.0.2.2:6363");
+ BOOST_CHECK_EQUAL(decoded.getOrigin(), ROUTE_ORIGIN_NLSR);
+ BOOST_CHECK_EQUAL(decoded.getCost(), 1388);
+ BOOST_CHECK_EQUAL(decoded.getCapacity(), 2632);
+ BOOST_CHECK_EQUAL(decoded.getCount(), 3100);
+ BOOST_CHECK_EQUAL(decoded.getFlags(), 0xAFC4);
+ BOOST_CHECK_EQUAL(decoded.getMask(), 0xF7A1);
+ BOOST_CHECK_EQUAL(decoded.getStrategy(), "/strategy-name");
+ BOOST_CHECK_EQUAL(decoded.getExpirationPeriod(), 1800000_ms);
+ BOOST_CHECK_EQUAL(decoded.getFacePersistency(), FacePersistency::FACE_PERSISTENCY_PERSISTENT);
+
+ input.unsetName();
+ input.unsetFaceId();
+ input.unsetUri();
+ input.unsetLocalUri();
+ input.unsetOrigin();
+ input.unsetCost();
+ input.unsetCapacity();
+ input.unsetCount();
+ input.unsetFlags();
+ input.unsetMask();
+ input.unsetStrategy();
+ input.unsetExpirationPeriod();
+ input.unsetFacePersistency();
+ BOOST_CHECK_EQUAL(input.hasName(), false);
+ BOOST_CHECK_EQUAL(input.hasFaceId(), false);
+ BOOST_CHECK_EQUAL(input.hasUri(), false);
+ BOOST_CHECK_EQUAL(input.hasLocalUri(), false);
+ BOOST_CHECK_EQUAL(input.hasOrigin(), false);
+ BOOST_CHECK_EQUAL(input.hasCost(), false);
+ BOOST_CHECK_EQUAL(input.hasCapacity(), false);
+ BOOST_CHECK_EQUAL(input.hasCount(), false);
+ BOOST_CHECK_EQUAL(input.hasFlags(), false);
+ BOOST_CHECK_EQUAL(input.hasMask(), false);
+ BOOST_CHECK_EQUAL(input.hasStrategy(), false);
+ BOOST_CHECK_EQUAL(input.hasExpirationPeriod(), false);
+ BOOST_CHECK_EQUAL(input.hasFacePersistency(), false);
+}
+
+BOOST_AUTO_TEST_CASE(FlagsAndMask)
+{
+ ControlParameters p;
+
+ BOOST_CHECK(!p.hasFlags());
+ BOOST_CHECK(!p.hasMask());
+ BOOST_CHECK(!p.hasFlagBit(0));
+ BOOST_CHECK(!p.getFlagBit(0));
+
+ // Set bit 2 to true (change Mask)
+ p.setFlagBit(2, true);
+ // 2^2 = 4
+ BOOST_CHECK_EQUAL(p.getFlags(), 4);
+ BOOST_CHECK_EQUAL(p.getMask(), 4);
+ BOOST_CHECK(p.hasFlagBit(2));
+ BOOST_CHECK(p.getFlagBit(2));
+ BOOST_CHECK(!p.hasFlagBit(1));
+ BOOST_CHECK(!p.getFlagBit(1));
+
+ // Set bit 3 to true (no change to Mask)
+ p.setFlagBit(3, true, false);
+ // 2^3 + 2^2 = 12
+ BOOST_CHECK_EQUAL(p.getFlags(), 12);
+ // 2^2 = 4
+ BOOST_CHECK_EQUAL(p.getMask(), 4);
+ BOOST_CHECK(!p.hasFlagBit(3));
+ BOOST_CHECK(p.getFlagBit(3));
+ BOOST_CHECK(p.hasFlagBit(2));
+ BOOST_CHECK(p.getFlagBit(2));
+
+ // Set bit 1 to false (change Mask)
+ p.setFlagBit(1, false);
+ // 2^3 + 2^2 = 12
+ BOOST_CHECK_EQUAL(p.getFlags(), 12);
+ // 2^2 + 2^1 = 6
+ BOOST_CHECK_EQUAL(p.getMask(), 6);
+ BOOST_CHECK(!p.hasFlagBit(3));
+ BOOST_CHECK(p.getFlagBit(3));
+ BOOST_CHECK(p.hasFlagBit(2));
+ BOOST_CHECK(p.getFlagBit(2));
+ BOOST_CHECK(p.hasFlagBit(1));
+ BOOST_CHECK(!p.getFlagBit(1));
+
+ // Set bit 2 to false (change Mask)
+ p.setFlagBit(2, false);
+ // 2^3 = 8
+ BOOST_CHECK_EQUAL(p.getFlags(), 8);
+ // 2^2 + 2^1 = 6
+ BOOST_CHECK_EQUAL(p.getMask(), 6);
+ BOOST_CHECK(!p.hasFlagBit(3));
+ BOOST_CHECK(p.getFlagBit(3));
+ BOOST_CHECK(p.hasFlagBit(2));
+ BOOST_CHECK(!p.getFlagBit(2));
+ BOOST_CHECK(p.hasFlagBit(1));
+ BOOST_CHECK(!p.getFlagBit(1));
+
+ // Set bit 0 to true (change Mask)
+ p.setFlagBit(0, true);
+ // 2^3 + 2^0 = 9
+ BOOST_CHECK_EQUAL(p.getFlags(), 9);
+ // 2^2 + 2^1 + 2^0 = 7
+ BOOST_CHECK_EQUAL(p.getMask(), 7);
+ BOOST_CHECK(p.hasFlagBit(0));
+ BOOST_CHECK(p.getFlagBit(0));
+
+ // Unset bit 0
+ p.unsetFlagBit(0);
+ BOOST_REQUIRE(p.hasFlags());
+ BOOST_REQUIRE(p.hasMask());
+ // 2^3 + 2^0 = 9
+ BOOST_CHECK_EQUAL(p.getFlags(), 9);
+ // 2^2 + 2^1 = 6
+ BOOST_CHECK_EQUAL(p.getMask(), 6);
+ BOOST_CHECK(p.hasFlagBit(1));
+ BOOST_CHECK(!p.hasFlagBit(0));
+ BOOST_CHECK(p.getFlagBit(0));
+
+ // Unset bit 3 (already unset in Mask, so no change)
+ p.unsetFlagBit(3);
+ BOOST_REQUIRE(p.hasFlags());
+ BOOST_REQUIRE(p.hasMask());
+ // 2^3 + 2^0 = 9
+ BOOST_CHECK_EQUAL(p.getFlags(), 9);
+ // 2^2 + 2^1 = 6
+ BOOST_CHECK_EQUAL(p.getMask(), 6);
+ BOOST_CHECK(!p.hasFlagBit(3));
+ BOOST_CHECK(p.getFlagBit(3));
+
+ // Unset bit 2
+ p.unsetFlagBit(2);
+ BOOST_REQUIRE(p.hasFlags());
+ BOOST_REQUIRE(p.hasMask());
+ // 2^3 + 2^0 = 9
+ BOOST_CHECK_EQUAL(p.getFlags(), 9);
+ // 2^1 = 2
+ BOOST_CHECK_EQUAL(p.getMask(), 2);
+ BOOST_CHECK(!p.hasFlagBit(2));
+ BOOST_CHECK(!p.getFlagBit(2));
+
+ // Unset bit 1
+ // Flags and Mask fields will be deleted as Mask is now 0
+ p.unsetFlagBit(1);
+ BOOST_CHECK(!p.hasFlags());
+ BOOST_CHECK(!p.hasMask());
+ BOOST_CHECK(!p.hasFlagBit(3));
+ BOOST_CHECK(!p.getFlagBit(3));
+ BOOST_CHECK(!p.hasFlagBit(2));
+ BOOST_CHECK(!p.getFlagBit(2));
+ BOOST_CHECK(!p.hasFlagBit(1));
+ BOOST_CHECK(!p.getFlagBit(1));
+ BOOST_CHECK(!p.hasFlagBit(0));
+ BOOST_CHECK(!p.getFlagBit(0));
+
+ BOOST_CHECK_THROW(p.setFlagBit(64, true), std::out_of_range);
+ BOOST_CHECK_THROW(p.unsetFlagBit(64), std::out_of_range);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestControlParameters
+BOOST_AUTO_TEST_SUITE_END() // Nfd
+BOOST_AUTO_TEST_SUITE_END() // Mgmt
+
+} // namespace tests
+} // namespace nfd
+} // namespace ndn
diff --git a/tests/unit/mgmt/nfd/controller-fixture.hpp b/tests/unit/mgmt/nfd/controller-fixture.hpp
new file mode 100644
index 0000000..0d14296
--- /dev/null
+++ b/tests/unit/mgmt/nfd/controller-fixture.hpp
@@ -0,0 +1,90 @@
+/* -*- 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.
+ */
+
+#ifndef NDN_TESTS_MGMT_NFD_CONTROLLER_FIXTURE_HPP
+#define NDN_TESTS_MGMT_NFD_CONTROLLER_FIXTURE_HPP
+
+#include "mgmt/nfd/controller.hpp"
+#include "util/dummy-client-face.hpp"
+#include "security/v2/certificate-fetcher-offline.hpp"
+
+#include "boost-test.hpp"
+#include "dummy-validator.hpp"
+#include "../../identity-management-time-fixture.hpp"
+
+namespace ndn {
+namespace nfd {
+namespace tests {
+
+using namespace ndn::tests;
+
+class ControllerFixture : public IdentityManagementTimeFixture
+{
+protected:
+ ControllerFixture()
+ : face(io, m_keyChain)
+ , m_validator(true)
+ , controller(face, m_keyChain, m_validator)
+ , commandFailCallback(bind(&ControllerFixture::recordCommandFail, this, _1))
+ , datasetFailCallback(bind(&ControllerFixture::recordDatasetFail, this, _1, _2))
+ {
+ Name identityName("/localhost/ControllerFixture");
+ m_keyChain.setDefaultIdentity(this->addIdentity(identityName));
+ }
+
+ /** \brief controls whether Controller's validator should accept or reject validation requests
+ *
+ * Initially, the validator accepts all requests.
+ * Setting \p false causes validator to reject all requests.
+ */
+ void
+ setValidationResult(bool shouldAccept)
+ {
+ m_validator.getPolicy().setResult(shouldAccept);
+ }
+
+private:
+ void
+ recordCommandFail(const ControlResponse& response)
+ {
+ failCodes.push_back(response.getCode());
+ }
+
+ void
+ recordDatasetFail(uint32_t code, const std::string& reason)
+ {
+ failCodes.push_back(code);
+ }
+
+protected:
+ ndn::util::DummyClientFace face;
+ DummyValidator m_validator;
+ Controller controller;
+ Controller::CommandFailCallback commandFailCallback;
+ Controller::DatasetFailCallback datasetFailCallback;
+ std::vector<uint32_t> failCodes;
+};
+
+} // namespace tests
+} // namespace nfd
+} // namespace ndn
+
+#endif // NDN_TESTS_MGMT_NFD_CONTROLLER_FIXTURE_HPP
diff --git a/tests/unit/mgmt/nfd/controller.t.cpp b/tests/unit/mgmt/nfd/controller.t.cpp
new file mode 100644
index 0000000..e9a3c4f
--- /dev/null
+++ b/tests/unit/mgmt/nfd/controller.t.cpp
@@ -0,0 +1,273 @@
+/* -*- 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 "mgmt/nfd/controller.hpp"
+#include "mgmt/nfd/control-response.hpp"
+
+#include "controller-fixture.hpp"
+#include "make-interest-data.hpp"
+
+namespace ndn {
+namespace nfd {
+namespace tests {
+
+using namespace ndn::tests;
+
+BOOST_AUTO_TEST_SUITE(Mgmt)
+BOOST_AUTO_TEST_SUITE(Nfd)
+
+class CommandFixture : public ControllerFixture
+{
+protected:
+ CommandFixture()
+ : succeedCallback(bind(&CommandFixture::succeed, this, _1))
+ {
+ }
+
+ void
+ respond(const ControlResponse& responsePayload)
+ {
+ auto responseData = makeData(face.sentInterests.at(0).getName());
+ responseData->setContent(responsePayload.wireEncode());
+ face.receive(*responseData);
+ this->advanceClocks(1_ms);
+ }
+
+private:
+ void
+ succeed(const ControlParameters& parameters)
+ {
+ succeeds.push_back(parameters);
+ }
+
+protected:
+ Controller::CommandSucceedCallback succeedCallback;
+ std::vector<ControlParameters> succeeds;
+};
+
+// This test suite focuses on ControlCommand functionality of Controller.
+// Individual commands are tested in nfd-control-command.t.cpp
+// StatusDataset functionality is tested in nfd-status-dataset.t.cpp
+BOOST_FIXTURE_TEST_SUITE(TestController, CommandFixture)
+
+static ControlParameters
+makeFaceCreateResponse()
+{
+ ControlParameters resp;
+ resp.setFaceId(22)
+ .setUri("tcp4://192.0.2.1:6363")
+ .setLocalUri("tcp4://192.0.2.2:10847")
+ .setFacePersistency(ndn::nfd::FacePersistency::FACE_PERSISTENCY_PERSISTENT)
+ .setFlags(0x7);
+ return resp;
+}
+
+BOOST_AUTO_TEST_CASE(Success)
+{
+ ControlParameters parameters;
+ parameters.setUri("tcp4://192.0.2.1:6363");
+
+ controller.start<FaceCreateCommand>(parameters, succeedCallback, commandFailCallback);
+ this->advanceClocks(1_ms);
+
+ BOOST_REQUIRE_EQUAL(face.sentInterests.size(), 1);
+ const Interest& requestInterest = face.sentInterests[0];
+
+ FaceCreateCommand command;
+ BOOST_CHECK(Name("/localhost/nfd/faces/create").isPrefixOf(requestInterest.getName()));
+ // 9 components: ndn:/localhost/nfd/faces/create/<parameters>/<signed Interest x4>
+ BOOST_REQUIRE_EQUAL(requestInterest.getName().size(), 9);
+ ControlParameters request;
+ // 4th component: <parameters>
+ BOOST_REQUIRE_NO_THROW(request.wireDecode(requestInterest.getName().at(4).blockFromValue()));
+ BOOST_CHECK_NO_THROW(command.validateRequest(request));
+ BOOST_CHECK_EQUAL(request.getUri(), parameters.getUri());
+ BOOST_CHECK_EQUAL(requestInterest.getInterestLifetime(), CommandOptions::DEFAULT_TIMEOUT);
+
+ ControlParameters responseBody = makeFaceCreateResponse();
+ ControlResponse responsePayload(201, "created");
+ responsePayload.setBody(responseBody.wireEncode());
+ this->respond(responsePayload);
+
+ BOOST_CHECK_EQUAL(failCodes.size(), 0);
+ BOOST_REQUIRE_EQUAL(succeeds.size(), 1);
+ BOOST_CHECK_EQUAL(succeeds.back().getUri(), responseBody.getUri());
+ BOOST_CHECK_EQUAL(succeeds.back().getFaceId(), responseBody.getFaceId());
+}
+
+BOOST_AUTO_TEST_CASE(SuccessNoCallback)
+{
+ ControlParameters parameters;
+ parameters.setUri("tcp4://192.0.2.1:6363");
+
+ controller.start<FaceCreateCommand>(parameters, nullptr, commandFailCallback);
+ this->advanceClocks(1_ms);
+
+ ControlParameters responseBody = makeFaceCreateResponse();
+ ControlResponse responsePayload(201, "created");
+ responsePayload.setBody(responseBody.wireEncode());
+ this->respond(responsePayload);
+
+ BOOST_CHECK_EQUAL(failCodes.size(), 0);
+}
+
+BOOST_AUTO_TEST_CASE(OptionsPrefix)
+{
+ ControlParameters parameters;
+ parameters.setName("/ndn/com/example");
+ parameters.setFaceId(400);
+
+ CommandOptions options;
+ options.setPrefix("/localhop/net/example/router1/nfd");
+
+ controller.start<RibRegisterCommand>(parameters, succeedCallback, commandFailCallback, options);
+ this->advanceClocks(1_ms);
+
+ BOOST_REQUIRE_EQUAL(face.sentInterests.size(), 1);
+ const Interest& requestInterest = face.sentInterests[0];
+
+ FaceCreateCommand command;
+ BOOST_CHECK(Name("/localhop/net/example/router1/nfd/rib/register").isPrefixOf(requestInterest.getName()));
+}
+
+BOOST_AUTO_TEST_CASE(InvalidRequest)
+{
+ ControlParameters parameters;
+ parameters.setName("ndn:/should-not-have-this-field");
+ // Uri is missing
+
+ BOOST_CHECK_THROW(controller.start<FaceCreateCommand>(
+ parameters, succeedCallback, commandFailCallback),
+ ControlCommand::ArgumentError);
+}
+
+BOOST_AUTO_TEST_CASE(ValidationFailure)
+{
+ this->setValidationResult(false);
+
+ ControlParameters parameters;
+ parameters.setUri("tcp4://192.0.2.1:6363");
+
+ controller.start<FaceCreateCommand>(parameters, succeedCallback, commandFailCallback);
+ this->advanceClocks(1_ms);
+
+ ControlParameters responseBody = makeFaceCreateResponse();
+ ControlResponse responsePayload(201, "created");
+ responsePayload.setBody(responseBody.wireEncode());
+ this->respond(responsePayload);
+
+ BOOST_CHECK_EQUAL(succeeds.size(), 0);
+ BOOST_REQUIRE_EQUAL(failCodes.size(), 1);
+ BOOST_CHECK_EQUAL(failCodes.back(), Controller::ERROR_VALIDATION);
+}
+
+BOOST_AUTO_TEST_CASE(ErrorCode)
+{
+ ControlParameters parameters;
+ parameters.setUri("tcp4://192.0.2.1:6363");
+
+ controller.start<FaceCreateCommand>(parameters, succeedCallback, commandFailCallback);
+ this->advanceClocks(1_ms);
+
+ ControlResponse responsePayload(401, "Not Authenticated");
+ this->respond(responsePayload);
+
+ BOOST_CHECK_EQUAL(succeeds.size(), 0);
+ BOOST_REQUIRE_EQUAL(failCodes.size(), 1);
+ BOOST_CHECK_EQUAL(failCodes.back(), 401);
+}
+
+BOOST_AUTO_TEST_CASE(InvalidResponse)
+{
+ ControlParameters parameters;
+ parameters.setUri("tcp4://192.0.2.1:6363");
+
+ controller.start<FaceCreateCommand>(parameters, succeedCallback, commandFailCallback);
+ this->advanceClocks(1_ms);
+
+ ControlParameters responseBody = makeFaceCreateResponse();
+ responseBody.unsetFaceId() // FaceId is missing
+ .setName("ndn:/should-not-have-this-field");
+ ControlResponse responsePayload(201, "created");
+ responsePayload.setBody(responseBody.wireEncode());
+ this->respond(responsePayload);
+
+ BOOST_CHECK_EQUAL(succeeds.size(), 0);
+ BOOST_REQUIRE_EQUAL(failCodes.size(), 1);
+}
+
+BOOST_AUTO_TEST_CASE(Nack)
+{
+ ControlParameters parameters;
+ parameters.setUri("tcp4://192.0.2.1:6363");
+
+ controller.start<FaceCreateCommand>(parameters, succeedCallback, commandFailCallback);
+ this->advanceClocks(1_ms);
+
+ BOOST_REQUIRE_EQUAL(face.sentInterests.size(), 1);
+ const Interest& requestInterest = face.sentInterests[0];
+
+ auto responseNack = makeNack(requestInterest, lp::NackReason::NO_ROUTE);
+ face.receive(responseNack);
+ this->advanceClocks(1_ms);
+
+ BOOST_REQUIRE_EQUAL(failCodes.size(), 1);
+ BOOST_CHECK_EQUAL(failCodes.back(), Controller::ERROR_NACK);
+}
+
+BOOST_AUTO_TEST_CASE(Timeout)
+{
+ ControlParameters parameters;
+ parameters.setUri("tcp4://192.0.2.1:6363");
+
+ CommandOptions options;
+ options.setTimeout(50_ms);
+
+ controller.start<FaceCreateCommand>(parameters, succeedCallback, commandFailCallback, options);
+ this->advanceClocks(1_ms); // express Interest
+ this->advanceClocks(51_ms); // timeout
+
+ BOOST_REQUIRE_EQUAL(failCodes.size(), 1);
+ BOOST_CHECK_EQUAL(failCodes.back(), Controller::ERROR_TIMEOUT);
+}
+
+BOOST_AUTO_TEST_CASE(FailureNoCallback)
+{
+ ControlParameters parameters;
+ parameters.setUri("tcp4://192.0.2.1:6363");
+
+ CommandOptions options;
+ options.setTimeout(50_ms);
+
+ controller.start<FaceCreateCommand>(parameters, succeedCallback, nullptr, options);
+ this->advanceClocks(1_ms); // express Interest
+ this->advanceClocks(51_ms); // timeout
+
+ BOOST_CHECK_EQUAL(succeeds.size(), 0);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestController
+BOOST_AUTO_TEST_SUITE_END() // Nfd
+BOOST_AUTO_TEST_SUITE_END() // Mgmt
+
+} // namespace tests
+} // namespace nfd
+} // namespace ndn
diff --git a/tests/unit/mgmt/nfd/cs-info.t.cpp b/tests/unit/mgmt/nfd/cs-info.t.cpp
new file mode 100644
index 0000000..46a0ed3
--- /dev/null
+++ b/tests/unit/mgmt/nfd/cs-info.t.cpp
@@ -0,0 +1,127 @@
+/* -*- 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 "mgmt/nfd/cs-info.hpp"
+
+#include "boost-test.hpp"
+#include <boost/lexical_cast.hpp>
+
+namespace ndn {
+namespace nfd {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Mgmt)
+BOOST_AUTO_TEST_SUITE(Nfd)
+BOOST_AUTO_TEST_SUITE(TestCsInfo)
+
+static CsInfo
+makeCsInfo()
+{
+ return CsInfo()
+ .setCapacity(20177)
+ .setEnableAdmit(false)
+ .setEnableServe(true)
+ .setNEntries(5509)
+ .setNHits(12951)
+ .setNMisses(28179);
+}
+
+BOOST_AUTO_TEST_CASE(Encode)
+{
+ CsInfo csi1 = makeCsInfo();
+ Block wire = csi1.wireEncode();
+
+ static const uint8_t EXPECTED[] = {
+ 0x80, 0x13, // CsInfo
+ 0x83, 0x02, 0x4E, 0xD1, // Capacity
+ 0x6C, 0x01, 0x02, // Flags
+ 0x87, 0x02, 0x15, 0x85, // NCsEntries
+ 0x81, 0x02, 0x32, 0x97, // NHits
+ 0x82, 0x02, 0x6E, 0x13, // NMisses
+ };
+ BOOST_CHECK_EQUAL_COLLECTIONS(wire.begin(), wire.end(), EXPECTED, EXPECTED + sizeof(EXPECTED));
+
+ CsInfo csi2(wire);
+ BOOST_CHECK_EQUAL(csi2.getCapacity(), 20177);
+ BOOST_CHECK_EQUAL(csi2.getEnableAdmit(), false);
+ BOOST_CHECK_EQUAL(csi2.getEnableServe(), true);
+ BOOST_CHECK_EQUAL(csi2.getNEntries(), 5509);
+ BOOST_CHECK_EQUAL(csi2.getNHits(), 12951);
+ BOOST_CHECK_EQUAL(csi2.getNMisses(), 28179);
+}
+
+BOOST_AUTO_TEST_CASE(Equality)
+{
+ CsInfo csi1, csi2;
+ BOOST_CHECK_EQUAL(csi1, csi2);
+
+ csi1 = makeCsInfo();
+ BOOST_CHECK_NE(csi1, csi2);
+ csi2 = csi1;
+ BOOST_CHECK_EQUAL(csi1, csi2);
+
+ csi2.setCapacity(csi2.getCapacity() + 1);
+ BOOST_CHECK_NE(csi1, csi2);
+ csi2 = csi1;
+
+ csi2.setEnableAdmit(!csi2.getEnableAdmit());
+ BOOST_CHECK_NE(csi1, csi2);
+ csi2 = csi1;
+
+ csi2.setEnableServe(!csi2.getEnableServe());
+ BOOST_CHECK_NE(csi1, csi2);
+ csi2 = csi1;
+
+ csi2.setNEntries(csi2.getNEntries() + 1);
+ BOOST_CHECK_NE(csi1, csi2);
+ csi2 = csi1;
+
+ csi2.setNHits(csi2.getNHits() + 1);
+ BOOST_CHECK_NE(csi1, csi2);
+ csi2 = csi1;
+
+ csi2.setNMisses(csi2.getNMisses() + 1);
+ BOOST_CHECK_NE(csi1, csi2);
+ csi2 = csi1;
+}
+
+BOOST_AUTO_TEST_CASE(Print)
+{
+ CsInfo csi;
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(csi),
+ "CS: 0 entries, 0 max, admit disabled, serve disabled, 0 hits, 0 misses");
+
+ csi = makeCsInfo();
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(csi),
+ "CS: 5509 entries, 20177 max, admit disabled, serve enabled, 12951 hits, 28179 misses");
+
+ csi.setEnableAdmit(true).setNHits(1).setNMisses(1);
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(csi),
+ "CS: 5509 entries, 20177 max, admit enabled, serve enabled, 1 hit, 1 miss");
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestCsInfo
+BOOST_AUTO_TEST_SUITE_END() // Nfd
+BOOST_AUTO_TEST_SUITE_END() // Mgmt
+
+} // namespace tests
+} // namespace nfd
+} // namespace ndn
diff --git a/tests/unit/mgmt/nfd/face-event-notification.t.cpp b/tests/unit/mgmt/nfd/face-event-notification.t.cpp
new file mode 100644
index 0000000..4da762b
--- /dev/null
+++ b/tests/unit/mgmt/nfd/face-event-notification.t.cpp
@@ -0,0 +1,269 @@
+/* -*- 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 "mgmt/nfd/face-event-notification.hpp"
+
+#include "boost-test.hpp"
+#include <boost/lexical_cast.hpp>
+
+namespace ndn {
+namespace nfd {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Mgmt)
+BOOST_AUTO_TEST_SUITE(Nfd)
+BOOST_AUTO_TEST_SUITE(TestFaceEventNotification)
+
+BOOST_AUTO_TEST_CASE(Traits)
+{
+ FaceEventNotification notification;
+ BOOST_CHECK_EQUAL(notification.getFaceScope(), FACE_SCOPE_NON_LOCAL);
+ BOOST_CHECK_EQUAL(notification.getFacePersistency(), FACE_PERSISTENCY_PERSISTENT);
+ BOOST_CHECK_EQUAL(notification.getLinkType(), LINK_TYPE_POINT_TO_POINT);
+
+ notification.setFaceScope(FACE_SCOPE_LOCAL);
+ BOOST_CHECK_EQUAL(notification.getFaceScope(), FACE_SCOPE_LOCAL);
+ BOOST_CHECK_EQUAL(notification.getFacePersistency(), FACE_PERSISTENCY_PERSISTENT);
+ BOOST_CHECK_EQUAL(notification.getLinkType(), LINK_TYPE_POINT_TO_POINT);
+
+ notification.setFacePersistency(FACE_PERSISTENCY_ON_DEMAND);
+ BOOST_CHECK_EQUAL(notification.getFaceScope(), FACE_SCOPE_LOCAL);
+ BOOST_CHECK_EQUAL(notification.getFacePersistency(), FACE_PERSISTENCY_ON_DEMAND);
+ BOOST_CHECK_EQUAL(notification.getLinkType(), LINK_TYPE_POINT_TO_POINT);
+
+ notification.setFacePersistency(FACE_PERSISTENCY_PERMANENT);
+ BOOST_CHECK_EQUAL(notification.getFaceScope(), FACE_SCOPE_LOCAL);
+ BOOST_CHECK_EQUAL(notification.getFacePersistency(), FACE_PERSISTENCY_PERMANENT);
+ BOOST_CHECK_EQUAL(notification.getLinkType(), LINK_TYPE_POINT_TO_POINT);
+
+ notification.setLinkType(LINK_TYPE_MULTI_ACCESS);
+ BOOST_CHECK_EQUAL(notification.getFaceScope(), FACE_SCOPE_LOCAL);
+ BOOST_CHECK_EQUAL(notification.getFacePersistency(), FACE_PERSISTENCY_PERMANENT);
+ BOOST_CHECK_EQUAL(notification.getLinkType(), LINK_TYPE_MULTI_ACCESS);
+}
+
+BOOST_AUTO_TEST_CASE(Created)
+{
+ FaceEventNotification notification1;
+ notification1.setKind(FACE_EVENT_CREATED)
+ .setFaceId(20)
+ .setRemoteUri("tcp4://192.0.2.1:55555")
+ .setLocalUri("tcp4://192.0.2.2:6363")
+ .setFaceScope(FACE_SCOPE_LOCAL)
+ .setFacePersistency(FACE_PERSISTENCY_ON_DEMAND)
+ .setLinkType(LINK_TYPE_MULTI_ACCESS)
+ .setFlags(0x3);
+ Block wire = notification1.wireEncode();
+
+ // These octets are obtained by the snippet below.
+ // This check is intended to detect unexpected encoding change in the future.
+ // for (Buffer::const_iterator it = wire.begin(); it != wire.end(); ++it) {
+ // printf("0x%02x, ", *it);
+ // }
+ static const uint8_t expected[] = {
+ 0xc0, 0x41, 0xc1, 0x01, 0x01, 0x69, 0x01, 0x14, 0x72, 0x16,
+ 0x74, 0x63, 0x70, 0x34, 0x3a, 0x2f, 0x2f, 0x31, 0x39, 0x32,
+ 0x2e, 0x30, 0x2e, 0x32, 0x2e, 0x31, 0x3a, 0x35, 0x35, 0x35,
+ 0x35, 0x35, 0x81, 0x15, 0x74, 0x63, 0x70, 0x34, 0x3a, 0x2f,
+ 0x2f, 0x31, 0x39, 0x32, 0x2e, 0x30, 0x2e, 0x32, 0x2e, 0x32,
+ 0x3a, 0x36, 0x33, 0x36, 0x33, 0x84, 0x01, 0x01, 0x85, 0x01,
+ 0x01, 0x86, 0x01, 0x01, 0x6c, 0x01, 0x03,
+ };
+ BOOST_CHECK_EQUAL_COLLECTIONS(expected, expected + sizeof(expected),
+ wire.begin(), wire.end());
+
+ FaceEventNotification notification2(wire);
+ BOOST_CHECK_EQUAL(notification1, notification2);
+
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(notification2),
+ "FaceEvent(Kind: created,\n"
+ " FaceId: 20,\n"
+ " RemoteUri: tcp4://192.0.2.1:55555,\n"
+ " LocalUri: tcp4://192.0.2.2:6363,\n"
+ " FaceScope: local,\n"
+ " FacePersistency: on-demand,\n"
+ " LinkType: multi-access,\n"
+ " Flags: 0x3\n"
+ " )");
+}
+
+BOOST_AUTO_TEST_CASE(Destroyed)
+{
+ FaceEventNotification notification1;
+ notification1.setKind(FACE_EVENT_DESTROYED)
+ .setFaceId(20)
+ .setRemoteUri("tcp4://192.0.2.1:55555")
+ .setLocalUri("tcp4://192.0.2.2:6363")
+ .setFaceScope(FACE_SCOPE_LOCAL)
+ .setFacePersistency(FACE_PERSISTENCY_ON_DEMAND)
+ .setLinkType(LINK_TYPE_MULTI_ACCESS)
+ .setFlags(0x4);
+ Block wire = notification1.wireEncode();
+
+ // These octets are obtained by the snippet below.
+ // This check is intended to detect unexpected encoding change in the future.
+ // for (Buffer::const_iterator it = wire.begin(); it != wire.end(); ++it) {
+ // printf("0x%02x, ", *it);
+ // }
+ static const uint8_t expected[] = {
+ 0xc0, 0x41, 0xc1, 0x01, 0x02, 0x69, 0x01, 0x14, 0x72, 0x16,
+ 0x74, 0x63, 0x70, 0x34, 0x3a, 0x2f, 0x2f, 0x31, 0x39, 0x32,
+ 0x2e, 0x30, 0x2e, 0x32, 0x2e, 0x31, 0x3a, 0x35, 0x35, 0x35,
+ 0x35, 0x35, 0x81, 0x15, 0x74, 0x63, 0x70, 0x34, 0x3a, 0x2f,
+ 0x2f, 0x31, 0x39, 0x32, 0x2e, 0x30, 0x2e, 0x32, 0x2e, 0x32,
+ 0x3a, 0x36, 0x33, 0x36, 0x33, 0x84, 0x01, 0x01, 0x85, 0x01,
+ 0x01, 0x86, 0x01, 0x01, 0x6c, 0x01, 0x04,
+ };
+ BOOST_CHECK_EQUAL_COLLECTIONS(expected, expected + sizeof(expected),
+ wire.begin(), wire.end());
+
+ FaceEventNotification notification2(wire);
+ BOOST_CHECK_EQUAL(notification1, notification2);
+
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(notification2),
+ "FaceEvent(Kind: destroyed,\n"
+ " FaceId: 20,\n"
+ " RemoteUri: tcp4://192.0.2.1:55555,\n"
+ " LocalUri: tcp4://192.0.2.2:6363,\n"
+ " FaceScope: local,\n"
+ " FacePersistency: on-demand,\n"
+ " LinkType: multi-access,\n"
+ " Flags: 0x4\n"
+ " )");
+}
+
+BOOST_AUTO_TEST_CASE(Up)
+{
+ FaceEventNotification notification1;
+ notification1.setKind(FACE_EVENT_UP)
+ .setFaceId(20)
+ .setRemoteUri("tcp4://192.0.2.1:55555")
+ .setLocalUri("tcp4://192.0.2.2:6363")
+ .setFaceScope(FACE_SCOPE_LOCAL)
+ .setFacePersistency(FACE_PERSISTENCY_ON_DEMAND)
+ .setLinkType(LINK_TYPE_MULTI_ACCESS)
+ .setFlags(0x05);
+ Block wire = notification1.wireEncode();
+
+ // These octets are obtained by the snippet below.
+ // This check is intended to detect unexpected encoding change in the future.
+ // for (Buffer::const_iterator it = wire.begin(); it != wire.end(); ++it) {
+ // printf("0x%02x, ", *it);
+ // }
+ static const uint8_t expected[] = {
+ 0xc0, 0x41, 0xc1, 0x01, 0x03, 0x69, 0x01, 0x14, 0x72, 0x16,
+ 0x74, 0x63, 0x70, 0x34, 0x3a, 0x2f, 0x2f, 0x31, 0x39, 0x32,
+ 0x2e, 0x30, 0x2e, 0x32, 0x2e, 0x31, 0x3a, 0x35, 0x35, 0x35,
+ 0x35, 0x35, 0x81, 0x15, 0x74, 0x63, 0x70, 0x34, 0x3a, 0x2f,
+ 0x2f, 0x31, 0x39, 0x32, 0x2e, 0x30, 0x2e, 0x32, 0x2e, 0x32,
+ 0x3a, 0x36, 0x33, 0x36, 0x33, 0x84, 0x01, 0x01, 0x85, 0x01,
+ 0x01, 0x86, 0x01, 0x01, 0x6c, 0x01, 0x05,
+ };
+ BOOST_CHECK_EQUAL_COLLECTIONS(expected, expected + sizeof(expected),
+ wire.begin(), wire.end());
+
+ FaceEventNotification notification2(wire);
+ BOOST_CHECK_EQUAL(notification1, notification2);
+
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(notification2),
+ "FaceEvent(Kind: up,\n"
+ " FaceId: 20,\n"
+ " RemoteUri: tcp4://192.0.2.1:55555,\n"
+ " LocalUri: tcp4://192.0.2.2:6363,\n"
+ " FaceScope: local,\n"
+ " FacePersistency: on-demand,\n"
+ " LinkType: multi-access,\n"
+ " Flags: 0x5\n"
+ " )");
+}
+
+BOOST_AUTO_TEST_CASE(Down)
+{
+ FaceEventNotification notification1;
+ notification1.setKind(FACE_EVENT_DOWN)
+ .setFaceId(20)
+ .setRemoteUri("tcp4://192.0.2.1:55555")
+ .setLocalUri("tcp4://192.0.2.2:6363")
+ .setFaceScope(FACE_SCOPE_LOCAL)
+ .setFacePersistency(FACE_PERSISTENCY_ON_DEMAND)
+ .setLinkType(LINK_TYPE_MULTI_ACCESS)
+ .setFlags(0x06);
+ Block wire = notification1.wireEncode();
+
+ // These octets are obtained by the snippet below.
+ // This check is intended to detect unexpected encoding change in the future.
+ // for (Buffer::const_iterator it = wire.begin(); it != wire.end(); ++it) {
+ // printf("0x%02x, ", *it);
+ // }
+ static const uint8_t expected[] = {
+ 0xc0, 0x41, 0xc1, 0x01, 0x04, 0x69, 0x01, 0x14, 0x72, 0x16,
+ 0x74, 0x63, 0x70, 0x34, 0x3a, 0x2f, 0x2f, 0x31, 0x39, 0x32,
+ 0x2e, 0x30, 0x2e, 0x32, 0x2e, 0x31, 0x3a, 0x35, 0x35, 0x35,
+ 0x35, 0x35, 0x81, 0x15, 0x74, 0x63, 0x70, 0x34, 0x3a, 0x2f,
+ 0x2f, 0x31, 0x39, 0x32, 0x2e, 0x30, 0x2e, 0x32, 0x2e, 0x32,
+ 0x3a, 0x36, 0x33, 0x36, 0x33, 0x84, 0x01, 0x01, 0x85, 0x01,
+ 0x01, 0x86, 0x01, 0x01, 0x6c, 0x01, 0x06,
+ };
+ BOOST_CHECK_EQUAL_COLLECTIONS(expected, expected + sizeof(expected),
+ wire.begin(), wire.end());
+
+ FaceEventNotification notification2(wire);
+ BOOST_CHECK_EQUAL(notification1, notification2);
+
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(notification2),
+ "FaceEvent(Kind: down,\n"
+ " FaceId: 20,\n"
+ " RemoteUri: tcp4://192.0.2.1:55555,\n"
+ " LocalUri: tcp4://192.0.2.2:6363,\n"
+ " FaceScope: local,\n"
+ " FacePersistency: on-demand,\n"
+ " LinkType: multi-access,\n"
+ " Flags: 0x6\n"
+ " )");
+}
+
+BOOST_AUTO_TEST_CASE(Equality)
+{
+ FaceEventNotification notification1, notification2;
+ BOOST_CHECK_EQUAL(notification1, notification2);
+
+ notification1.setKind(FACE_EVENT_CREATED)
+ .setFaceId(123)
+ .setRemoteUri("tcp4://192.0.2.1:55555")
+ .setLocalUri("tcp4://192.0.2.2:6363");
+ notification2 = notification1;
+ BOOST_CHECK_EQUAL(notification1, notification2);
+
+ notification2.setFaceId(42);
+ BOOST_CHECK_NE(notification1, notification2);
+
+ notification2 = notification1;
+ notification2.setKind(FACE_EVENT_DESTROYED);
+ BOOST_CHECK_NE(notification1, notification2);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestFaceEventNotification
+BOOST_AUTO_TEST_SUITE_END() // Nfd
+BOOST_AUTO_TEST_SUITE_END() // Mgmt
+
+} // namespace tests
+} // namespace nfd
+} // namespace ndn
diff --git a/tests/unit/mgmt/nfd/face-query-filter.t.cpp b/tests/unit/mgmt/nfd/face-query-filter.t.cpp
new file mode 100644
index 0000000..3b9b2a2
--- /dev/null
+++ b/tests/unit/mgmt/nfd/face-query-filter.t.cpp
@@ -0,0 +1,143 @@
+/* -*- 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 "mgmt/nfd/face-query-filter.hpp"
+
+#include "boost-test.hpp"
+#include <boost/lexical_cast.hpp>
+
+namespace ndn {
+namespace nfd {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Mgmt)
+BOOST_AUTO_TEST_SUITE(Nfd)
+BOOST_AUTO_TEST_SUITE(TestFaceQueryFilter)
+
+BOOST_AUTO_TEST_CASE(Encode)
+{
+ FaceQueryFilter filter1;
+ BOOST_CHECK_EQUAL(filter1.hasFaceId(), false);
+ BOOST_CHECK_EQUAL(filter1.hasUriScheme(), false);
+ BOOST_CHECK_EQUAL(filter1.hasRemoteUri(), false);
+ BOOST_CHECK_EQUAL(filter1.hasLocalUri(), false);
+ BOOST_CHECK_EQUAL(filter1.hasFaceScope(), false);
+ BOOST_CHECK_EQUAL(filter1.hasFacePersistency(), false);
+ BOOST_CHECK_EQUAL(filter1.hasLinkType(), false);
+
+ filter1.setFaceId(100)
+ .setUriScheme("tcp4")
+ .setRemoteUri("tcp4://192.0.2.1:6363")
+ .setLocalUri("tcp4://192.0.2.2:55555")
+ .setFaceScope(FACE_SCOPE_LOCAL)
+ .setFacePersistency(FACE_PERSISTENCY_ON_DEMAND)
+ .setLinkType(LINK_TYPE_MULTI_ACCESS);
+
+ Block wire = filter1.wireEncode();
+
+ // These octets are obtained by the snippet below.
+ // This check is intended to detect unexpected encoding change in the future.
+ // for (Buffer::const_iterator it = wire.begin(); it != wire.end(); ++it) {
+ // printf("0x%02x, ", *it);
+ // }
+ static const uint8_t expected[] = {
+ 0x96, 0x41, 0x69, 0x01, 0x64, 0x83, 0x04, 0x74, 0x63, 0x70,
+ 0x34, 0x72, 0x15, 0x74, 0x63, 0x70, 0x34, 0x3a, 0x2f, 0x2f,
+ 0x31, 0x39, 0x32, 0x2e, 0x30, 0x2e, 0x32, 0x2e, 0x31, 0x3a,
+ 0x36, 0x33, 0x36, 0x33, 0x81, 0x16, 0x74, 0x63, 0x70, 0x34,
+ 0x3a, 0x2f, 0x2f, 0x31, 0x39, 0x32, 0x2e, 0x30, 0x2e, 0x32,
+ 0x2e, 0x32, 0x3a, 0x35, 0x35, 0x35, 0x35, 0x35, 0x84, 0x01,
+ 0x01, 0x85, 0x01, 0x01, 0x86, 0x01, 0x01,
+ };
+
+ BOOST_CHECK_EQUAL_COLLECTIONS(expected, expected + sizeof(expected),
+ wire.begin(), wire.end());
+
+ FaceQueryFilter filter2(wire);
+ BOOST_CHECK_EQUAL(filter1.getFaceId(), filter2.getFaceId());
+ BOOST_CHECK_EQUAL(filter1.getUriScheme(), filter2.getUriScheme());
+ BOOST_CHECK_EQUAL(filter1.getRemoteUri(), filter2.getRemoteUri());
+ BOOST_CHECK_EQUAL(filter1.getLocalUri(), filter2.getLocalUri());
+ BOOST_CHECK_EQUAL(filter1.getFaceScope(), filter2.getFaceScope());
+ BOOST_CHECK_EQUAL(filter1.getFacePersistency(), filter2.getFacePersistency());
+ BOOST_CHECK_EQUAL(filter1.getLinkType(), filter2.getLinkType());
+}
+
+BOOST_AUTO_TEST_CASE(Equality)
+{
+ FaceQueryFilter filter1, filter2;
+ BOOST_CHECK_EQUAL(filter1.empty(), true);
+ BOOST_CHECK_EQUAL(filter1, filter2);
+
+ filter1.setFaceId(100)
+ .setUriScheme("tcp4")
+ .setRemoteUri("tcp4://192.0.2.1:6363")
+ .setLocalUri("tcp4://192.0.2.2:55555")
+ .setFaceScope(FACE_SCOPE_LOCAL)
+ .setFacePersistency(FACE_PERSISTENCY_ON_DEMAND)
+ .setLinkType(LINK_TYPE_MULTI_ACCESS);
+ BOOST_CHECK_EQUAL(filter1.empty(), false);
+ BOOST_CHECK_NE(filter1, filter2);
+
+ filter2 = filter1;
+ BOOST_CHECK_EQUAL(filter1, filter2);
+
+ filter2.setFaceScope(FACE_SCOPE_NON_LOCAL);
+ BOOST_CHECK_NE(filter1, filter2);
+}
+
+BOOST_AUTO_TEST_CASE(Print)
+{
+ FaceQueryFilter filter;
+ filter.setFaceId(100)
+ .setUriScheme("tcp4")
+ .setRemoteUri("tcp4://192.0.2.1:6363")
+ .setLocalUri("tcp4://192.0.2.2:55555")
+ .setFaceScope(FACE_SCOPE_LOCAL)
+ .setFacePersistency(FACE_PERSISTENCY_ON_DEMAND)
+ .setLinkType(LINK_TYPE_MULTI_ACCESS);
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(filter),
+ "FaceQueryFilter(FaceID: 100,\n"
+ "UriScheme: tcp4,\n"
+ "RemoteUri: tcp4://192.0.2.1:6363,\n"
+ "LocalUri: tcp4://192.0.2.2:55555,\n"
+ "FaceScope: local,\n"
+ "FacePersistency: on-demand,\n"
+ "LinkType: multi-access,\n"
+ ")");
+
+ filter.unsetFaceId()
+ .unsetUriScheme()
+ .unsetRemoteUri()
+ .unsetLocalUri()
+ .unsetFaceScope()
+ .unsetFacePersistency()
+ .unsetLinkType();
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(filter), "FaceQueryFilter()");
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestFaceQueryFilter
+BOOST_AUTO_TEST_SUITE_END() // Nfd
+BOOST_AUTO_TEST_SUITE_END() // Mgmt
+
+} // namespace tests
+} // namespace nfd
+} // namespace ndn
diff --git a/tests/unit/mgmt/nfd/face-status.t.cpp b/tests/unit/mgmt/nfd/face-status.t.cpp
new file mode 100644
index 0000000..0ed7aca
--- /dev/null
+++ b/tests/unit/mgmt/nfd/face-status.t.cpp
@@ -0,0 +1,179 @@
+/* -*- 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 "mgmt/nfd/face-status.hpp"
+
+#include "boost-test.hpp"
+#include <boost/lexical_cast.hpp>
+
+namespace ndn {
+namespace nfd {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Mgmt)
+BOOST_AUTO_TEST_SUITE(Nfd)
+BOOST_AUTO_TEST_SUITE(TestFaceStatus)
+
+static FaceStatus
+makeFaceStatus()
+{
+ return FaceStatus()
+ .setFaceId(100)
+ .setRemoteUri("tcp4://192.0.2.1:6363")
+ .setLocalUri("tcp4://192.0.2.2:55555")
+ .setFaceScope(FACE_SCOPE_LOCAL)
+ .setFacePersistency(FACE_PERSISTENCY_ON_DEMAND)
+ .setLinkType(LINK_TYPE_MULTI_ACCESS)
+ .setExpirationPeriod(10_s)
+ .setBaseCongestionMarkingInterval(5_ns)
+ .setDefaultCongestionThreshold(7)
+ .setMtu(9)
+ .setNInInterests(10)
+ .setNInData(200)
+ .setNInNacks(1)
+ .setNOutInterests(3000)
+ .setNOutData(4)
+ .setNOutNacks(2)
+ .setNInBytes(1329719163)
+ .setNOutBytes(999110448)
+ .setFlags(0x7);
+}
+
+BOOST_AUTO_TEST_CASE(Encode)
+{
+ FaceStatus status1 = makeFaceStatus();
+ Block wire = status1.wireEncode();
+
+ // These octets are obtained by the snippet below.
+ // This check is intended to detect unexpected encoding change in the future.
+ // for (Buffer::const_iterator it = wire.begin(); it != wire.end(); ++it) {
+ // printf("0x%02x, ", *it);
+ // }
+ static const uint8_t expected[] = {
+ 0x80, 0x6a, 0x69, 0x01, 0x64, 0x72, 0x15, 0x74, 0x63, 0x70,
+ 0x34, 0x3a, 0x2f, 0x2f, 0x31, 0x39, 0x32, 0x2e, 0x30, 0x2e,
+ 0x32, 0x2e, 0x31, 0x3a, 0x36, 0x33, 0x36, 0x33, 0x81, 0x16,
+ 0x74, 0x63, 0x70, 0x34, 0x3a, 0x2f, 0x2f, 0x31, 0x39, 0x32,
+ 0x2e, 0x30, 0x2e, 0x32, 0x2e, 0x32, 0x3a, 0x35, 0x35, 0x35,
+ 0x35, 0x35, 0x6d, 0x02, 0x27, 0x10, 0x84, 0x01, 0x01, 0x85,
+ 0x01, 0x01, 0x86, 0x01, 0x01, 0x87, 0x01, 0x05, 0x88, 0x01,
+ 0x07, 0x89, 0x01, 0x09, 0x90, 0x01, 0x0a, 0x91, 0x01, 0xc8,
+ 0x97, 0x01, 0x01, 0x92, 0x02, 0x0b, 0xb8, 0x93, 0x01, 0x04,
+ 0x98, 0x01, 0x02, 0x94, 0x04, 0x4f, 0x41, 0xe7, 0x7b, 0x95,
+ 0x04, 0x3b, 0x8d, 0x37, 0x30, 0x6c, 0x01, 0x07,
+ };
+ BOOST_CHECK_EQUAL_COLLECTIONS(expected, expected + sizeof(expected),
+ wire.begin(), wire.end());
+
+ FaceStatus status2(wire);
+ BOOST_CHECK_EQUAL(status1, status2);
+}
+
+BOOST_AUTO_TEST_CASE(Equality)
+{
+ FaceStatus status1, status2;
+
+ status1 = makeFaceStatus();
+ status2 = status1;
+ BOOST_CHECK_EQUAL(status1, status2);
+
+ status2.setFaceId(42);
+ BOOST_CHECK_NE(status1, status2);
+}
+
+BOOST_AUTO_TEST_CASE(Print)
+{
+ FaceStatus status;
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(status),
+ "Face(FaceId: 0,\n"
+ " RemoteUri: ,\n"
+ " LocalUri: ,\n"
+ " ExpirationPeriod: infinite,\n"
+ " FaceScope: non-local,\n"
+ " FacePersistency: persistent,\n"
+ " LinkType: point-to-point,\n"
+ " Flags: 0x0,\n"
+ " Counters: {Interests: {in: 0, out: 0},\n"
+ " Data: {in: 0, out: 0},\n"
+ " Nacks: {in: 0, out: 0},\n"
+ " bytes: {in: 0, out: 0}}\n"
+ " )");
+
+ status = makeFaceStatus();
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(status),
+ "Face(FaceId: 100,\n"
+ " RemoteUri: tcp4://192.0.2.1:6363,\n"
+ " LocalUri: tcp4://192.0.2.2:55555,\n"
+ " ExpirationPeriod: 10000 milliseconds,\n"
+ " FaceScope: local,\n"
+ " FacePersistency: on-demand,\n"
+ " LinkType: multi-access,\n"
+ " BaseCongestionMarkingInterval: 5 nanoseconds,\n"
+ " DefaultCongestionThreshold: 7 bytes,\n"
+ " Mtu: 9 bytes,\n"
+ " Flags: 0x7,\n"
+ " Counters: {Interests: {in: 10, out: 3000},\n"
+ " Data: {in: 200, out: 4},\n"
+ " Nacks: {in: 1, out: 2},\n"
+ " bytes: {in: 1329719163, out: 999110448}}\n"
+ " )");
+}
+
+BOOST_AUTO_TEST_CASE(ExpirationPeriod)
+{
+ FaceStatus status;
+ BOOST_CHECK_EQUAL(status.hasExpirationPeriod(), false);
+
+ status.setExpirationPeriod(1_min);
+ BOOST_REQUIRE_EQUAL(status.hasExpirationPeriod(), true);
+ BOOST_CHECK_EQUAL(status.getExpirationPeriod(), 1_min);
+
+ status.unsetExpirationPeriod();
+ BOOST_CHECK_EQUAL(status.hasExpirationPeriod(), false);
+}
+
+BOOST_AUTO_TEST_CASE(FlagBit)
+{
+ FaceStatus status;
+ status.setFlags(0x7);
+ BOOST_CHECK_EQUAL(status.getFlags(), 0x7);
+
+ BOOST_CHECK(status.getFlagBit(0));
+ BOOST_CHECK(status.getFlagBit(1));
+ BOOST_CHECK(status.getFlagBit(2));
+ BOOST_CHECK(!status.getFlagBit(3));
+
+ status.setFlagBit(3, true);
+ BOOST_CHECK_EQUAL(status.getFlags(), 0xf);
+ BOOST_CHECK(status.getFlagBit(3));
+
+ status.setFlagBit(1, false);
+ BOOST_CHECK_EQUAL(status.getFlags(), 0xd);
+ BOOST_CHECK(!status.getFlagBit(1));
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestFaceStatus
+BOOST_AUTO_TEST_SUITE_END() // Nfd
+BOOST_AUTO_TEST_SUITE_END() // Mgmt
+
+} // namespace tests
+} // namespace nfd
+} // namespace ndn
diff --git a/tests/unit/mgmt/nfd/fib-entry.t.cpp b/tests/unit/mgmt/nfd/fib-entry.t.cpp
new file mode 100644
index 0000000..fa7ec69
--- /dev/null
+++ b/tests/unit/mgmt/nfd/fib-entry.t.cpp
@@ -0,0 +1,196 @@
+/* -*- 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 "mgmt/nfd/fib-entry.hpp"
+
+#include "boost-test.hpp"
+#include <boost/lexical_cast.hpp>
+
+namespace ndn {
+namespace nfd {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Mgmt)
+BOOST_AUTO_TEST_SUITE(Nfd)
+BOOST_AUTO_TEST_SUITE(TestFibEntry)
+
+static FibEntry
+makeFibEntry()
+{
+ std::vector<NextHopRecord> nexthops;
+ for (size_t i = 1; i < 4; i++) {
+ nexthops.push_back(NextHopRecord()
+ .setFaceId(i * 10)
+ .setCost(i * 100 + 100));
+ }
+
+ return FibEntry()
+ .setPrefix("/this/is/a/test")
+ .setNextHopRecords(nexthops.begin(), nexthops.end());
+}
+
+BOOST_AUTO_TEST_CASE(NextHopRecordEncode)
+{
+ NextHopRecord record1;
+ record1.setFaceId(10)
+ .setCost(200);
+ const Block& wire = record1.wireEncode();
+
+ static const uint8_t expected[] = {
+ 0x81, 0x06, 0x69, 0x01, 0x0a, 0x6a, 0x01, 0xc8
+ };
+ BOOST_CHECK_EQUAL_COLLECTIONS(expected, expected + sizeof(expected),
+ wire.begin(), wire.end());
+
+ NextHopRecord record2(wire);
+ BOOST_CHECK_EQUAL(record1, record2);
+}
+
+BOOST_AUTO_TEST_CASE(NextHopRecordEquality)
+{
+ NextHopRecord record1, record2;
+
+ record1.setFaceId(10)
+ .setCost(200);
+ record2 = record1;
+ BOOST_CHECK_EQUAL(record1, record2);
+
+ record2.setFaceId(42);
+ BOOST_CHECK_NE(record1, record2);
+
+ record2 = record1;
+ record2.setCost(42);
+ BOOST_CHECK_NE(record1, record2);
+}
+
+BOOST_AUTO_TEST_CASE(FibEntryNoNextHopsEncode)
+{
+ FibEntry entry1;
+ entry1.setPrefix("/this/is/a/test");
+ BOOST_REQUIRE(entry1.getNextHopRecords().empty());
+ const Block& wire = entry1.wireEncode();
+
+ static const uint8_t expected[] = {
+ 0x80, 0x15, 0x07, 0x13, 0x08, 0x04, 0x74, 0x68, 0x69, 0x73,
+ 0x08, 0x02, 0x69, 0x73, 0x08, 0x01, 0x61, 0x08, 0x04, 0x74,
+ 0x65, 0x73, 0x74
+ };
+ BOOST_CHECK_EQUAL_COLLECTIONS(expected, expected + sizeof(expected),
+ wire.begin(), wire.end());
+
+ FibEntry entry2(wire);
+ BOOST_CHECK_EQUAL(entry1, entry2);
+}
+
+BOOST_AUTO_TEST_CASE(FibEntryEncode)
+{
+ FibEntry entry1 = makeFibEntry();
+ NextHopRecord oneMore;
+ oneMore.setFaceId(40);
+ oneMore.setCost(500);
+ entry1.addNextHopRecord(oneMore);
+ const Block& wire = entry1.wireEncode();
+
+ static const uint8_t expected[] = {
+ 0x80, 0x38, 0x07, 0x13, 0x08, 0x04, 0x74, 0x68, 0x69, 0x73, 0x08, 0x02, 0x69, 0x73, 0x08, 0x01,
+ 0x61, 0x08, 0x04, 0x74, 0x65, 0x73, 0x74, 0x81, 0x06, 0x69, 0x01, 0x0a, 0x6a, 0x01, 0xc8, 0x81,
+ 0x07, 0x69, 0x01, 0x14, 0x6a, 0x02, 0x01, 0x2c, 0x81, 0x07, 0x69, 0x01, 0x1e, 0x6a, 0x02, 0x01,
+ 0x90, 0x81, 0x07, 0x69, 0x01, 0x28, 0x6a, 0x02, 0x01, 0xf4
+ };
+ BOOST_CHECK_EQUAL_COLLECTIONS(expected, expected + sizeof(expected),
+ wire.begin(), wire.end());
+
+ FibEntry entry2(wire);
+ BOOST_CHECK_EQUAL(entry1, entry2);
+}
+
+BOOST_AUTO_TEST_CASE(FibEntryEquality)
+{
+ FibEntry entry1, entry2;
+ BOOST_CHECK_EQUAL(entry1, entry2);
+
+ entry1 = entry2 = makeFibEntry();
+ BOOST_CHECK_EQUAL(entry1, entry2);
+ BOOST_CHECK_EQUAL(entry2, entry1);
+
+ entry2.setPrefix("/another/prefix");
+ BOOST_CHECK_NE(entry1, entry2);
+
+ entry2 = entry1;
+ std::vector<NextHopRecord> empty;
+ entry2.setNextHopRecords(empty.begin(), empty.end());
+ BOOST_CHECK_NE(entry1, entry2);
+ BOOST_CHECK_NE(entry2, entry1);
+
+ entry2 = entry1;
+ auto nh1 = NextHopRecord()
+ .setFaceId(1)
+ .setCost(1000);
+ entry1.addNextHopRecord(nh1);
+ BOOST_CHECK_NE(entry1, entry2);
+ BOOST_CHECK_NE(entry2, entry1);
+
+ auto nh42 = NextHopRecord()
+ .setFaceId(42)
+ .setCost(42);
+ entry1.addNextHopRecord(nh42);
+ entry2.addNextHopRecord(nh42)
+ .addNextHopRecord(nh1);
+ BOOST_CHECK_EQUAL(entry1, entry2); // order of NextHopRecords is irrelevant
+ BOOST_CHECK_EQUAL(entry2, entry1);
+
+ entry1 = entry2 = makeFibEntry();
+ entry1.addNextHopRecord(nh1)
+ .addNextHopRecord(nh42);
+ entry2.addNextHopRecord(nh42)
+ .addNextHopRecord(nh42);
+ BOOST_CHECK_NE(entry1, entry2); // match each NextHopRecord at most once
+ BOOST_CHECK_NE(entry2, entry1);
+}
+
+BOOST_AUTO_TEST_CASE(Print)
+{
+ NextHopRecord record;
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(record),
+ "NextHopRecord(FaceId: 0, Cost: 0)");
+
+ FibEntry entry;
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(entry),
+ "FibEntry(Prefix: /,\n"
+ " NextHops: []\n"
+ " )");
+
+ entry = makeFibEntry();
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(entry),
+ "FibEntry(Prefix: /this/is/a/test,\n"
+ " NextHops: [NextHopRecord(FaceId: 10, Cost: 200),\n"
+ " NextHopRecord(FaceId: 20, Cost: 300),\n"
+ " NextHopRecord(FaceId: 30, Cost: 400)]\n"
+ " )");
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestFibEntry
+BOOST_AUTO_TEST_SUITE_END() // Nfd
+BOOST_AUTO_TEST_SUITE_END() // Mgmt
+
+} // namespace tests
+} // namespace nfd
+} // namespace ndn
diff --git a/tests/unit/mgmt/nfd/forwarder-status.t.cpp b/tests/unit/mgmt/nfd/forwarder-status.t.cpp
new file mode 100644
index 0000000..b2c3d48
--- /dev/null
+++ b/tests/unit/mgmt/nfd/forwarder-status.t.cpp
@@ -0,0 +1,141 @@
+/* -*- 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 "mgmt/nfd/forwarder-status.hpp"
+
+#include "boost-test.hpp"
+#include <boost/lexical_cast.hpp>
+
+namespace ndn {
+namespace nfd {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Mgmt)
+BOOST_AUTO_TEST_SUITE(Nfd)
+BOOST_AUTO_TEST_SUITE(TestForwarderStatus)
+
+static ForwarderStatus
+makeForwarderStatus()
+{
+ return ForwarderStatus()
+ .setNfdVersion("0.5.1-14-g05dd444")
+ .setStartTimestamp(time::fromUnixTimestamp(time::milliseconds(375193249325LL)))
+ .setCurrentTimestamp(time::fromUnixTimestamp(time::milliseconds(886109034272LL)))
+ .setNNameTreeEntries(1849943160)
+ .setNFibEntries(621739748)
+ .setNPitEntries(482129741)
+ .setNMeasurementsEntries(1771725298)
+ .setNCsEntries(1264968688)
+ .setNInInterests(612811615)
+ .setNInData(1843576050)
+ .setNInNacks(1234)
+ .setNOutInterests(952144445)
+ .setNOutData(138198826)
+ .setNOutNacks(4321)
+ .setNSatisfiedInterests(961020)
+ .setNUnsatisfiedInterests(941024);
+}
+
+BOOST_AUTO_TEST_CASE(Encode)
+{
+ ForwarderStatus status1 = makeForwarderStatus();
+ Block wire = status1.wireEncode();
+
+ // These octets are obtained by the snippet below.
+ // This check is intended to detect unexpected encoding change in the future.
+ // for (Buffer::const_iterator it = wire.begin(); it != wire.end(); ++it) {
+ // printf("0x%02x, ", *it);
+ // }
+ static const uint8_t expected[] = {
+ 0x15, 0x71, 0x80, 0x11, 0x30, 0x2e, 0x35, 0x2e, 0x31, 0x2d, 0x31, 0x34,
+ 0x2d, 0x67, 0x30, 0x35, 0x64, 0x64, 0x34, 0x34, 0x34, 0x81, 0x08, 0x00,
+ 0x00, 0x00, 0x57, 0x5b, 0x42, 0xa6, 0x2d, 0x82, 0x08, 0x00, 0x00, 0x00,
+ 0xce, 0x50, 0x36, 0xd7, 0x20, 0x83, 0x04, 0x6e, 0x43, 0xe4, 0x78, 0x84,
+ 0x04, 0x25, 0x0e, 0xfe, 0xe4, 0x85, 0x04, 0x1c, 0xbc, 0xb7, 0x4d, 0x86,
+ 0x04, 0x69, 0x9a, 0x61, 0xf2, 0x87, 0x04, 0x4b, 0x65, 0xe3, 0xf0, 0x90,
+ 0x04, 0x24, 0x86, 0xc3, 0x5f, 0x91, 0x04, 0x6d, 0xe2, 0xbc, 0xf2, 0x97,
+ 0x02, 0x04, 0xd2, 0x92, 0x04, 0x38, 0xc0, 0x92, 0x3d, 0x93, 0x04, 0x08,
+ 0x3c, 0xbf, 0x2a, 0x98, 0x02, 0x10, 0xe1, 0x99, 0x04, 0x00, 0x0e, 0xa9,
+ 0xfc, 0x9a, 0x04, 0x00, 0x0e, 0x5b, 0xe0,
+ };
+ BOOST_CHECK_EQUAL_COLLECTIONS(expected, expected + sizeof(expected),
+ wire.begin(), wire.end());
+
+ ForwarderStatus status2(wire);
+ BOOST_CHECK_EQUAL(status1, status2);
+}
+
+BOOST_AUTO_TEST_CASE(Equality)
+{
+ ForwarderStatus status1, status2;
+
+ status1 = makeForwarderStatus();
+ status2 = status1;
+ BOOST_CHECK_EQUAL(status1, status2);
+
+ status2.setNPitEntries(42);
+ BOOST_CHECK_NE(status1, status2);
+}
+
+BOOST_AUTO_TEST_CASE(Print)
+{
+ ForwarderStatus status;
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(status),
+ "GeneralStatus(NfdVersion: ,\n"
+ " StartTimestamp: 0 nanoseconds since Jan 1, 1970,\n"
+ " CurrentTimestamp: 0 nanoseconds since Jan 1, 1970,\n"
+ " Counters: {NameTreeEntries: 0,\n"
+ " FibEntries: 0,\n"
+ " PitEntries: 0,\n"
+ " MeasurementsEntries: 0,\n"
+ " CsEntries: 0,\n"
+ " Interests: {in: 0, out: 0},\n"
+ " Data: {in: 0, out: 0},\n"
+ " Nacks: {in: 0, out: 0},\n"
+ " SatisfiedInterests: 0,\n"
+ " UnsatisfiedInterests: 0}\n"
+ " )");
+
+ status = makeForwarderStatus();
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(status),
+ "GeneralStatus(NfdVersion: 0.5.1-14-g05dd444,\n"
+ " StartTimestamp: 375193249325000000 nanoseconds since Jan 1, 1970,\n"
+ " CurrentTimestamp: 886109034272000000 nanoseconds since Jan 1, 1970,\n"
+ " Counters: {NameTreeEntries: 1849943160,\n"
+ " FibEntries: 621739748,\n"
+ " PitEntries: 482129741,\n"
+ " MeasurementsEntries: 1771725298,\n"
+ " CsEntries: 1264968688,\n"
+ " Interests: {in: 612811615, out: 952144445},\n"
+ " Data: {in: 1843576050, out: 138198826},\n"
+ " Nacks: {in: 1234, out: 4321},\n"
+ " SatisfiedInterests: 961020,\n"
+ " UnsatisfiedInterests: 941024}\n"
+ " )");
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestForwarderStatus
+BOOST_AUTO_TEST_SUITE_END() // Nfd
+BOOST_AUTO_TEST_SUITE_END() // Mgmt
+
+} // namespace tests
+} // namespace nfd
+} // namespace ndn
diff --git a/tests/unit/mgmt/nfd/rib-entry.t.cpp b/tests/unit/mgmt/nfd/rib-entry.t.cpp
new file mode 100644
index 0000000..f2ab51a
--- /dev/null
+++ b/tests/unit/mgmt/nfd/rib-entry.t.cpp
@@ -0,0 +1,240 @@
+/* -*- 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 "mgmt/nfd/rib-entry.hpp"
+
+#include "boost-test.hpp"
+#include <boost/lexical_cast.hpp>
+
+namespace ndn {
+namespace nfd {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Mgmt)
+BOOST_AUTO_TEST_SUITE(Nfd)
+BOOST_AUTO_TEST_SUITE(TestRibEntry)
+
+static Route
+makeRoute()
+{
+ return Route()
+ .setFaceId(1)
+ .setOrigin(ROUTE_ORIGIN_NLSR)
+ .setCost(100)
+ .setFlags(ROUTE_FLAG_CAPTURE);
+}
+
+static RibEntry
+makeRibEntry()
+{
+ return RibEntry()
+ .setName("/hello/world")
+ .addRoute(makeRoute()
+ .setExpirationPeriod(10_s));
+}
+
+BOOST_AUTO_TEST_CASE(RouteEncode)
+{
+ Route route1 = makeRoute();
+ route1.setExpirationPeriod(10_s);
+ const Block& wire = route1.wireEncode();
+
+ static const uint8_t expected[] = {
+ 0x81, 0x10, 0x69, 0x01, 0x01, 0x6f, 0x01, 0x80, 0x6a, 0x01, 0x64, 0x6c, 0x01, 0x02,
+ 0x6d, 0x02, 0x27, 0x10
+ };
+ BOOST_CHECK_EQUAL_COLLECTIONS(expected, expected + sizeof(expected),
+ wire.begin(), wire.end());
+
+ Route route2(wire);
+ BOOST_CHECK_EQUAL(route1, route2);
+}
+
+BOOST_AUTO_TEST_CASE(RouteNoExpirationPeriodEncode)
+{
+ Route route1 = makeRoute();
+ const Block& wire = route1.wireEncode();
+
+ static const uint8_t expected[] = {
+ 0x81, 0x0C, 0x69, 0x01, 0x01, 0x6f, 0x01, 0x80, 0x6a, 0x01, 0x64, 0x6c, 0x01, 0x02
+ };
+ BOOST_CHECK_EQUAL_COLLECTIONS(expected, expected + sizeof(expected),
+ wire.begin(), wire.end());
+
+ Route route2(wire);
+ BOOST_CHECK_EQUAL(route1, route2);
+}
+
+BOOST_AUTO_TEST_CASE(RouteExpirationPeriod)
+{
+ Route route;
+ BOOST_CHECK_EQUAL(route.hasExpirationPeriod(), false);
+ BOOST_CHECK_EQUAL(route.getExpirationPeriod(), time::milliseconds::max());
+
+ route.setExpirationPeriod(1_min);
+ BOOST_CHECK_EQUAL(route.hasExpirationPeriod(), true);
+ BOOST_CHECK_EQUAL(route.getExpirationPeriod(), 1_min);
+
+ route.setExpirationPeriod(time::milliseconds::max());
+ BOOST_CHECK_EQUAL(route.hasExpirationPeriod(), false);
+ BOOST_CHECK_EQUAL(route.getExpirationPeriod(), time::milliseconds::max());
+
+ route.setExpirationPeriod(1_min);
+ BOOST_CHECK_EQUAL(route.hasExpirationPeriod(), true);
+
+ route.unsetExpirationPeriod();
+ BOOST_CHECK_EQUAL(route.hasExpirationPeriod(), false);
+}
+
+BOOST_AUTO_TEST_CASE(RouteEquality)
+{
+ Route route1, route2;
+
+ route1 = makeRoute();
+ route2 = route1;
+ BOOST_CHECK_EQUAL(route1, route2);
+
+ route2.setFaceId(42);
+ BOOST_CHECK_NE(route1, route2);
+
+ route2 = route1;
+ route2.setExpirationPeriod(1_min);
+ BOOST_CHECK_NE(route1, route2);
+}
+
+BOOST_AUTO_TEST_CASE(RibEntryEncode)
+{
+ RibEntry entry1 = makeRibEntry();
+ entry1.addRoute(Route()
+ .setFaceId(2)
+ .setOrigin(ROUTE_ORIGIN_APP)
+ .setCost(32)
+ .setFlags(ROUTE_FLAG_CHILD_INHERIT)
+ .setExpirationPeriod(5_s));
+ const Block& wire = entry1.wireEncode();
+
+ static const uint8_t expected[] = {
+ 0x80, 0x34, 0x07, 0x0e, 0x08, 0x05, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x08, 0x05, 0x77,
+ 0x6f, 0x72, 0x6c, 0x64, 0x81, 0x10, 0x69, 0x01, 0x01, 0x6f, 0x01, 0x80, 0x6a, 0x01,
+ 0x64, 0x6c, 0x01, 0x02, 0x6d, 0x02, 0x27, 0x10, 0x81, 0x10, 0x69, 0x01, 0x02, 0x6f,
+ 0x01, 0x00, 0x6a, 0x01, 0x20, 0x6c, 0x01, 0x01, 0x6d, 0x02, 0x13, 0x88
+ };
+ BOOST_CHECK_EQUAL_COLLECTIONS(expected, expected + sizeof(expected),
+ wire.begin(), wire.end());
+
+ RibEntry entry2(wire);
+ BOOST_CHECK_EQUAL(entry1, entry2);
+}
+
+BOOST_AUTO_TEST_CASE(RibEntryClearRoutes)
+{
+ RibEntry entry;
+ entry.setName("/hello/world");
+ BOOST_CHECK_EQUAL(entry.getRoutes().size(), 0);
+
+ Route route1;
+ route1.setFaceId(42);
+ entry.addRoute(route1);
+ BOOST_REQUIRE_EQUAL(entry.getRoutes().size(), 1);
+ BOOST_CHECK_EQUAL(entry.getRoutes().front(), route1);
+
+ entry.clearRoutes();
+ BOOST_CHECK_EQUAL(entry.getRoutes().size(), 0);
+}
+
+BOOST_AUTO_TEST_CASE(RibEntryEquality)
+{
+ RibEntry entry1, entry2;
+ BOOST_CHECK_EQUAL(entry1, entry2);
+
+ entry1 = entry2 = makeRibEntry();
+ BOOST_CHECK_EQUAL(entry1, entry2);
+ BOOST_CHECK_EQUAL(entry2, entry1);
+
+ entry2.setName("/different/name");
+ BOOST_CHECK_NE(entry1, entry2);
+
+ entry2 = entry1;
+ std::vector<Route> empty;
+ entry2.setRoutes(empty.begin(), empty.end());
+ BOOST_CHECK_NE(entry1, entry2);
+ BOOST_CHECK_NE(entry2, entry1);
+
+ entry2 = entry1;
+ auto r1 = Route()
+ .setFaceId(1)
+ .setCost(1000);
+ entry1.addRoute(r1);
+ BOOST_CHECK_NE(entry1, entry2);
+ BOOST_CHECK_NE(entry2, entry1);
+
+ auto r42 = Route()
+ .setFaceId(42)
+ .setCost(42);
+ entry1.addRoute(r42);
+ entry2.addRoute(r42)
+ .addRoute(r1);
+ BOOST_CHECK_EQUAL(entry1, entry2); // order of Routes is irrelevant
+ BOOST_CHECK_EQUAL(entry2, entry1);
+
+ entry1 = entry2 = makeRibEntry();
+ entry1.addRoute(r1)
+ .addRoute(r42);
+ entry2.addRoute(r42)
+ .addRoute(r42);
+ BOOST_CHECK_NE(entry1, entry2); // match each Route at most once
+ BOOST_CHECK_NE(entry2, entry1);
+}
+
+BOOST_AUTO_TEST_CASE(Print)
+{
+ Route route;
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(route),
+ "Route(FaceId: 0, Origin: app, Cost: 0, Flags: 0x1, ExpirationPeriod: infinite)");
+
+ RibEntry entry;
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(entry),
+ "RibEntry(Prefix: /,\n"
+ " Routes: []\n"
+ " )");
+
+ entry = makeRibEntry();
+ entry.addRoute(Route()
+ .setFaceId(2)
+ .setOrigin(ROUTE_ORIGIN_STATIC)
+ .setCost(32)
+ .setFlags(ROUTE_FLAG_CHILD_INHERIT));
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(entry),
+ "RibEntry(Prefix: /hello/world,\n"
+ " Routes: [Route(FaceId: 1, Origin: nlsr, Cost: 100, Flags: 0x2, "
+ "ExpirationPeriod: 10000 milliseconds),\n"
+ " Route(FaceId: 2, Origin: static, Cost: 32, Flags: 0x1, "
+ "ExpirationPeriod: infinite)]\n"
+ " )");
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestRibEntry
+BOOST_AUTO_TEST_SUITE_END() // Nfd
+BOOST_AUTO_TEST_SUITE_END() // Mgmt
+
+} // namespace tests
+} // namespace nfd
+} // namespace ndn
diff --git a/tests/unit/mgmt/nfd/status-dataset.t.cpp b/tests/unit/mgmt/nfd/status-dataset.t.cpp
new file mode 100644
index 0000000..a436de0
--- /dev/null
+++ b/tests/unit/mgmt/nfd/status-dataset.t.cpp
@@ -0,0 +1,486 @@
+/* -*- 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 "mgmt/nfd/status-dataset.hpp"
+#include "mgmt/nfd/controller.hpp"
+
+#include "controller-fixture.hpp"
+#include "make-interest-data.hpp"
+
+namespace ndn {
+namespace nfd {
+namespace tests {
+
+using namespace ndn::tests;
+
+BOOST_AUTO_TEST_SUITE(Mgmt)
+BOOST_AUTO_TEST_SUITE(Nfd)
+
+class ControllerStatusDatasetFixture : public ControllerFixture
+{
+protected:
+ /** \brief send one WireEncodable as Data reply
+ * \param prefix dataset prefix without version and segment
+ * \param payload payload block
+ * \note payload must fit in one Data
+ */
+ template<typename T>
+ void
+ sendDataset(const Name& prefix, const T& payload)
+ {
+ BOOST_CONCEPT_ASSERT((WireEncodable<T>));
+
+ auto data = this->prepareDatasetReply(prefix);
+ data->setContent(payload.wireEncode());
+ face.receive(*signData(data));
+ }
+
+ /** \brief send two WireEncodables as Data reply
+ * \param prefix dataset prefix without version and segment
+ * \param payload1 first vector item
+ * \param payload2 second vector item
+ * \note all payloads must fit in one Data
+ */
+ template<typename T1, typename T2>
+ void
+ sendDataset(const Name& prefix, const T1& payload1, const T2& payload2)
+ {
+ // The test suite allows up to two items, and put them in the same Data packet,
+ // because this test suite focuses on Controller::fetch<StatusDataset>,
+ // and is not intended to cover SegmentFetcher behavior.
+
+ BOOST_CONCEPT_ASSERT((WireEncodable<T1>));
+ BOOST_CONCEPT_ASSERT((WireEncodable<T2>));
+
+ ndn::encoding::EncodingBuffer buffer;
+ payload2.wireEncode(buffer);
+ payload1.wireEncode(buffer);
+
+ auto data = this->prepareDatasetReply(prefix);
+ data->setContent(buffer.buf(), buffer.size());
+ face.receive(*signData(data));
+ }
+
+private:
+ shared_ptr<Data>
+ prepareDatasetReply(const Name& prefix)
+ {
+ Name name = prefix;
+ name.appendVersion().appendSegment(0);
+
+ // These warnings assist in debugging a `hasResult` check failure.
+ // They usually indicate a misspelled prefix or incorrect timing in the test case.
+ if (face.sentInterests.size() < 1) {
+ BOOST_WARN_MESSAGE(false, "no Interest expressed");
+ }
+ else {
+ BOOST_WARN_MESSAGE(face.sentInterests.back().getName().isPrefixOf(name),
+ "last Interest " << face.sentInterests.back().getName() <<
+ " cannot be satisfied by this Data " << name);
+ }
+
+ auto data = make_shared<Data>(name);
+ data->setFinalBlock(name[-1]);
+ return data;
+ }
+};
+
+BOOST_FIXTURE_TEST_SUITE(TestStatusDataset, ControllerStatusDatasetFixture)
+
+BOOST_AUTO_TEST_SUITE(Failures)
+
+BOOST_AUTO_TEST_CASE(Timeout)
+{
+ CommandOptions options;
+ options.setTimeout(3000_ms);
+ controller.fetch<FaceDataset>(
+ [] (const std::vector<FaceStatus>& result) { BOOST_FAIL("fetchDataset should not succeed"); },
+ datasetFailCallback,
+ options);
+ this->advanceClocks(500_ms);
+ BOOST_CHECK_EQUAL(controller.m_fetchers.size(), 1);
+
+ this->advanceClocks(500_ms, 6);
+ BOOST_CHECK_EQUAL(controller.m_fetchers.size(), 0);
+ BOOST_REQUIRE_EQUAL(failCodes.size(), 1);
+ BOOST_CHECK_EQUAL(failCodes.back(), Controller::ERROR_TIMEOUT);
+}
+
+BOOST_AUTO_TEST_CASE(DataHasNoSegment)
+{
+ controller.fetch<FaceDataset>(
+ [] (const std::vector<FaceStatus>& result) { BOOST_FAIL("fetchDataset should not succeed"); },
+ datasetFailCallback);
+ this->advanceClocks(500_ms);
+
+ face.receive(*makeData("/localhost/nfd/faces/list/%FD%00"));
+ this->advanceClocks(500_ms);
+
+ BOOST_REQUIRE_EQUAL(failCodes.size(), 1);
+ BOOST_CHECK_EQUAL(failCodes.back(), Controller::ERROR_SERVER);
+}
+
+BOOST_AUTO_TEST_CASE(ValidationFailure)
+{
+ this->setValidationResult(false);
+
+ controller.fetch<FaceDataset>(
+ [] (const std::vector<FaceStatus>& result) { BOOST_FAIL("fetchDataset should not succeed"); },
+ datasetFailCallback);
+ this->advanceClocks(500_ms);
+
+ FaceStatus payload;
+ payload.setFaceId(5744);
+ this->sendDataset("/localhost/nfd/faces/list", payload);
+ this->advanceClocks(500_ms);
+
+ BOOST_REQUIRE_EQUAL(failCodes.size(), 1);
+ BOOST_CHECK_EQUAL(failCodes.back(), Controller::ERROR_VALIDATION);
+}
+
+BOOST_AUTO_TEST_CASE(Nack)
+{
+ controller.fetch<FaceDataset>(
+ [] (const std::vector<FaceStatus>& result) { BOOST_FAIL("fetchDataset should not succeed"); },
+ datasetFailCallback);
+ this->advanceClocks(500_ms);
+
+ BOOST_REQUIRE_EQUAL(face.sentInterests.size(), 1);
+ face.receive(lp::Nack(face.sentInterests.back()));
+ this->advanceClocks(500_ms);
+
+ BOOST_REQUIRE_EQUAL(failCodes.size(), 1);
+ BOOST_CHECK_EQUAL(failCodes.back(), Controller::ERROR_NACK);
+}
+
+BOOST_AUTO_TEST_CASE(ParseError1)
+{
+ controller.fetch<FaceDataset>(
+ [] (const std::vector<FaceStatus>& result) { BOOST_FAIL("fetchDataset should not succeed"); },
+ datasetFailCallback);
+ this->advanceClocks(500_ms);
+
+ Name payload; // Name is not valid FaceStatus
+ this->sendDataset("/localhost/nfd/faces/list", payload);
+ this->advanceClocks(500_ms);
+
+ BOOST_REQUIRE_EQUAL(failCodes.size(), 1);
+ BOOST_CHECK_EQUAL(failCodes.back(), Controller::ERROR_SERVER);
+}
+
+BOOST_AUTO_TEST_CASE(ParseError2)
+{
+ controller.fetch<FaceDataset>(
+ [] (const std::vector<FaceStatus>& result) { BOOST_FAIL("fetchDataset should not succeed"); },
+ datasetFailCallback);
+ this->advanceClocks(500_ms);
+
+ FaceStatus payload1;
+ payload1.setFaceId(10930);
+ Name payload2; // Name is not valid FaceStatus
+ this->sendDataset("/localhost/nfd/faces/list", payload1, payload2);
+ this->advanceClocks(500_ms);
+
+ BOOST_REQUIRE_EQUAL(failCodes.size(), 1);
+ BOOST_CHECK_EQUAL(failCodes.back(), Controller::ERROR_SERVER);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // Failures
+
+BOOST_AUTO_TEST_SUITE(NoCallback)
+
+BOOST_AUTO_TEST_CASE(Success)
+{
+ controller.fetch<FaceDataset>(
+ nullptr,
+ datasetFailCallback);
+ this->advanceClocks(500_ms);
+ BOOST_CHECK_EQUAL(controller.m_fetchers.size(), 1);
+
+ FaceStatus payload;
+ payload.setFaceId(2577);
+ this->sendDataset("/localhost/nfd/faces/list", payload);
+ BOOST_CHECK_NO_THROW(this->advanceClocks(500_ms));
+
+ BOOST_CHECK_EQUAL(failCodes.size(), 0);
+ BOOST_CHECK_EQUAL(controller.m_fetchers.size(), 0);
+}
+
+BOOST_AUTO_TEST_CASE(Failure)
+{
+ CommandOptions options;
+ options.setTimeout(3000_ms);
+ controller.fetch<FaceDataset>(
+ [] (const std::vector<FaceStatus>& result) { BOOST_FAIL("fetchDataset should not succeed"); },
+ nullptr,
+ options);
+ BOOST_CHECK_NO_THROW(this->advanceClocks(500_ms, 7));
+}
+
+BOOST_AUTO_TEST_SUITE_END() // NoCallback
+
+BOOST_AUTO_TEST_SUITE(Datasets)
+
+BOOST_AUTO_TEST_CASE(StatusGeneral)
+{
+ bool hasResult = false;
+ controller.fetch<ForwarderGeneralStatusDataset>(
+ [&hasResult] (const ForwarderStatus& result) {
+ hasResult = true;
+ BOOST_CHECK_EQUAL(result.getNfdVersion(), "0.4.2");
+ },
+ datasetFailCallback);
+ this->advanceClocks(500_ms);
+
+ ForwarderStatus payload;
+ payload.setNfdVersion("0.4.2");
+ this->sendDataset("/localhost/nfd/status/general", payload);
+ this->advanceClocks(500_ms);
+
+ BOOST_CHECK(hasResult);
+ BOOST_CHECK_EQUAL(failCodes.size(), 0);
+}
+
+BOOST_AUTO_TEST_CASE(FaceList)
+{
+ bool hasResult = false;
+ controller.fetch<FaceDataset>(
+ [&hasResult] (const std::vector<FaceStatus>& result) {
+ hasResult = true;
+ BOOST_CHECK_EQUAL(result.size(), 2);
+ BOOST_CHECK_EQUAL(result.front().getFaceId(), 24485);
+ },
+ datasetFailCallback);
+ this->advanceClocks(500_ms);
+
+ FaceStatus payload1;
+ payload1.setFaceId(24485);
+ FaceStatus payload2;
+ payload2.setFaceId(12987);
+ this->sendDataset("/localhost/nfd/faces/list", payload1, payload2);
+ this->advanceClocks(500_ms);
+
+ BOOST_CHECK(hasResult);
+ BOOST_CHECK_EQUAL(failCodes.size(), 0);
+}
+
+BOOST_AUTO_TEST_CASE(FaceQuery)
+{
+ FaceQueryFilter filter;
+ filter.setUriScheme("udp4");
+ bool hasResult = false;
+ controller.fetch<FaceQueryDataset>(
+ filter,
+ [&hasResult] (const std::vector<FaceStatus>& result) {
+ hasResult = true;
+ BOOST_CHECK_EQUAL(result.size(), 1);
+ BOOST_CHECK_EQUAL(result.front().getFaceId(), 8795);
+ },
+ datasetFailCallback);
+ this->advanceClocks(500_ms);
+
+ Name prefix("/localhost/nfd/faces/query");
+ prefix.append(filter.wireEncode());
+ FaceStatus payload;
+ payload.setFaceId(8795);
+ this->sendDataset(prefix, payload);
+ this->advanceClocks(500_ms);
+
+ BOOST_CHECK(hasResult);
+ BOOST_CHECK_EQUAL(failCodes.size(), 0);
+}
+
+BOOST_AUTO_TEST_CASE(FaceQueryWithOptions)
+{
+ FaceQueryFilter filter;
+ filter.setUriScheme("udp4");
+ CommandOptions options;
+ options.setTimeout(3000_ms);
+ bool hasResult = false;
+ controller.fetch<FaceQueryDataset>(
+ filter,
+ [&hasResult] (const std::vector<FaceStatus>& result) {
+ hasResult = true;
+ BOOST_CHECK_EQUAL(result.size(), 1);
+ BOOST_CHECK_EQUAL(result.front().getFaceId(), 14022);
+ },
+ datasetFailCallback,
+ options);
+ this->advanceClocks(500_ms);
+
+ Name prefix("/localhost/nfd/faces/query");
+ prefix.append(filter.wireEncode());
+ FaceStatus payload;
+ payload.setFaceId(14022);
+ this->sendDataset(prefix, payload);
+ this->advanceClocks(500_ms);
+
+ BOOST_CHECK(hasResult);
+ BOOST_CHECK_EQUAL(failCodes.size(), 0);
+}
+
+BOOST_AUTO_TEST_CASE(FaceChannels)
+{
+ bool hasResult = false;
+ controller.fetch<ChannelDataset>(
+ [&hasResult] (const std::vector<ChannelStatus>& result) {
+ hasResult = true;
+ BOOST_CHECK_EQUAL(result.size(), 2);
+ BOOST_CHECK_EQUAL(result.front().getLocalUri(), "tcp4://192.0.2.1:6363");
+ },
+ datasetFailCallback);
+ this->advanceClocks(500_ms);
+
+ ChannelStatus payload1;
+ payload1.setLocalUri("tcp4://192.0.2.1:6363");
+ ChannelStatus payload2;
+ payload2.setLocalUri("udp4://192.0.2.1:6363");
+ this->sendDataset("/localhost/nfd/faces/channels", payload1, payload2);
+ this->advanceClocks(500_ms);
+
+ BOOST_CHECK(hasResult);
+ BOOST_CHECK_EQUAL(failCodes.size(), 0);
+}
+
+BOOST_AUTO_TEST_CASE(FibList)
+{
+ bool hasResult = false;
+ controller.fetch<FibDataset>(
+ [&hasResult] (const std::vector<FibEntry>& result) {
+ hasResult = true;
+ BOOST_CHECK_EQUAL(result.size(), 2);
+ BOOST_CHECK_EQUAL(result.front().getPrefix(), "/wYs7fzYcfG");
+ },
+ datasetFailCallback);
+ this->advanceClocks(500_ms);
+
+ FibEntry payload1;
+ payload1.setPrefix("/wYs7fzYcfG");
+ FibEntry payload2;
+ payload2.setPrefix("/LKvmnzY5S");
+ this->sendDataset("/localhost/nfd/fib/list", payload1, payload2);
+ this->advanceClocks(500_ms);
+
+ BOOST_CHECK(hasResult);
+ BOOST_CHECK_EQUAL(failCodes.size(), 0);
+}
+
+BOOST_AUTO_TEST_CASE(CsInfo)
+{
+ using ndn::nfd::CsInfo;
+
+ bool hasResult = false;
+ controller.fetch<CsInfoDataset>(
+ [&hasResult] (const CsInfo& result) {
+ hasResult = true;
+ BOOST_CHECK_EQUAL(result.getNHits(), 4539);
+ },
+ datasetFailCallback);
+ this->advanceClocks(500_ms);
+
+ CsInfo payload;
+ payload.setNHits(4539);
+ this->sendDataset("/localhost/nfd/cs/info", payload);
+ this->advanceClocks(500_ms);
+
+ BOOST_CHECK(hasResult);
+ BOOST_CHECK_EQUAL(failCodes.size(), 0);
+}
+
+BOOST_AUTO_TEST_CASE(StrategyChoiceList)
+{
+ bool hasResult = false;
+ controller.fetch<StrategyChoiceDataset>(
+ [&hasResult] (const std::vector<StrategyChoice>& result) {
+ hasResult = true;
+ BOOST_CHECK_EQUAL(result.size(), 2);
+ BOOST_CHECK_EQUAL(result.front().getName(), "/8MLz6N3B");
+ },
+ datasetFailCallback);
+ this->advanceClocks(500_ms);
+
+ StrategyChoice payload1;
+ payload1.setName("/8MLz6N3B");
+ StrategyChoice payload2;
+ payload2.setName("/svqcBu0YwU");
+ this->sendDataset("/localhost/nfd/strategy-choice/list", payload1, payload2);
+ this->advanceClocks(500_ms);
+
+ BOOST_CHECK(hasResult);
+ BOOST_CHECK_EQUAL(failCodes.size(), 0);
+}
+
+BOOST_AUTO_TEST_CASE(RibList)
+{
+ bool hasResult = false;
+ controller.fetch<RibDataset>(
+ [&hasResult] (const std::vector<RibEntry>& result) {
+ hasResult = true;
+ BOOST_CHECK_EQUAL(result.size(), 2);
+ BOOST_CHECK_EQUAL(result.front().getName(), "/zXxBth97ee");
+ },
+ datasetFailCallback);
+ this->advanceClocks(500_ms);
+
+ RibEntry payload1;
+ payload1.setName("/zXxBth97ee");
+ RibEntry payload2;
+ payload2.setName("/rJ8CvUpr4G");
+ this->sendDataset("/localhost/nfd/rib/list", payload1, payload2);
+ this->advanceClocks(500_ms);
+
+ BOOST_CHECK(hasResult);
+ BOOST_CHECK_EQUAL(failCodes.size(), 0);
+}
+
+BOOST_AUTO_TEST_CASE(RibListWithOptions)
+{
+ CommandOptions options;
+ options.setPrefix("/localhop/nfd");
+ bool hasResult = false;
+ controller.fetch<RibDataset>(
+ [&hasResult] (const std::vector<RibEntry>& result) {
+ hasResult = true;
+ BOOST_CHECK_EQUAL(result.size(), 1);
+ BOOST_CHECK_EQUAL(result.front().getName(), "/e6L5K4ascd");
+ },
+ datasetFailCallback,
+ options);
+ this->advanceClocks(500_ms);
+
+ RibEntry payload;
+ payload.setName("/e6L5K4ascd");
+ this->sendDataset("/localhop/nfd/rib/list", payload);
+ this->advanceClocks(500_ms);
+
+ BOOST_CHECK(hasResult);
+ BOOST_CHECK_EQUAL(failCodes.size(), 0);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // Datasets
+
+BOOST_AUTO_TEST_SUITE_END() // TestStatusDataset
+BOOST_AUTO_TEST_SUITE_END() // Nfd
+BOOST_AUTO_TEST_SUITE_END() // Mgmt
+
+} // namespace tests
+} // namespace nfd
+} // namespace ndn
diff --git a/tests/unit/mgmt/nfd/strategy-choice.t.cpp b/tests/unit/mgmt/nfd/strategy-choice.t.cpp
new file mode 100644
index 0000000..9996541
--- /dev/null
+++ b/tests/unit/mgmt/nfd/strategy-choice.t.cpp
@@ -0,0 +1,96 @@
+/* -*- 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 "mgmt/nfd/strategy-choice.hpp"
+
+#include "boost-test.hpp"
+#include <boost/lexical_cast.hpp>
+
+namespace ndn {
+namespace nfd {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Mgmt)
+BOOST_AUTO_TEST_SUITE(Nfd)
+BOOST_AUTO_TEST_SUITE(TestStrategyChoice)
+
+BOOST_AUTO_TEST_CASE(Encode)
+{
+ StrategyChoice sc1;
+ sc1.setName("/hello/world")
+ .setStrategy("/some/non/existing/strategy/name");
+ Block wire = sc1.wireEncode();
+
+ // These octets are obtained by the snippet below.
+ // This check is intended to detect unexpected encoding change in the future.
+ // for (Buffer::const_iterator it = wire.begin(); it != wire.end(); ++it) {
+ // printf("0x%02x, ", *it);
+ // }
+ static const uint8_t expected[] = {
+ 0x80, 0x39, 0x07, 0x0e, 0x08, 0x05, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x08, 0x05, 0x77,
+ 0x6f, 0x72, 0x6c, 0x64, 0x6b, 0x27, 0x07, 0x25, 0x08, 0x04, 0x73, 0x6f, 0x6d, 0x65,
+ 0x08, 0x03, 0x6e, 0x6f, 0x6e, 0x08, 0x08, 0x65, 0x78, 0x69, 0x73, 0x74, 0x69, 0x6e,
+ 0x67, 0x08, 0x08, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x08, 0x04, 0x6e,
+ 0x61, 0x6d, 0x65
+ };
+ BOOST_CHECK_EQUAL_COLLECTIONS(expected, expected + sizeof(expected),
+ wire.begin(), wire.end());
+
+ StrategyChoice sc2(wire);
+ BOOST_CHECK_EQUAL(sc1, sc2);
+}
+
+BOOST_AUTO_TEST_CASE(Equality)
+{
+ StrategyChoice sc1, sc2;
+
+ sc1.setName("/A")
+ .setStrategy("/strategyP");
+ sc2 = sc1;
+ BOOST_CHECK_EQUAL(sc1, sc2);
+
+ sc2.setName("/B");
+ BOOST_CHECK_NE(sc1, sc2);
+
+ sc2 = sc1;
+ sc2.setStrategy("/strategyQ");
+ BOOST_CHECK_NE(sc1, sc2);
+}
+
+BOOST_AUTO_TEST_CASE(Print)
+{
+ StrategyChoice sc;
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(sc),
+ "StrategyChoice(Name: /, Strategy: /)");
+
+ sc.setName("/A")
+ .setStrategy("/strategyP");
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(sc),
+ "StrategyChoice(Name: /A, Strategy: /strategyP)");
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestStrategyChoice
+BOOST_AUTO_TEST_SUITE_END() // Nfd
+BOOST_AUTO_TEST_SUITE_END() // Mgmt
+
+} // namespace tests
+} // namespace nfd
+} // namespace ndn
diff --git a/tests/unit/mgmt/status-dataset-context.t.cpp b/tests/unit/mgmt/status-dataset-context.t.cpp
new file mode 100644
index 0000000..34e6fc1
--- /dev/null
+++ b/tests/unit/mgmt/status-dataset-context.t.cpp
@@ -0,0 +1,309 @@
+/* -*- 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 "mgmt/status-dataset-context.hpp"
+
+#include "boost-test.hpp"
+#include "make-interest-data.hpp"
+
+namespace ndn {
+namespace mgmt {
+namespace tests {
+
+using namespace ndn::tests;
+
+class StatusDatasetContextFixture
+{
+private:
+ struct SendDataArgs
+ {
+ Name dataName;
+ Block content;
+ time::milliseconds imsFresh;
+ bool isFinalBlock;
+ };
+
+protected:
+ StatusDatasetContextFixture()
+ : interest(makeInterest("/test/context/interest"))
+ , contentBlock(makeStringBlock(tlv::Content, "/test/data/content"))
+ , context(*interest,
+ [this] (const Name& dataName, const Block& content,
+ time::milliseconds imsFresh, bool isFinalBlock) {
+ SendDataArgs args{dataName, content, imsFresh, isFinalBlock};
+ sendDataHistory.push_back(args);
+ },
+ [this] (const ControlResponse& resp) {
+ sendNackHistory.push_back(resp);
+ })
+ , defaultImsFresh(1000_ms)
+ {
+ }
+
+ Name
+ makeSegmentName(size_t segmentNo) const
+ {
+ auto name = context.getPrefix();
+ return name.appendSegment(segmentNo);
+ }
+
+ Block
+ concatenateDataContent() const
+ {
+ EncodingBuffer encoder;
+ size_t valueLength = 0;
+ for (const auto& args : sendDataHistory) {
+ const auto& content = args.content;
+ valueLength += encoder.appendByteArray(content.value(), content.value_size());
+ }
+ encoder.prependVarNumber(valueLength);
+ encoder.prependVarNumber(tlv::Content);
+ return encoder.block();
+ }
+
+protected:
+ std::vector<SendDataArgs> sendDataHistory;
+ std::vector<ControlResponse> sendNackHistory;
+ shared_ptr<Interest> interest;
+ Block contentBlock;
+ mgmt::StatusDatasetContext context;
+ time::milliseconds defaultImsFresh;
+};
+
+BOOST_AUTO_TEST_SUITE(Mgmt)
+BOOST_FIXTURE_TEST_SUITE(TestStatusDatasetContext, StatusDatasetContextFixture)
+
+BOOST_AUTO_TEST_SUITE(Prefix)
+
+BOOST_AUTO_TEST_CASE(Get)
+{
+ Name dataName = context.getPrefix();
+ BOOST_CHECK(dataName[-1].isVersion());
+ BOOST_CHECK_EQUAL(dataName.getPrefix(-1), interest->getName());
+}
+
+BOOST_AUTO_TEST_CASE(SetValid)
+{
+ Name validPrefix = Name(interest->getName()).append("/valid");
+ BOOST_CHECK_NO_THROW(context.setPrefix(validPrefix));
+ BOOST_CHECK(context.getPrefix()[-1].isVersion());
+ BOOST_CHECK_EQUAL(context.getPrefix().getPrefix(-1), validPrefix);
+}
+
+BOOST_AUTO_TEST_CASE(SetInvalid)
+{
+ Name invalidPrefix = Name(interest->getName()).getPrefix(-1).append("/invalid");
+ BOOST_CHECK_THROW(context.setPrefix(invalidPrefix), std::invalid_argument);
+}
+
+BOOST_AUTO_TEST_CASE(SetValidWithAppendCalled)
+{
+ Name validPrefix = Name(interest->getName()).append("/valid");
+ context.append(contentBlock);
+ BOOST_CHECK_THROW(context.setPrefix(validPrefix), std::domain_error);
+}
+
+BOOST_AUTO_TEST_CASE(SetValidWithEndCalled)
+{
+ Name validPrefix = Name(interest->getName()).append("/valid");
+ context.end();
+ BOOST_CHECK_THROW(context.setPrefix(validPrefix), std::domain_error);
+}
+
+BOOST_AUTO_TEST_CASE(SetValidWithRejectCalled)
+{
+ Name validPrefix = Name(interest->getName()).append("/valid");
+ context.reject();
+ BOOST_CHECK_THROW(context.setPrefix(validPrefix), std::domain_error);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // Prefix
+
+BOOST_AUTO_TEST_SUITE(Expiry)
+
+BOOST_AUTO_TEST_CASE(GetAndSet)
+{
+ auto period = 9527_ms;
+ BOOST_CHECK_EQUAL(context.getExpiry(), 1000_ms);
+ BOOST_CHECK_EQUAL(context.setExpiry(period).getExpiry(), period);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // Expiry
+
+BOOST_AUTO_TEST_SUITE(Respond)
+
+BOOST_AUTO_TEST_CASE(Basic)
+{
+ BOOST_CHECK_NO_THROW(context.append(contentBlock));
+ BOOST_CHECK(sendDataHistory.empty()); // does not call end yet
+
+ BOOST_CHECK_NO_THROW(context.end());
+
+ BOOST_REQUIRE_EQUAL(sendDataHistory.size(), 1);
+ const auto& args = sendDataHistory[0];
+
+ BOOST_CHECK_EQUAL(args.dataName, makeSegmentName(0));
+ BOOST_CHECK_EQUAL(args.content.blockFromValue(), contentBlock);
+ BOOST_CHECK_EQUAL(args.imsFresh, defaultImsFresh);
+ BOOST_CHECK_EQUAL(args.isFinalBlock, true);
+}
+
+BOOST_AUTO_TEST_CASE(Large)
+{
+ static Block largeBlock = [] () -> Block {
+ EncodingBuffer encoder;
+ size_t maxBlockSize = MAX_NDN_PACKET_SIZE >> 1;
+ for (size_t i = 0; i < maxBlockSize; ++i) {
+ encoder.prependByte(1);
+ }
+ encoder.prependVarNumber(maxBlockSize);
+ encoder.prependVarNumber(tlv::Content);
+ return encoder.block();
+ }();
+
+ BOOST_CHECK_NO_THROW(context.append(largeBlock));
+ BOOST_CHECK_NO_THROW(context.end());
+
+ // two segments are generated
+ BOOST_REQUIRE_EQUAL(sendDataHistory.size(), 2);
+
+ // check segment0
+ BOOST_CHECK_EQUAL(sendDataHistory[0].dataName, makeSegmentName(0));
+ BOOST_CHECK_EQUAL(sendDataHistory[0].imsFresh, defaultImsFresh);
+ BOOST_CHECK_EQUAL(sendDataHistory[0].isFinalBlock, false);
+
+ // check segment1
+ BOOST_CHECK_EQUAL(sendDataHistory[1].dataName, makeSegmentName(1));
+ BOOST_CHECK_EQUAL(sendDataHistory[1].imsFresh, defaultImsFresh);
+ BOOST_CHECK_EQUAL(sendDataHistory[1].isFinalBlock, true);
+
+ // check data content
+ auto contentLargeBlock = concatenateDataContent();
+ BOOST_CHECK_NO_THROW(contentLargeBlock.parse());
+ BOOST_REQUIRE_EQUAL(contentLargeBlock.elements().size(), 1);
+ BOOST_CHECK_EQUAL(contentLargeBlock.elements()[0], largeBlock);
+}
+
+BOOST_AUTO_TEST_CASE(MultipleSmall)
+{
+ size_t nBlocks = 100;
+ for (size_t i = 0 ; i < nBlocks ; i ++) {
+ BOOST_CHECK_NO_THROW(context.append(contentBlock));
+ }
+ BOOST_CHECK_NO_THROW(context.end());
+
+ // check data to in-memory storage
+ BOOST_REQUIRE_EQUAL(sendDataHistory.size(), 1);
+ BOOST_CHECK_EQUAL(sendDataHistory[0].dataName, makeSegmentName(0));
+ BOOST_CHECK_EQUAL(sendDataHistory[0].imsFresh, defaultImsFresh);
+ BOOST_CHECK_EQUAL(sendDataHistory[0].isFinalBlock, true);
+
+ auto contentMultiBlocks = concatenateDataContent();
+ BOOST_CHECK_NO_THROW(contentMultiBlocks.parse());
+ BOOST_CHECK_EQUAL(contentMultiBlocks.elements().size(), nBlocks);
+ for (auto&& element : contentMultiBlocks.elements()) {
+ BOOST_CHECK_EQUAL(element, contentBlock);
+ }
+}
+
+BOOST_AUTO_TEST_SUITE_END() // Respond
+
+BOOST_AUTO_TEST_SUITE(Reject)
+
+BOOST_AUTO_TEST_CASE(Basic)
+{
+ BOOST_CHECK_NO_THROW(context.reject());
+ BOOST_REQUIRE_EQUAL(sendNackHistory.size(), 1);
+ BOOST_CHECK_EQUAL(sendNackHistory[0].getCode(), 400);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // Reject
+
+class AbnormalStateTestFixture
+{
+protected:
+ AbnormalStateTestFixture()
+ : context(Interest("/abnormal-state"), bind([]{}), bind([]{}))
+ {
+ }
+
+protected:
+ mgmt::StatusDatasetContext context;
+};
+
+BOOST_FIXTURE_TEST_SUITE(AbnormalState, AbnormalStateTestFixture)
+
+BOOST_AUTO_TEST_CASE(AppendReject)
+{
+ const uint8_t buf[] = {0x82, 0x01, 0x02};
+ BOOST_CHECK_NO_THROW(context.append(Block(buf, sizeof(buf))));
+ BOOST_CHECK_THROW(context.reject(), std::domain_error);
+}
+
+BOOST_AUTO_TEST_CASE(AppendEndReject)
+{
+ const uint8_t buf[] = {0x82, 0x01, 0x02};
+ BOOST_CHECK_NO_THROW(context.append(Block(buf, sizeof(buf))));
+ BOOST_CHECK_NO_THROW(context.end());
+ BOOST_CHECK_THROW(context.reject(), std::domain_error);
+}
+
+BOOST_AUTO_TEST_CASE(EndAppend)
+{
+ BOOST_CHECK_NO_THROW(context.end());
+ // end, append -> error
+ const uint8_t buf[] = {0x82, 0x01, 0x02};
+ BOOST_CHECK_THROW(context.append(Block(buf, sizeof(buf))), std::domain_error);
+}
+
+BOOST_AUTO_TEST_CASE(EndEnd)
+{
+ BOOST_CHECK_NO_THROW(context.end());
+ BOOST_CHECK_THROW(context.end(), std::domain_error);
+}
+
+BOOST_AUTO_TEST_CASE(EndReject)
+{
+ BOOST_CHECK_NO_THROW(context.end());
+ BOOST_CHECK_THROW(context.reject(), std::domain_error);
+}
+
+BOOST_AUTO_TEST_CASE(RejectAppend)
+{
+ BOOST_CHECK_NO_THROW(context.reject());
+ const uint8_t buf[] = {0x82, 0x01, 0x02};
+ BOOST_CHECK_THROW(context.append(Block(buf, sizeof(buf))), std::domain_error);
+}
+
+BOOST_AUTO_TEST_CASE(RejectEnd)
+{
+ BOOST_CHECK_NO_THROW(context.reject());
+ BOOST_CHECK_THROW(context.end(), std::domain_error);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // AbnormalState
+
+BOOST_AUTO_TEST_SUITE_END() // TestStatusDatasetContext
+BOOST_AUTO_TEST_SUITE_END() // Mgmt
+
+} // namespace tests
+} // namespace mgmt
+} // namespace ndn
diff --git a/tests/unit/name-component.t.cpp b/tests/unit/name-component.t.cpp
new file mode 100644
index 0000000..031ccb8
--- /dev/null
+++ b/tests/unit/name-component.t.cpp
@@ -0,0 +1,387 @@
+/* -*- 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 "name-component.hpp"
+#include "name.hpp"
+#include "util/string-helper.hpp"
+
+#include "boost-test.hpp"
+#include <boost/algorithm/string/case_conv.hpp>
+#include <boost/mpl/vector.hpp>
+
+namespace ndn {
+namespace name {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(TestNameComponent)
+
+BOOST_AUTO_TEST_SUITE(Decode)
+
+BOOST_AUTO_TEST_CASE(Generic)
+{
+ Component comp("0807 6E646E2D637878"_block);
+ BOOST_CHECK_EQUAL(comp.type(), tlv::GenericNameComponent);
+ BOOST_CHECK_EQUAL(comp.toUri(), "ndn-cxx");
+ BOOST_CHECK_EQUAL(Component::fromEscapedString("ndn-cxx"), comp);
+ BOOST_CHECK_EQUAL(Component::fromEscapedString("8=ndn-cxx"), comp);
+
+ comp.wireDecode("0800"_block);
+ BOOST_CHECK_EQUAL(comp.toUri(), "...");
+ BOOST_CHECK_EQUAL(Component::fromEscapedString("..."), comp);
+ BOOST_CHECK_EQUAL(Component::fromEscapedString("8=..."), comp);
+ BOOST_CHECK_EQUAL(Component::fromEscapedString(".%2E."), comp);
+
+ comp.wireDecode("0801 2E"_block);
+ BOOST_CHECK_EQUAL(comp.toUri(), "....");
+ BOOST_CHECK_EQUAL(Component::fromEscapedString("...."), comp);
+ BOOST_CHECK_EQUAL(Component::fromEscapedString("%2E..%2E"), comp);
+
+ comp.wireDecode("0803 2E412E"_block);
+ BOOST_CHECK_EQUAL(comp.toUri(), ".A.");
+ BOOST_CHECK_EQUAL(Component::fromEscapedString(".A."), comp);
+
+ comp.wireDecode("0807 666F6F25626172"_block);
+ BOOST_CHECK_EQUAL(comp.toUri(), "foo%25bar");
+ BOOST_CHECK_EQUAL(Component::fromEscapedString("foo%25bar"), comp);
+ BOOST_CHECK_EQUAL(Component::fromEscapedString("8=foo%25bar"), comp);
+
+ comp.wireDecode("0804 2D2E5F7E"_block);
+ BOOST_CHECK_EQUAL(comp.toUri(), "-._~");
+ BOOST_CHECK_EQUAL(Component::fromEscapedString("-._~"), comp);
+
+ comp.wireDecode("0803 393D41"_block);
+ BOOST_CHECK_EQUAL(comp.toUri(), "9%3DA");
+ BOOST_CHECK_EQUAL(Component::fromEscapedString("9%3DA"), comp);
+
+ comp = Component(":/?#[]@");
+ BOOST_CHECK_EQUAL(comp.toUri(), "%3A%2F%3F%23%5B%5D%40");
+ BOOST_CHECK_EQUAL(Component::fromEscapedString("%3A%2F%3F%23%5B%5D%40"), comp);
+
+ BOOST_CHECK_THROW(Component::fromEscapedString(""), Component::Error);
+ BOOST_CHECK_THROW(Component::fromEscapedString("."), Component::Error);
+ BOOST_CHECK_THROW(Component::fromEscapedString(".."), Component::Error);
+}
+
+static void
+testSha256(uint32_t type, const std::string& uriPrefix)
+{
+ std::string hexLower = "28bad4b5275bd392dbb670c75cf0b66f13f7942b21e80f55c0e86b374753a548";
+ std::string hexUpper = boost::to_upper_copy(hexLower);
+ std::string hexPct;
+ for (size_t i = 0; i < hexUpper.size(); i += 2) {
+ hexPct += "%" + hexUpper.substr(i, 2);
+ }
+
+ Component comp(Block(type, fromHex(hexLower)));
+ BOOST_CHECK_EQUAL(comp.type(), type);
+ BOOST_CHECK_EQUAL(comp.toUri(), uriPrefix + hexLower);
+ BOOST_CHECK_EQUAL(Component::fromEscapedString(uriPrefix + hexLower), comp);
+ BOOST_CHECK_EQUAL(Component::fromEscapedString(uriPrefix + hexUpper), comp);
+ BOOST_CHECK_EQUAL(Component::fromEscapedString(to_string(type) + "=" + hexPct), comp);
+
+ BOOST_CHECK_THROW(comp.wireDecode(Block(type, fromHex("A791806951F25C4D"))), Component::Error);
+ BOOST_CHECK_THROW(Component::fromEscapedString(uriPrefix), Component::Error);
+ BOOST_CHECK_THROW(Component::fromEscapedString(uriPrefix + "a791806951f25c4d"),
+ Component::Error);
+ BOOST_CHECK_THROW(Component::fromEscapedString(boost::to_upper_copy(uriPrefix) + hexLower),
+ Component::Error);
+}
+
+BOOST_AUTO_TEST_CASE(Digest)
+{
+ testSha256(tlv::ImplicitSha256DigestComponent, "sha256digest=");
+}
+
+BOOST_AUTO_TEST_CASE(Params)
+{
+ testSha256(tlv::ParametersSha256DigestComponent, "params-sha256=");
+}
+
+BOOST_AUTO_TEST_CASE(OtherType)
+{
+ Component comp("0907 6E646E2D637878"_block);
+ BOOST_CHECK_EQUAL(comp.type(), 0x09);
+ BOOST_CHECK_EQUAL(comp.toUri(), "9=ndn-cxx");
+ BOOST_CHECK_EQUAL(Component::fromEscapedString("9=ndn-cxx"), comp);
+
+ comp.wireDecode("FDFFFF00"_block);
+ BOOST_CHECK_EQUAL(comp.type(), 0xFFFF);
+ BOOST_CHECK_EQUAL(comp.toUri(), "65535=...");
+ BOOST_CHECK_EQUAL(Component::fromEscapedString("65535=..."), comp);
+
+ comp.wireDecode("FD576501 2E"_block);
+ BOOST_CHECK_EQUAL(comp.type(), 0x5765);
+ BOOST_CHECK_EQUAL(comp.toUri(), "22373=....");
+ BOOST_CHECK_EQUAL(Component::fromEscapedString("22373=...."), comp);
+
+ BOOST_CHECK_THROW(Component::fromEscapedString("3="), Component::Error);
+ BOOST_CHECK_THROW(Component::fromEscapedString("3=."), Component::Error);
+ BOOST_CHECK_THROW(Component::fromEscapedString("3=.."), Component::Error);
+}
+
+BOOST_AUTO_TEST_CASE(InvalidType)
+{
+ Component comp;
+ BOOST_CHECK_THROW(comp.wireDecode("0001 80"_block), Component::Error);
+ BOOST_CHECK_THROW(comp.wireDecode("FE0001000001 80"_block), Component::Error);
+
+ BOOST_CHECK_THROW(Component::fromEscapedString("0=A"), Component::Error);
+ BOOST_CHECK_THROW(Component::fromEscapedString("65536=A"), Component::Error);
+ BOOST_CHECK_THROW(Component::fromEscapedString("4294967296=A"), Component::Error);
+ BOOST_CHECK_THROW(Component::fromEscapedString("-1=A"), Component::Error);
+ BOOST_CHECK_THROW(Component::fromEscapedString("+=A"), Component::Error);
+ BOOST_CHECK_THROW(Component::fromEscapedString("=A"), Component::Error);
+ BOOST_CHECK_THROW(Component::fromEscapedString("0x1=A"), Component::Error);
+ BOOST_CHECK_THROW(Component::fromEscapedString("Z=A"), Component::Error);
+ BOOST_CHECK_THROW(Component::fromEscapedString("09=A"), Component::Error);
+ BOOST_CHECK_THROW(Component::fromEscapedString("0x3=A"), Component::Error);
+ BOOST_CHECK_THROW(Component::fromEscapedString("+9=A"), Component::Error);
+ BOOST_CHECK_THROW(Component::fromEscapedString(" 9=A"), Component::Error);
+ BOOST_CHECK_THROW(Component::fromEscapedString("9 =A"), Component::Error);
+ BOOST_CHECK_THROW(Component::fromEscapedString("9.0=A"), Component::Error);
+ BOOST_CHECK_THROW(Component::fromEscapedString("9E0=A"), Component::Error);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // Decode
+
+BOOST_AUTO_TEST_CASE(Compare)
+{
+ std::vector<Component> comps = {
+ Component("0120 0000000000000000000000000000000000000000000000000000000000000000"_block),
+ Component("0120 0000000000000000000000000000000000000000000000000000000000000001"_block),
+ Component("0120 FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"_block),
+ Component("0220 0000000000000000000000000000000000000000000000000000000000000000"_block),
+ Component("0220 0000000000000000000000000000000000000000000000000000000000000001"_block),
+ Component("0220 FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"_block),
+ Component(0x03),
+ Component("0301 44"_block),
+ Component("0301 46"_block),
+ Component("0302 4141"_block),
+ Component(),
+ Component("D"),
+ Component("F"),
+ Component("AA"),
+ Component(0x53B2),
+ Component("FD53B201 44"_block),
+ Component("FD53B201 46"_block),
+ Component("FD53B202 4141"_block),
+ };
+
+ for (size_t i = 0; i < comps.size(); ++i) {
+ for (size_t j = 0; j < comps.size(); ++j) {
+ Component lhs = comps[i];
+ Component rhs = comps[j];
+ BOOST_CHECK_EQUAL(lhs == rhs, i == j);
+ BOOST_CHECK_EQUAL(lhs != rhs, i != j);
+ BOOST_CHECK_EQUAL(lhs < rhs, i < j);
+ BOOST_CHECK_EQUAL(lhs <= rhs, i <= j);
+ BOOST_CHECK_EQUAL(lhs > rhs, i > j);
+ BOOST_CHECK_EQUAL(lhs >= rhs, i >= j);
+ }
+ }
+}
+
+BOOST_AUTO_TEST_SUITE(CreateFromIterators) // Bug 2490
+
+typedef boost::mpl::vector<
+ std::vector<uint8_t>,
+ std::list<uint8_t>,
+ std::vector<int8_t>,
+ std::list<int8_t>
+> ContainerTypes;
+
+BOOST_AUTO_TEST_CASE_TEMPLATE(ZeroOctet, T, ContainerTypes)
+{
+ T bytes;
+ Component c(bytes.begin(), bytes.end());
+ BOOST_CHECK_EQUAL(c.type(), tlv::GenericNameComponent);
+ BOOST_CHECK_EQUAL(c.value_size(), 0);
+ BOOST_CHECK_EQUAL(c.size(), 2);
+}
+
+BOOST_AUTO_TEST_CASE_TEMPLATE(OneOctet, T, ContainerTypes)
+{
+ T bytes{1};
+ Component c(0x09, bytes.begin(), bytes.end());
+ BOOST_CHECK_EQUAL(c.type(), 0x09);
+ BOOST_CHECK_EQUAL(c.value_size(), 1);
+ BOOST_CHECK_EQUAL(c.size(), 3);
+}
+
+BOOST_AUTO_TEST_CASE_TEMPLATE(FourOctets, T, ContainerTypes)
+{
+ T bytes{1, 2, 3, 4};
+ Component c(0xFCEC, bytes.begin(), bytes.end());
+ BOOST_CHECK_EQUAL(c.type(), 0xFCEC);
+ BOOST_CHECK_EQUAL(c.value_size(), 4);
+ BOOST_CHECK_EQUAL(c.size(), 8);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // CreateFromIterators
+
+BOOST_AUTO_TEST_SUITE(NamingConvention)
+
+template<typename ArgType>
+struct ConventionTest
+{
+ function<Component(ArgType)> makeComponent;
+ function<ArgType(const Component&)> getValue;
+ function<Name&(Name&, ArgType)> append;
+ Name expected;
+ ArgType value;
+ function<bool(const Component&)> isComponent;
+};
+
+class NumberWithMarker
+{
+public:
+ ConventionTest<uint64_t>
+ operator()() const
+ {
+ return {bind(&Component::fromNumberWithMarker, 0xAA, _1),
+ bind(&Component::toNumberWithMarker, _1, 0xAA),
+ bind(&Name::appendNumberWithMarker, _1, 0xAA, _2),
+ Name("/%AA%03%E8"),
+ 1000,
+ bind(&Component::isNumberWithMarker, _1, 0xAA)};
+ }
+};
+
+class Segment
+{
+public:
+ ConventionTest<uint64_t>
+ operator()() const
+ {
+ return {&Component::fromSegment,
+ bind(&Component::toSegment, _1),
+ bind(&Name::appendSegment, _1, _2),
+ Name("/%00%27%10"),
+ 10000,
+ bind(&Component::isSegment, _1)};
+ }
+};
+
+class SegmentOffset
+{
+public:
+ ConventionTest<uint64_t>
+ operator()() const
+ {
+ return {&Component::fromSegmentOffset,
+ bind(&Component::toSegmentOffset, _1),
+ bind(&Name::appendSegmentOffset, _1, _2),
+ Name("/%FB%00%01%86%A0"),
+ 100000,
+ bind(&Component::isSegmentOffset, _1)};
+ }
+};
+
+class Version
+{
+public:
+ ConventionTest<uint64_t>
+ operator()() const
+ {
+ return {&Component::fromVersion,
+ bind(&Component::toVersion, _1),
+ [] (Name& name, uint64_t version) -> Name& { return name.appendVersion(version); },
+ Name("/%FD%00%0FB%40"),
+ 1000000,
+ bind(&Component::isVersion, _1)};
+ }
+};
+
+class Timestamp
+{
+public:
+ ConventionTest<time::system_clock::TimePoint>
+ operator()() const
+ {
+ return {&Component::fromTimestamp,
+ bind(&Component::toTimestamp, _1),
+ [] (Name& name, time::system_clock::TimePoint t) -> Name& { return name.appendTimestamp(t); },
+ Name("/%FC%00%04%7BE%E3%1B%00%00"),
+ time::getUnixEpoch() + 14600_days, // 40 years
+ bind(&Component::isTimestamp, _1)};
+ }
+};
+
+class SequenceNumber
+{
+public:
+ ConventionTest<uint64_t>
+ operator()() const
+ {
+ return {&Component::fromSequenceNumber,
+ bind(&Component::toSequenceNumber, _1),
+ bind(&Name::appendSequenceNumber, _1, _2),
+ Name("/%FE%00%98%96%80"),
+ 10000000,
+ bind(&Component::isSequenceNumber, _1)};
+ }
+};
+
+using ConventionTests = boost::mpl::vector<
+ NumberWithMarker,
+ Segment,
+ SegmentOffset,
+ Version,
+ Timestamp,
+ SequenceNumber
+>;
+
+BOOST_AUTO_TEST_CASE_TEMPLATE(Convention, T, ConventionTests)
+{
+ Component invalidComponent1;
+ Component invalidComponent2("1234567890");
+
+ auto test = T()();
+
+ const Name& expected = test.expected;
+ BOOST_TEST_MESSAGE("Check " << expected[0].toUri());
+
+ BOOST_CHECK_EQUAL(expected[0].isGeneric(), true);
+
+ Component actualComponent = test.makeComponent(test.value);
+ BOOST_CHECK_EQUAL(actualComponent, expected[0]);
+
+ Name actualName;
+ test.append(actualName, test.value);
+ BOOST_CHECK_EQUAL(actualName, expected);
+
+ BOOST_CHECK_EQUAL(test.isComponent(expected[0]), true);
+ BOOST_CHECK_EQUAL(test.getValue(expected[0]), test.value);
+
+ BOOST_CHECK_EQUAL(test.isComponent(invalidComponent1), false);
+ BOOST_CHECK_EQUAL(test.isComponent(invalidComponent2), false);
+
+ BOOST_CHECK_THROW(test.getValue(invalidComponent1), Component::Error);
+ BOOST_CHECK_THROW(test.getValue(invalidComponent2), Component::Error);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // NamingConvention
+
+BOOST_AUTO_TEST_SUITE_END() // TestNameComponent
+
+} // namespace tests
+} // namespace name
+} // namespace ndn
diff --git a/tests/unit/name.t.cpp b/tests/unit/name.t.cpp
new file mode 100644
index 0000000..1e0aa1d
--- /dev/null
+++ b/tests/unit/name.t.cpp
@@ -0,0 +1,452 @@
+/* -*- 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 "name.hpp"
+
+#include "boost-test.hpp"
+#include <unordered_map>
+
+namespace ndn {
+namespace tests {
+
+using Component = name::Component;
+
+BOOST_AUTO_TEST_SUITE(TestName)
+
+// ---- encoding, decoding, and URI ----
+
+BOOST_AUTO_TEST_CASE(EncodeDecode)
+{
+ std::string uri = "/Emid/25042=P3/.../..../%1C%9F/"
+ "sha256digest=0415e3624a151850ac686c84f155f29808c0dd73819aa4a4c20be73a4d8a874c";
+ Name name(uri);
+ BOOST_CHECK_EQUAL(name.size(), 6);
+ BOOST_CHECK_EQUAL(name[0], Component("Emid"));
+ BOOST_CHECK_EQUAL(name[1], Component("FD61D2025033"_block));
+ BOOST_CHECK_EQUAL(name[2], Component(""));
+ BOOST_CHECK_EQUAL(name[3], Component("."));
+ BOOST_CHECK_EQUAL(name[4], Component("\x1C\x9F"));
+ BOOST_CHECK(name[5].isImplicitSha256Digest());
+
+ Block wire = name.wireEncode();
+ BOOST_CHECK_EQUAL(wire,
+ "0737 0804456D6964 FD61D2025033 0800 08012E 08021C9F "
+ "01200415E3624A151850AC686C84F155F29808C0DD73819AA4A4C20BE73A4D8A874C"_block);
+
+ Name decoded(wire);
+ BOOST_CHECK_EQUAL(decoded, name);
+}
+
+BOOST_AUTO_TEST_CASE(ParseUri)
+{
+ // URI with correct scheme
+ BOOST_CHECK_EQUAL(Name("ndn:/hello/world").toUri(), "/hello/world");
+
+ // URI with incorrect scheme: auto-corrected
+ BOOST_CHECK_EQUAL(Name("ncc:/hello/world").toUri(), "/hello/world");
+
+ // URI with authority: authority ignored
+ BOOST_CHECK_EQUAL(Name("//authority/hello/world").toUri(), "/hello/world");
+ BOOST_CHECK_EQUAL(Name("ndn://authority/hello/world").toUri(), "/hello/world");
+
+ // URI containing unescaped characters: auto-corrected
+ BOOST_CHECK_EQUAL(Name("/ hello\t/\tworld \r\n").toUri(), "/%20hello%09/%09world%20%0D%0A");
+ BOOST_CHECK_EQUAL(Name("/hello/world/ ").toUri(), "/hello/world/%20%20");
+ BOOST_CHECK_EQUAL(Name("/:?#[]@").toUri(), "/%3A%3F%23%5B%5D%40");
+
+ // URI not starting with '/': accepted as PartialName
+ BOOST_CHECK_EQUAL(Name("").toUri(), "/");
+ BOOST_CHECK_EQUAL(Name(" ").toUri(), "/%20");
+ BOOST_CHECK_EQUAL(Name(" /hello/world").toUri(), "/%20%20/hello/world");
+ BOOST_CHECK_EQUAL(Name("hello/world").toUri(), "/hello/world");
+
+ // URI ending with '/': auto-corrected
+ BOOST_CHECK_EQUAL(Name("/hello/world/").toUri(), "/hello/world");
+
+ // URI containing bad component: rejected
+ BOOST_CHECK_THROW(Name("/hello//world"), name::Component::Error);
+ BOOST_CHECK_THROW(Name("/hello/./world"), name::Component::Error);
+ BOOST_CHECK_THROW(Name("/hello/../world"), name::Component::Error);
+}
+
+BOOST_AUTO_TEST_CASE(DeepCopy)
+{
+ Name n1("/hello/world");
+ Name n2 = n1.deepCopy();
+
+ BOOST_CHECK_EQUAL(n1, n2);
+ BOOST_CHECK_NE(&n1.wireEncode(), &n2.wireEncode());
+
+ EncodingBuffer buffer(1024, 0);
+ n1.wireEncode(buffer);
+ Name n3(buffer.block());
+
+ BOOST_CHECK_EQUAL(n1, n3);
+ BOOST_CHECK_EQUAL(n3.wireEncode().getBuffer()->size(), 1024);
+ n3 = n3.deepCopy();
+
+ BOOST_CHECK_LT(n3.wireEncode().size(), 1024);
+ BOOST_CHECK_EQUAL(n3.wireEncode().getBuffer()->size(), n3.wireEncode().size());
+}
+
+// ---- access ----
+
+BOOST_AUTO_TEST_CASE(At)
+{
+ Name name("/hello/5=NDN");
+
+ BOOST_CHECK_EQUAL(name.at(0), name::Component("080568656C6C6F"_block));
+ BOOST_CHECK_EQUAL(name.at(1), name::Component("05034E444E"_block));
+ BOOST_CHECK_EQUAL(name.at(-1), name::Component("05034E444E"_block));
+ BOOST_CHECK_EQUAL(name.at(-2), name::Component("080568656C6C6F"_block));
+
+ BOOST_CHECK_THROW(name.at(2), Name::Error);
+ BOOST_CHECK_THROW(name.at(-3), Name::Error);
+}
+
+BOOST_AUTO_TEST_CASE(SubName)
+{
+ Name name("/hello/world");
+
+ BOOST_CHECK_EQUAL("/hello/world", name.getSubName(0));
+ BOOST_CHECK_EQUAL("/world", name.getSubName(1));
+ BOOST_CHECK_EQUAL("/hello", name.getSubName(0, 1));
+}
+
+BOOST_AUTO_TEST_CASE(SubNameNegativeIndex)
+{
+ Name name("/first/second/third/last");
+
+ BOOST_CHECK_EQUAL("/last", name.getSubName(-1));
+ BOOST_CHECK_EQUAL("/third/last", name.getSubName(-2));
+ BOOST_CHECK_EQUAL("/second", name.getSubName(-3, 1));
+}
+
+BOOST_AUTO_TEST_CASE(SubNameOutOfRangeIndexes)
+{
+ Name name("/first/second/last");
+ // No length
+ BOOST_CHECK_EQUAL("/first/second/last", name.getSubName(-10));
+ BOOST_CHECK_EQUAL("/", name.getSubName(10));
+
+ // Starting after the max position
+ BOOST_CHECK_EQUAL("/", name.getSubName(10, 1));
+ BOOST_CHECK_EQUAL("/", name.getSubName(10, 10));
+
+ // Not enough components
+ BOOST_CHECK_EQUAL("/second/last", name.getSubName(1, 10));
+ BOOST_CHECK_EQUAL("/last", name.getSubName(-1, 10));
+
+ // Start before first
+ BOOST_CHECK_EQUAL("/first/second", name.getSubName(-10, 2));
+ BOOST_CHECK_EQUAL("/first/second/last", name.getSubName(-10, 10));
+}
+
+// ---- iterators ----
+
+BOOST_AUTO_TEST_CASE(ForwardIterator)
+{
+ name::Component comps[] {
+ name::Component("A"),
+ name::Component("B"),
+ name::Component("C"),
+ name::Component("D")
+ };
+
+ Name n0;
+ BOOST_CHECK_EQUAL_COLLECTIONS(n0.begin(), n0.end(), comps, comps + 0);
+
+ Name n4("/A/B/C/D");
+ BOOST_CHECK_EQUAL_COLLECTIONS(n4.begin(), n4.end(), comps, comps + 4);
+}
+
+BOOST_AUTO_TEST_CASE(ReverseIterator)
+{
+ name::Component comps[] {
+ name::Component("D"),
+ name::Component("C"),
+ name::Component("B"),
+ name::Component("A")
+ };
+
+ Name n0;
+ BOOST_CHECK_EQUAL_COLLECTIONS(n0.rbegin(), n0.rend(), comps, comps + 0);
+
+ Name n4("/A/B/C/D");
+ BOOST_CHECK_EQUAL_COLLECTIONS(n4.rbegin(), n4.rend(), comps, comps + 4);
+}
+
+// ---- modifiers ----
+
+BOOST_AUTO_TEST_CASE(AppendComponent)
+{
+ Name name;
+ BOOST_CHECK_EQUAL(name.wireEncode(), "0700"_block);
+
+ name.append(Component("Emid"));
+ BOOST_CHECK_EQUAL(name.wireEncode(), "0706 0804456D6964"_block);
+
+ name.append(25042, reinterpret_cast<const uint8_t*>("P3"), 2);
+ BOOST_CHECK_EQUAL(name.wireEncode(), "070C 0804456D6964 FD61D2025033"_block);
+
+ name.append(reinterpret_cast<const uint8_t*>("."), 1);
+ BOOST_CHECK_EQUAL(name.wireEncode(), "070F 0804456D6964 FD61D2025033 08012E"_block);
+
+ std::vector<uint8_t> v1{0x28, 0xF0, 0xA3, 0x6B};
+ name.append(16, v1.begin(), v1.end());
+ BOOST_CHECK_EQUAL(name.wireEncode(), "0715 0804456D6964 FD61D2025033 08012E 100428F0A36B"_block);
+
+ BOOST_CHECK(!name.empty());
+ name.clear();
+ BOOST_CHECK(name.empty());
+ BOOST_CHECK_EQUAL(name.wireEncode(), "0700"_block);
+
+ name.append(v1.begin(), v1.end());
+ BOOST_CHECK_EQUAL(name.wireEncode(), "0706 080428F0A36B"_block);
+
+ name.append("xKh");
+ BOOST_CHECK_EQUAL(name.wireEncode(), "070B 080428F0A36B 0803784B68"_block);
+
+ name.append("0100"_block);
+ BOOST_CHECK_EQUAL(name.wireEncode(), "070F 080428F0A36B 0803784B68 08020100"_block);
+
+ name.append("080109"_block);
+ BOOST_CHECK_EQUAL(name.wireEncode(), "0712 080428F0A36B 0803784B68 08020100 080109"_block);
+}
+
+BOOST_AUTO_TEST_CASE(AppendPartialName)
+{
+ Name name("/A/B");
+ name.append(PartialName("/6=C/D"))
+ .append(PartialName("/E"));
+ BOOST_CHECK_EQUAL(name.wireEncode(), "070F 080141 080142 060143 080144 080145"_block);
+}
+
+BOOST_AUTO_TEST_CASE(AppendNumber)
+{
+ Name name;
+ for (uint32_t i = 0; i < 10; i++) {
+ name.appendNumber(i);
+ }
+ BOOST_CHECK_EQUAL(name.size(), 10);
+
+ for (uint32_t i = 0; i < 10; i++) {
+ BOOST_CHECK_EQUAL(name[i].toNumber(), i);
+ }
+}
+
+BOOST_AUTO_TEST_CASE(Markers)
+{
+ // TestNameComponent/NamingConvention provides additional coverage for these methods,
+ // including verifications of the wire format.
+
+ Name name;
+ uint64_t number;
+
+ BOOST_REQUIRE_NO_THROW(number = name.appendSegment(30923).at(-1).toSegment());
+ BOOST_CHECK_EQUAL(number, 30923);
+
+ BOOST_REQUIRE_NO_THROW(number = name.appendSegmentOffset(589).at(-1).toSegmentOffset());
+ BOOST_CHECK_EQUAL(number, 589);
+
+ BOOST_REQUIRE_NO_THROW(number = name.appendVersion().at(-1).toVersion());
+
+ BOOST_REQUIRE_NO_THROW(number = name.appendVersion(25912).at(-1).toVersion());
+ BOOST_CHECK_EQUAL(number, 25912);
+
+ const time::system_clock::TimePoint tp = time::system_clock::now();
+ time::system_clock::TimePoint tp2;
+ BOOST_REQUIRE_NO_THROW(tp2 = name.appendTimestamp(tp).at(-1).toTimestamp());
+ BOOST_CHECK_LE(std::abs(time::duration_cast<time::microseconds>(tp2 - tp).count()), 1);
+
+ BOOST_REQUIRE_NO_THROW(number = name.appendSequenceNumber(11676).at(-1).toSequenceNumber());
+ BOOST_CHECK_EQUAL(number, 11676);
+}
+
+// ---- algorithms ----
+
+BOOST_AUTO_TEST_CASE(GetSuccessor)
+{
+ BOOST_CHECK_EQUAL(Name().getSuccessor(), "/sha256digest=0000000000000000000000000000000000000000000000000000000000000000");
+ BOOST_CHECK_EQUAL(Name("/sha256digest=0000000000000000000000000000000000000000000000000000000000000000").getSuccessor(),
+ "/sha256digest=0000000000000000000000000000000000000000000000000000000000000001");
+ BOOST_CHECK_EQUAL(Name("/sha256digest=ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").getSuccessor(),
+ "/params-sha256=0000000000000000000000000000000000000000000000000000000000000000");
+ BOOST_CHECK_EQUAL(Name("/params-sha256=0000000000000000000000000000000000000000000000000000000000000000").getSuccessor(),
+ "/params-sha256=0000000000000000000000000000000000000000000000000000000000000001");
+ BOOST_CHECK_EQUAL(Name("/params-sha256=ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").getSuccessor(),
+ "/3=...");
+ BOOST_CHECK_EQUAL(Name("/P/A").getSuccessor(), "/P/B");
+ BOOST_CHECK_EQUAL(Name("/P/AAA").getSuccessor(), "/P/AAB");
+ BOOST_CHECK_EQUAL(Name("/Q/...").getSuccessor(), "/Q/%00");
+ BOOST_CHECK_EQUAL(Name("/Q/%FF").getSuccessor(), "/Q/%00%00");
+ BOOST_CHECK_EQUAL(Name("/Q/%FE%FF").getSuccessor(), "/Q/%FF%00");
+ BOOST_CHECK_EQUAL(Name("/Q/%FF%FF").getSuccessor(), "/Q/%00%00%00");
+ BOOST_CHECK_EQUAL(Name("/P/3=A").getSuccessor(), "/P/3=B");
+ BOOST_CHECK_EQUAL(Name("/P/3=AAA").getSuccessor(), "/P/3=AAB");
+ BOOST_CHECK_EQUAL(Name("/Q/3=...").getSuccessor(), "/Q/3=%00");
+ BOOST_CHECK_EQUAL(Name("/Q/3=%FF").getSuccessor(), "/Q/3=%00%00");
+ BOOST_CHECK_EQUAL(Name("/Q/3=%FE%FF").getSuccessor(), "/Q/3=%FF%00");
+ BOOST_CHECK_EQUAL(Name("/Q/3=%FF%FF").getSuccessor(), "/Q/3=%00%00%00");
+}
+
+BOOST_AUTO_TEST_CASE(IsPrefixOf)
+{
+ BOOST_CHECK(Name("/").isPrefixOf("/"));
+ BOOST_CHECK(Name("/").isPrefixOf("/sha256digest=0000000000000000000000000000000000000000000000000000000000000000"));
+ BOOST_CHECK(Name("/").isPrefixOf("/params-sha256=ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"));
+ BOOST_CHECK(Name("/").isPrefixOf("/3=D"));
+ BOOST_CHECK(Name("/").isPrefixOf("/F"));
+ BOOST_CHECK(Name("/").isPrefixOf("/21426=AA"));
+
+ BOOST_CHECK(Name("/B").isPrefixOf("/B"));
+ BOOST_CHECK(Name("/B").isPrefixOf("/B/sha256digest=0000000000000000000000000000000000000000000000000000000000000000"));
+ BOOST_CHECK(Name("/").isPrefixOf("/B/params-sha256=ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"));
+ BOOST_CHECK(Name("/B").isPrefixOf("/B/3=D"));
+ BOOST_CHECK(Name("/B").isPrefixOf("/B/F"));
+ BOOST_CHECK(Name("/B").isPrefixOf("/B/21426=AA"));
+
+ BOOST_CHECK(!Name("/C").isPrefixOf("/"));
+ BOOST_CHECK(!Name("/C").isPrefixOf("/sha256digest=0000000000000000000000000000000000000000000000000000000000000000"));
+ BOOST_CHECK(Name("/").isPrefixOf("/params-sha256=ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"));
+ BOOST_CHECK(!Name("/C").isPrefixOf("/3=D"));
+ BOOST_CHECK(!Name("/C").isPrefixOf("/F"));
+ BOOST_CHECK(!Name("/C").isPrefixOf("/21426=AA"));
+}
+
+BOOST_AUTO_TEST_CASE(CompareOp)
+{
+ std::vector<Name> names = {
+ Name("/"),
+ Name("/sha256digest=0000000000000000000000000000000000000000000000000000000000000000"),
+ Name("/sha256digest=0000000000000000000000000000000000000000000000000000000000000001"),
+ Name("/sha256digest=FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"),
+ Name("/params-sha256=0000000000000000000000000000000000000000000000000000000000000000"),
+ Name("/params-sha256=0000000000000000000000000000000000000000000000000000000000000001"),
+ Name("/params-sha256=FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"),
+ Name("/3=..."),
+ Name("/3=D"),
+ Name("/3=F"),
+ Name("/3=AA"),
+ Name("/..."),
+ Name("/D"),
+ Name("/D/sha256digest=0000000000000000000000000000000000000000000000000000000000000000"),
+ Name("/D/sha256digest=0000000000000000000000000000000000000000000000000000000000000001"),
+ Name("/D/sha256digest=FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"),
+ Name("/D/params-sha256=0000000000000000000000000000000000000000000000000000000000000000"),
+ Name("/D/params-sha256=0000000000000000000000000000000000000000000000000000000000000001"),
+ Name("/D/params-sha256=FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"),
+ Name("/D/3=..."),
+ Name("/D/3=D"),
+ Name("/D/3=F"),
+ Name("/D/3=AA"),
+ Name("/D/..."),
+ Name("/D/D"),
+ Name("/D/F"),
+ Name("/D/AA"),
+ Name("/D/21426=..."),
+ Name("/D/21426=D"),
+ Name("/D/21426=F"),
+ Name("/D/21426=AA"),
+ Name("/F"),
+ Name("/AA"),
+ Name("/21426=..."),
+ Name("/21426=D"),
+ Name("/21426=F"),
+ Name("/21426=AA"),
+ };
+
+ for (size_t i = 0; i < names.size(); ++i) {
+ for (size_t j = 0; j < names.size(); ++j) {
+ Name lhs = names[i];
+ Name rhs = names[j];
+ BOOST_CHECK_EQUAL(lhs == rhs, i == j);
+ BOOST_CHECK_EQUAL(lhs != rhs, i != j);
+ BOOST_CHECK_EQUAL(lhs < rhs, i < j);
+ BOOST_CHECK_EQUAL(lhs <= rhs, i <= j);
+ BOOST_CHECK_EQUAL(lhs > rhs, i > j);
+ BOOST_CHECK_EQUAL(lhs >= rhs, i >= j);
+ }
+ }
+}
+
+BOOST_AUTO_TEST_CASE(CompareFunc)
+{
+ BOOST_CHECK_EQUAL(Name("/A") .compare(Name("/A")), 0);
+ BOOST_CHECK_LT (Name("/A") .compare(Name("/B")), 0);
+ BOOST_CHECK_GT (Name("/B") .compare(Name("/A")), 0);
+ BOOST_CHECK_LT (Name("/A") .compare(Name("/AA")), 0);
+ BOOST_CHECK_GT (Name("/AA") .compare(Name("/A")), 0);
+ BOOST_CHECK_LT (Name("/A") .compare(Name("/A/C")), 0);
+ BOOST_CHECK_GT (Name("/A/C").compare(Name("/A")), 0);
+
+ BOOST_CHECK_EQUAL(Name("/Z/A/Y") .compare(1, 1, Name("/A")), 0);
+ BOOST_CHECK_LT (Name("/Z/A/Y") .compare(1, 1, Name("/B")), 0);
+ BOOST_CHECK_GT (Name("/Z/B/Y") .compare(1, 1, Name("/A")), 0);
+ BOOST_CHECK_LT (Name("/Z/A/Y") .compare(1, 1, Name("/AA")), 0);
+ BOOST_CHECK_GT (Name("/Z/AA/Y") .compare(1, 1, Name("/A")), 0);
+ BOOST_CHECK_LT (Name("/Z/A/Y") .compare(1, 1, Name("/A/C")), 0);
+ BOOST_CHECK_GT (Name("/Z/A/C/Y").compare(1, 2, Name("/A")), 0);
+
+ BOOST_CHECK_EQUAL(Name("/Z/A") .compare(1, Name::npos, Name("/A")), 0);
+ BOOST_CHECK_LT (Name("/Z/A") .compare(1, Name::npos, Name("/B")), 0);
+ BOOST_CHECK_GT (Name("/Z/B") .compare(1, Name::npos, Name("/A")), 0);
+ BOOST_CHECK_LT (Name("/Z/A") .compare(1, Name::npos, Name("/AA")), 0);
+ BOOST_CHECK_GT (Name("/Z/AA") .compare(1, Name::npos, Name("/A")), 0);
+ BOOST_CHECK_LT (Name("/Z/A") .compare(1, Name::npos, Name("/A/C")), 0);
+ BOOST_CHECK_GT (Name("/Z/A/C").compare(1, Name::npos, Name("/A")), 0);
+
+ BOOST_CHECK_EQUAL(Name("/Z/A/Y") .compare(1, 1, Name("/X/A/W"), 1, 1), 0);
+ BOOST_CHECK_LT (Name("/Z/A/Y") .compare(1, 1, Name("/X/B/W"), 1, 1), 0);
+ BOOST_CHECK_GT (Name("/Z/B/Y") .compare(1, 1, Name("/X/A/W"), 1, 1), 0);
+ BOOST_CHECK_LT (Name("/Z/A/Y") .compare(1, 1, Name("/X/AA/W"), 1, 1), 0);
+ BOOST_CHECK_GT (Name("/Z/AA/Y") .compare(1, 1, Name("/X/A/W"), 1, 1), 0);
+ BOOST_CHECK_LT (Name("/Z/A/Y") .compare(1, 1, Name("/X/A/C/W"), 1, 2), 0);
+ BOOST_CHECK_GT (Name("/Z/A/C/Y").compare(1, 2, Name("/X/A/W"), 1, 1), 0);
+
+ BOOST_CHECK_EQUAL(Name("/Z/A/Y") .compare(1, 1, Name("/X/A"), 1), 0);
+ BOOST_CHECK_LT (Name("/Z/A/Y") .compare(1, 1, Name("/X/B"), 1), 0);
+ BOOST_CHECK_GT (Name("/Z/B/Y") .compare(1, 1, Name("/X/A"), 1), 0);
+ BOOST_CHECK_LT (Name("/Z/A/Y") .compare(1, 1, Name("/X/AA"), 1), 0);
+ BOOST_CHECK_GT (Name("/Z/AA/Y") .compare(1, 1, Name("/X/A"), 1), 0);
+ BOOST_CHECK_LT (Name("/Z/A/Y") .compare(1, 1, Name("/X/A/C"), 1), 0);
+ BOOST_CHECK_GT (Name("/Z/A/C/Y").compare(1, 2, Name("/X/A"), 1), 0);
+}
+
+BOOST_AUTO_TEST_CASE(UnorderedMap)
+{
+ std::unordered_map<Name, int> map;
+ Name name1("/1");
+ Name name2("/2");
+ Name name3("/3");
+ map[name1] = 1;
+ map[name2] = 2;
+ map[name3] = 3;
+
+ BOOST_CHECK_EQUAL(map[name1], 1);
+ BOOST_CHECK_EQUAL(map[name2], 2);
+ BOOST_CHECK_EQUAL(map[name3], 3);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestName
+
+} // namespace tests
+} // namespace ndn
diff --git a/tests/unit/ndebug.t.cpp b/tests/unit/ndebug.t.cpp
new file mode 100644
index 0000000..b7219c2
--- /dev/null
+++ b/tests/unit/ndebug.t.cpp
@@ -0,0 +1,55 @@
+/* -*- 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 "common.hpp"
+
+#include "boost-test.hpp"
+
+namespace ndn {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(TestNdebug)
+
+BOOST_AUTO_TEST_CASE(AssertFalse)
+{
+#ifndef _DEBUG
+ // in release builds, assertion shouldn't execute
+ BOOST_ASSERT(false);
+#endif
+ // Trivial check to avoid "test case did not check any assertions" message from Boost.Test
+ BOOST_CHECK(true);
+}
+
+BOOST_AUTO_TEST_CASE(SideEffect)
+{
+ int a = 1;
+ BOOST_ASSERT((a = 2) > 0);
+#ifdef _DEBUG
+ BOOST_CHECK_EQUAL(a, 2);
+#else
+ BOOST_CHECK_EQUAL(a, 1);
+#endif
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestNdebug
+
+} // namespace tests
+} // namespace ndn
diff --git a/tests/unit/net/collect-netifs.cpp b/tests/unit/net/collect-netifs.cpp
new file mode 100644
index 0000000..ca8e6ef
--- /dev/null
+++ b/tests/unit/net/collect-netifs.cpp
@@ -0,0 +1,64 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-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 "collect-netifs.hpp"
+#include "net/network-monitor.hpp"
+
+#include <boost/asio/io_service.hpp>
+
+namespace ndn {
+namespace net {
+namespace tests {
+
+std::vector<shared_ptr<const NetworkInterface>>
+collectNetworkInterfaces(bool allowCached)
+{
+ static std::vector<shared_ptr<const NetworkInterface>> cached;
+ // cached.empty() indicates there's no cached list of netifs.
+ // Although it could also mean a system without any network interface, this situation is rare
+ // because the loopback interface is present on almost all systems.
+
+ if (!allowCached || cached.empty()) {
+ boost::asio::io_service io;
+ NetworkMonitor netmon(io);
+ if ((netmon.getCapabilities() & NetworkMonitor::CAP_ENUM) == 0) {
+ BOOST_THROW_EXCEPTION(NetworkMonitor::Error("NetworkMonitor::CAP_ENUM is unavailable"));
+ }
+
+ netmon.onEnumerationCompleted.connect([&io] { io.stop(); });
+ io.run();
+ io.reset();
+
+ cached = netmon.listNetworkInterfaces();
+ }
+
+ return cached;
+}
+
+} // namespace tests
+} // namespace net
+} // namespace ndn
diff --git a/tests/unit/net/collect-netifs.hpp b/tests/unit/net/collect-netifs.hpp
new file mode 100644
index 0000000..322e323
--- /dev/null
+++ b/tests/unit/net/collect-netifs.hpp
@@ -0,0 +1,52 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-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 NFD_TESTS_UNIT_TESTS_NET_COLLECT_NETIFS_HPP
+#define NFD_TESTS_UNIT_TESTS_NET_COLLECT_NETIFS_HPP
+
+#include "common.hpp"
+#include "net/network-interface.hpp"
+
+#include <vector>
+
+namespace ndn {
+namespace net {
+namespace tests {
+
+/** \brief Collect information about network interfaces
+ * \param allowCached if true, previously collected information can be returned
+ * \note This function is blocking if \p allowCached is false or no previous information exists
+ * \throw ndn::net::NetworkMonitor::Error NetworkMonitor::CAP_ENUM is unavailable
+ */
+std::vector<shared_ptr<const NetworkInterface>>
+collectNetworkInterfaces(bool allowCached = true);
+
+} // namespace tests
+} // namespace net
+} // namespace ndn
+
+#endif // NFD_TESTS_UNIT_TESTS_NET_COLLECT_NETIFS_HPP
diff --git a/tests/unit/net/dns.t.cpp b/tests/unit/net/dns.t.cpp
new file mode 100644
index 0000000..e4afa8e
--- /dev/null
+++ b/tests/unit/net/dns.t.cpp
@@ -0,0 +1,185 @@
+/* -*- 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 "net/dns.hpp"
+
+#include "boost-test.hpp"
+#include "network-configuration-detector.hpp"
+
+#include <boost/asio/io_service.hpp>
+
+namespace ndn {
+namespace dns {
+namespace tests {
+
+using boost::asio::ip::address_v4;
+using boost::asio::ip::address_v6;
+
+class DnsFixture
+{
+public:
+ DnsFixture()
+ : m_nFailures(0)
+ , m_nSuccesses(0)
+ {
+ }
+
+ void
+ onSuccess(const IpAddress& resolvedAddress,
+ const IpAddress& expectedAddress,
+ bool isValid,
+ bool shouldCheckAddress = false)
+ {
+ ++m_nSuccesses;
+
+ if (!isValid) {
+ BOOST_FAIL("Resolved to " + resolvedAddress.to_string() + ", but should have failed");
+ }
+
+ BOOST_CHECK_EQUAL(resolvedAddress.is_v4(), expectedAddress.is_v4());
+
+ // checking address is not deterministic and should be enabled only
+ // if only one IP address will be returned by resolution
+ if (shouldCheckAddress) {
+ BOOST_CHECK_EQUAL(resolvedAddress, expectedAddress);
+ }
+ }
+
+ void
+ onFailure(bool isValid)
+ {
+ ++m_nFailures;
+
+ if (!isValid) {
+ BOOST_FAIL("Resolution should not have failed");
+ }
+
+ BOOST_CHECK_MESSAGE(true, "Resolution failed as expected");
+ }
+
+protected:
+ uint32_t m_nFailures;
+ uint32_t m_nSuccesses;
+ boost::asio::io_service m_ioService;
+};
+
+BOOST_AUTO_TEST_SUITE(Net)
+BOOST_FIXTURE_TEST_SUITE(TestDns, DnsFixture)
+
+BOOST_AUTO_TEST_CASE(Asynchronous)
+{
+ SKIP_IF_IP_UNAVAILABLE();
+
+ asyncResolve("nothost.nothost.nothost.arpa",
+ bind(&DnsFixture::onSuccess, this, _1, IpAddress(address_v4()), false, false),
+ bind(&DnsFixture::onFailure, this, true),
+ m_ioService); // should fail
+
+ m_ioService.run();
+ BOOST_CHECK_EQUAL(m_nFailures, 1);
+ BOOST_CHECK_EQUAL(m_nSuccesses, 0);
+}
+
+BOOST_AUTO_TEST_CASE(AsynchronousV4)
+{
+ SKIP_IF_IPV4_UNAVAILABLE();
+
+ asyncResolve("192.0.2.1",
+ bind(&DnsFixture::onSuccess, this, _1,
+ IpAddress(address_v4::from_string("192.0.2.1")), true, true),
+ bind(&DnsFixture::onFailure, this, false),
+ m_ioService);
+
+ m_ioService.run();
+ BOOST_CHECK_EQUAL(m_nFailures, 0);
+ BOOST_CHECK_EQUAL(m_nSuccesses, 1);
+}
+
+BOOST_AUTO_TEST_CASE(AsynchronousV6)
+{
+ SKIP_IF_IPV6_UNAVAILABLE();
+
+ asyncResolve("ipv6.google.com", // only IPv6 address should be available
+ bind(&DnsFixture::onSuccess, this, _1, IpAddress(address_v6()), true, false),
+ bind(&DnsFixture::onFailure, this, false),
+ m_ioService);
+
+ asyncResolve("2001:db8:3f9:0:3025:ccc5:eeeb:86d3",
+ bind(&DnsFixture::onSuccess, this, _1,
+ IpAddress(address_v6::from_string("2001:db8:3f9:0:3025:ccc5:eeeb:86d3")),
+ true, true),
+ bind(&DnsFixture::onFailure, this, false),
+ m_ioService);
+
+ m_ioService.run();
+ BOOST_CHECK_EQUAL(m_nFailures, 0);
+ BOOST_CHECK_EQUAL(m_nSuccesses, 2);
+}
+
+BOOST_AUTO_TEST_CASE(AsynchronousV4AndV6)
+{
+ SKIP_IF_IPV4_UNAVAILABLE();
+ SKIP_IF_IPV6_UNAVAILABLE();
+
+ asyncResolve("www.named-data.net",
+ bind(&DnsFixture::onSuccess, this, _1, IpAddress(address_v4()), true, false),
+ bind(&DnsFixture::onFailure, this, false),
+ m_ioService, Ipv4Only());
+
+ asyncResolve("a.root-servers.net",
+ bind(&DnsFixture::onSuccess, this, _1, IpAddress(address_v4()), true, false),
+ bind(&DnsFixture::onFailure, this, false),
+ m_ioService, Ipv4Only()); // request IPv4 address
+
+ asyncResolve("a.root-servers.net",
+ bind(&DnsFixture::onSuccess, this, _1, IpAddress(address_v6()), true, false),
+ bind(&DnsFixture::onFailure, this, false),
+ m_ioService, Ipv6Only()); // request IPv6 address
+
+ asyncResolve("ipv6.google.com", // only IPv6 address should be available
+ bind(&DnsFixture::onSuccess, this, _1, IpAddress(address_v6()), true, false),
+ bind(&DnsFixture::onFailure, this, false),
+ m_ioService, Ipv6Only());
+
+ asyncResolve("ipv6.google.com", // only IPv6 address should be available
+ bind(&DnsFixture::onSuccess, this, _1, IpAddress(address_v6()), false, false),
+ bind(&DnsFixture::onFailure, this, true),
+ m_ioService, Ipv4Only()); // should fail
+
+ m_ioService.run();
+ BOOST_CHECK_EQUAL(m_nFailures, 1);
+ BOOST_CHECK_EQUAL(m_nSuccesses, 4);
+}
+
+BOOST_AUTO_TEST_CASE(Synchronous)
+{
+ SKIP_IF_IP_UNAVAILABLE();
+
+ IpAddress address = syncResolve("www.named-data.net", m_ioService);
+ BOOST_CHECK(address.is_v4() || address.is_v6());
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestDns
+BOOST_AUTO_TEST_SUITE_END() // Net
+
+} // namespace tests
+} // namespace dns
+} // namespace ndn
diff --git a/tests/unit/net/ethernet.t.cpp b/tests/unit/net/ethernet.t.cpp
new file mode 100644
index 0000000..4f76dbb
--- /dev/null
+++ b/tests/unit/net/ethernet.t.cpp
@@ -0,0 +1,117 @@
+/* -*- 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 "net/ethernet.hpp"
+
+#include "boost-test.hpp"
+
+namespace ndn {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Net)
+BOOST_AUTO_TEST_SUITE(TestEthernet)
+
+BOOST_AUTO_TEST_CASE(Basic)
+{
+ ethernet::Address a;
+ BOOST_CHECK(a.isNull());
+
+ a = {0x01, 0x23, 0x45, 0x67, 0x89, 0xAB};
+ ethernet::Address b(0x01, 0x23, 0x45, 0x67, 0x89, 0xAB);
+ const uint8_t bytes[] = {0x01, 0x23, 0x45, 0x67, 0x89, 0xAB};
+ ethernet::Address c(bytes);
+ ethernet::Address d(a);
+ ethernet::Address e;
+ e = a;
+
+ BOOST_CHECK_EQUAL(a, b);
+ BOOST_CHECK_EQUAL(a, c);
+ BOOST_CHECK_EQUAL(a, d);
+ BOOST_CHECK_EQUAL(a, e);
+
+ BOOST_CHECK(ethernet::getBroadcastAddress().isBroadcast());
+ BOOST_CHECK(ethernet::getDefaultMulticastAddress().isMulticast());
+}
+
+BOOST_AUTO_TEST_CASE(ToString)
+{
+ BOOST_CHECK_EQUAL(ethernet::Address().toString('-'),
+ "00-00-00-00-00-00");
+ BOOST_CHECK_EQUAL(ethernet::getBroadcastAddress().toString(),
+ "ff:ff:ff:ff:ff:ff");
+ BOOST_CHECK_EQUAL(ethernet::Address(0x01, 0x23, 0x45, 0x67, 0x89, 0xAB).toString('-'),
+ "01-23-45-67-89-ab");
+ BOOST_CHECK_EQUAL(ethernet::Address(0x01, 0x23, 0x45, 0x67, 0x89, 0xAB).toString(),
+ "01:23:45:67:89:ab");
+}
+
+BOOST_AUTO_TEST_CASE(FromString)
+{
+ BOOST_CHECK_EQUAL(ethernet::Address::fromString("0:0:0:0:0:0"),
+ ethernet::Address());
+ BOOST_CHECK_EQUAL(ethernet::Address::fromString("ff-ff-ff-ff-ff-ff"),
+ ethernet::getBroadcastAddress());
+ BOOST_CHECK_EQUAL(ethernet::Address::fromString("de:ad:be:ef:1:2"),
+ ethernet::Address(0xde, 0xad, 0xbe, 0xef, 0x01, 0x02));
+ BOOST_CHECK_EQUAL(ethernet::Address::fromString("DE:AD:BE:EF:1:2"),
+ ethernet::Address(0xde, 0xad, 0xbe, 0xef, 0x01, 0x02));
+
+ // malformed inputs
+ BOOST_CHECK_EQUAL(ethernet::Address::fromString("01.23.45.67.89.ab"),
+ ethernet::Address());
+ BOOST_CHECK_EQUAL(ethernet::Address::fromString("01:23:45 :67:89:ab"),
+ ethernet::Address());
+ BOOST_CHECK_EQUAL(ethernet::Address::fromString("01:23:45:67:89::1"),
+ ethernet::Address());
+ BOOST_CHECK_EQUAL(ethernet::Address::fromString("01-23-45-67-89"),
+ ethernet::Address());
+ BOOST_CHECK_EQUAL(ethernet::Address::fromString("01:23:45:67:89:ab:cd"),
+ ethernet::Address());
+ BOOST_CHECK_EQUAL(ethernet::Address::fromString("01:23:45:67-89-ab"),
+ ethernet::Address());
+ BOOST_CHECK_EQUAL(ethernet::Address::fromString("qw-er-ty-12-34-56"),
+ ethernet::Address());
+ BOOST_CHECK_EQUAL(ethernet::Address::fromString("this-is-not-an-ethernet-address"),
+ ethernet::Address());
+ BOOST_CHECK_EQUAL(ethernet::Address::fromString("foobar"),
+ ethernet::Address());
+ BOOST_CHECK_EQUAL(ethernet::Address::fromString(""),
+ ethernet::Address());
+}
+
+BOOST_AUTO_TEST_CASE(StdHash)
+{
+ // make sure we can use ethernet::Address as key type in std::unordered_map
+ std::hash<ethernet::Address> h;
+ BOOST_CHECK_NO_THROW(h(ethernet::getDefaultMulticastAddress()));
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestEthernet
+BOOST_AUTO_TEST_SUITE_END() // Net
+
+} // namespace tests
+} // namespace ndn
diff --git a/tests/unit/net/face-uri.t.cpp b/tests/unit/net/face-uri.t.cpp
new file mode 100644
index 0000000..71d76b1
--- /dev/null
+++ b/tests/unit/net/face-uri.t.cpp
@@ -0,0 +1,647 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2013-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 "net/face-uri.hpp"
+
+#include "boost-test.hpp"
+#include "collect-netifs.hpp"
+#include "network-configuration-detector.hpp"
+
+namespace ndn {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Net)
+BOOST_AUTO_TEST_SUITE(TestFaceUri)
+
+class CanonizeFixture : noncopyable
+{
+protected:
+ void
+ addTest(const std::string& request, bool shouldSucceed, const std::string& expectedUri)
+ {
+ ++m_nPending;
+ auto tc = make_shared<CanonizeTestCase>(request, shouldSucceed, expectedUri);
+
+ FaceUri uri(request);
+ uri.canonize(bind(&CanonizeFixture::onCanonizeSuccess, this, tc, _1),
+ bind(&CanonizeFixture::onCanonizeFailure, this, tc, _1),
+ m_io, 10_s);
+ }
+
+ void
+ runTests()
+ {
+ m_io.run();
+ BOOST_CHECK_EQUAL(m_nPending, 0);
+ }
+
+private:
+ class CanonizeTestCase
+ {
+ public:
+ CanonizeTestCase(const std::string& request, bool shouldSucceed, const std::string& expectedUri)
+ : m_expectedUri(expectedUri)
+ , m_message(request + " should " + (shouldSucceed ? "succeed" : "fail"))
+ , m_shouldSucceed(shouldSucceed)
+ , m_isCompleted(false)
+ {
+ }
+
+ public:
+ std::string m_expectedUri;
+ std::string m_message;
+ bool m_shouldSucceed;
+ bool m_isCompleted;
+ };
+
+ void
+ onCanonizeSuccess(const shared_ptr<CanonizeTestCase>& tc, const FaceUri& canonicalUri)
+ {
+ BOOST_CHECK_EQUAL(tc->m_isCompleted, false);
+ tc->m_isCompleted = true;
+ --m_nPending;
+
+ BOOST_CHECK_MESSAGE(tc->m_shouldSucceed, tc->m_message);
+ if (tc->m_shouldSucceed) {
+ BOOST_CHECK_EQUAL(canonicalUri.toString(), tc->m_expectedUri);
+ }
+ }
+
+ void
+ onCanonizeFailure(const shared_ptr<CanonizeTestCase>& tc, const std::string& reason)
+ {
+ BOOST_CHECK_EQUAL(tc->m_isCompleted, false);
+ tc->m_isCompleted = true;
+ --m_nPending;
+
+ BOOST_CHECK_MESSAGE(!tc->m_shouldSucceed, tc->m_message);
+ }
+
+private:
+ boost::asio::io_service m_io;
+ ssize_t m_nPending = 0;
+};
+
+BOOST_AUTO_TEST_CASE(ParseInternal)
+{
+ FaceUri uri;
+
+ BOOST_CHECK(uri.parse("internal://"));
+ BOOST_CHECK_EQUAL(uri.getScheme(), "internal");
+ BOOST_CHECK_EQUAL(uri.getHost(), "");
+ BOOST_CHECK_EQUAL(uri.getPort(), "");
+ BOOST_CHECK_EQUAL(uri.getPath(), "");
+
+ BOOST_CHECK_EQUAL(uri.parse("internal:"), false);
+ BOOST_CHECK_EQUAL(uri.parse("internal:/"), false);
+}
+
+BOOST_AUTO_TEST_CASE(ParseUdp)
+{
+ FaceUri uri("udp://hostname:6363");
+ BOOST_CHECK_THROW(FaceUri("udp//hostname:6363"), FaceUri::Error);
+ BOOST_CHECK_THROW(FaceUri("udp://hostname:port"), FaceUri::Error);
+
+ BOOST_CHECK_EQUAL(uri.parse("udp//hostname:6363"), false);
+
+ BOOST_CHECK(uri.parse("udp://hostname:80"));
+ BOOST_CHECK_EQUAL(uri.getScheme(), "udp");
+ BOOST_CHECK_EQUAL(uri.getHost(), "hostname");
+ BOOST_CHECK_EQUAL(uri.getPort(), "80");
+ BOOST_CHECK_EQUAL(uri.getPath(), "");
+
+ BOOST_CHECK(uri.parse("udp4://192.0.2.1:20"));
+ BOOST_CHECK_EQUAL(uri.getScheme(), "udp4");
+ BOOST_CHECK_EQUAL(uri.getHost(), "192.0.2.1");
+ BOOST_CHECK_EQUAL(uri.getPort(), "20");
+ BOOST_CHECK_EQUAL(uri.getPath(), "");
+
+ BOOST_CHECK(uri.parse("udp6://[2001:db8:3f9:0::1]:6363"));
+ BOOST_CHECK_EQUAL(uri.getScheme(), "udp6");
+ BOOST_CHECK_EQUAL(uri.getHost(), "2001:db8:3f9:0::1");
+ BOOST_CHECK_EQUAL(uri.getPort(), "6363");
+ BOOST_CHECK_EQUAL(uri.getPath(), "");
+
+ BOOST_CHECK(uri.parse("udp6://[2001:db8:3f9:0:3025:ccc5:eeeb:86d3]:6363"));
+ BOOST_CHECK_EQUAL(uri.getScheme(), "udp6");
+ BOOST_CHECK_EQUAL(uri.getHost(), "2001:db8:3f9:0:3025:ccc5:eeeb:86d3");
+ BOOST_CHECK_EQUAL(uri.getPort(), "6363");
+ BOOST_CHECK_EQUAL(uri.getPath(), "");
+
+ BOOST_CHECK_EQUAL(uri.parse("udp6://[2001:db8:3f9:0:3025:ccc5:eeeb:86dg]:6363"), false);
+
+ namespace ip = boost::asio::ip;
+
+ ip::udp::endpoint endpoint4(ip::address_v4::from_string("192.0.2.1"), 7777);
+ uri = FaceUri(endpoint4);
+ BOOST_CHECK_EQUAL(uri.toString(), "udp4://192.0.2.1:7777");
+
+ ip::udp::endpoint endpoint6(ip::address_v6::from_string("2001:DB8::1"), 7777);
+ uri = FaceUri(endpoint6);
+ BOOST_CHECK_EQUAL(uri.toString(), "udp6://[2001:db8::1]:7777");
+
+ BOOST_CHECK(uri.parse("udp6://[fe80::1%25eth1]:6363"));
+ BOOST_CHECK_EQUAL(uri.getHost(), "fe80::1%25eth1");
+
+ BOOST_CHECK(uri.parse("udp6://[fe80::1%eth1]:6363"));
+ BOOST_CHECK_EQUAL(uri.getHost(), "fe80::1%eth1");
+
+ BOOST_CHECK(uri.parse("udp6://[fe80::1%1]:6363"));
+ BOOST_CHECK(uri.parse("udp6://[fe80::1%eth1]"));
+
+ BOOST_CHECK(uri.parse("udp6://[ff01::114%eth#1]"));
+ BOOST_CHECK(uri.parse("udp6://[ff01::114%eth.1,2]"));
+ BOOST_CHECK(uri.parse("udp6://[ff01::114%a+b-c=0]"));
+ BOOST_CHECK(uri.parse("udp6://[ff01::114%[foo]]"));
+ BOOST_CHECK(uri.parse("udp6://[ff01::114%]]"));
+ BOOST_CHECK(uri.parse("udp6://[ff01::114%%]"));
+ BOOST_CHECK(!uri.parse("udp6://[ff01::114%]"));
+ BOOST_CHECK(!uri.parse("udp6://[ff01::114%foo bar]"));
+ BOOST_CHECK(!uri.parse("udp6://[ff01::114%foo/bar]"));
+ BOOST_CHECK(!uri.parse("udp6://[ff01::114%eth0:1]"));
+}
+
+BOOST_FIXTURE_TEST_CASE(IsCanonicalUdp, CanonizeFixture)
+{
+ BOOST_CHECK_EQUAL(FaceUri::canCanonize("udp"), true);
+ BOOST_CHECK_EQUAL(FaceUri::canCanonize("udp4"), true);
+ BOOST_CHECK_EQUAL(FaceUri::canCanonize("udp6"), true);
+
+ BOOST_CHECK_EQUAL(FaceUri("udp4://192.0.2.1:6363").isCanonical(), true);
+ BOOST_CHECK_EQUAL(FaceUri("udp://192.0.2.1:6363").isCanonical(), false);
+ BOOST_CHECK_EQUAL(FaceUri("udp4://192.0.2.1").isCanonical(), false);
+ BOOST_CHECK_EQUAL(FaceUri("udp4://192.0.2.1:6363/").isCanonical(), false);
+ BOOST_CHECK_EQUAL(FaceUri("udp6://[2001:db8::1]:6363").isCanonical(), true);
+ BOOST_CHECK_EQUAL(FaceUri("udp6://[2001:db8::01]:6363").isCanonical(), false);
+ BOOST_CHECK_EQUAL(FaceUri("udp://[2001:db8::1]:6363").isCanonical(), false);
+ BOOST_CHECK_EQUAL(FaceUri("udp://example.net:6363").isCanonical(), false);
+ BOOST_CHECK_EQUAL(FaceUri("udp4://example.net:6363").isCanonical(), false);
+ BOOST_CHECK_EQUAL(FaceUri("udp6://example.net:6363").isCanonical(), false);
+ BOOST_CHECK_EQUAL(FaceUri("udp4://224.0.23.170:56363").isCanonical(), true);
+ BOOST_CHECK_EQUAL(FaceUri("udp4://[2001:db8::1]:6363").isCanonical(), false);
+ BOOST_CHECK_EQUAL(FaceUri("udp6://192.0.2.1:6363").isCanonical(), false);
+
+ const auto& networkInterfaces = ndn::net::tests::collectNetworkInterfaces();
+ if (!networkInterfaces.empty()) {
+ const auto& netif = networkInterfaces.front();
+ auto name = netif->getName();
+ auto index = to_string(netif->getIndex());
+
+ BOOST_CHECK_EQUAL(FaceUri("udp6://[fe80::1%" + name + "]:6363").isCanonical(), true);
+ BOOST_CHECK_EQUAL(FaceUri("udp6://[fe80::1%" + index + "]:6363").isCanonical(), false);
+ BOOST_CHECK_EQUAL(FaceUri("udp6://[fe80::1%" + name + "]").isCanonical(), false);
+ BOOST_CHECK_EQUAL(FaceUri("udp6://[fe80::1068:dddb:fe26:fe3f%25en0]:6363").isCanonical(), false);
+ }
+}
+
+BOOST_AUTO_TEST_CASE_EXPECTED_FAILURES(CanonizeUdpV4, 1)
+BOOST_FIXTURE_TEST_CASE(CanonizeUdpV4, CanonizeFixture)
+{
+ SKIP_IF_IPV4_UNAVAILABLE();
+
+ // IPv4 unicast
+ addTest("udp4://192.0.2.1:6363", true, "udp4://192.0.2.1:6363");
+ addTest("udp://192.0.2.2:6363", true, "udp4://192.0.2.2:6363");
+ addTest("udp4://192.0.2.3", true, "udp4://192.0.2.3:6363");
+ addTest("udp4://192.0.2.4:6363/", true, "udp4://192.0.2.4:6363");
+ addTest("udp4://192.0.2.5:9695", true, "udp4://192.0.2.5:9695");
+ addTest("udp4://192.0.2.666:6363", false, "");
+ addTest("udp4://192.0.2.7:99999", false, ""); // Bug #3897
+ addTest("udp4://google-public-dns-a.google.com", true, "udp4://8.8.8.8:6363");
+ addTest("udp4://google-public-dns-a.google.com:70000", false, "");
+ addTest("udp4://invalid.invalid", false, "");
+
+ // IPv4 multicast
+ addTest("udp4://224.0.23.170:56363", true, "udp4://224.0.23.170:56363");
+ addTest("udp4://224.0.23.170", true, "udp4://224.0.23.170:56363");
+ addTest("udp4://all-routers.mcast.net:56363", true, "udp4://224.0.0.2:56363");
+
+ // IPv6 used with udp4 protocol - not canonical
+ addTest("udp4://[2001:db8::1]:6363", false, "");
+
+ runTests();
+}
+
+BOOST_AUTO_TEST_CASE_EXPECTED_FAILURES(CanonizeUdpV6, 1)
+BOOST_FIXTURE_TEST_CASE(CanonizeUdpV6, CanonizeFixture)
+{
+ SKIP_IF_IPV6_UNAVAILABLE();
+
+ // IPv6 unicast
+ addTest("udp6://[2001:db8::1]:6363", true, "udp6://[2001:db8::1]:6363");
+ addTest("udp6://[2001:db8::1]", true, "udp6://[2001:db8::1]:6363");
+ addTest("udp://[2001:db8::1]:6363", true, "udp6://[2001:db8::1]:6363");
+ addTest("udp6://[2001:db8::01]:6363", true, "udp6://[2001:db8::1]:6363");
+ addTest("udp6://[2001::db8::1]:6363", false, "");
+ addTest("udp6://[2001:db8::1]:99999", false, ""); // Bug #3897
+ addTest("udp6://google-public-dns-a.google.com", true, "udp6://[2001:4860:4860::8888]:6363");
+ addTest("udp6://google-public-dns-a.google.com:70000", false, "");
+ addTest("udp6://invalid.invalid", false, "");
+ addTest("udp://invalid.invalid", false, "");
+
+ // IPv6 multicast
+ addTest("udp6://[ff02::2]:56363", true, "udp6://[ff02::2]:56363");
+ addTest("udp6://[ff02::2]", true, "udp6://[ff02::2]:56363");
+
+ // IPv4 used with udp6 protocol - not canonical
+ addTest("udp6://192.0.2.1:6363", false, "");
+
+ const auto& networkInterfaces = ndn::net::tests::collectNetworkInterfaces();
+ if (!networkInterfaces.empty()) {
+ const auto& netif = networkInterfaces.front();
+ auto name = netif->getName();
+ auto index = to_string(netif->getIndex());
+
+ addTest("udp6://[fe80::1068:dddb:fe26:fe3f%25" + name + "]:6363", true,
+ "udp6://[fe80::1068:dddb:fe26:fe3f%" + name + "]:6363");
+
+ addTest("udp6://[fe80::1068:dddb:fe26:fe3f%" + index + "]:6363", true,
+ "udp6://[fe80::1068:dddb:fe26:fe3f%" + name + "]:6363");
+ }
+
+ runTests();
+}
+
+BOOST_AUTO_TEST_CASE(ParseTcp)
+{
+ FaceUri uri;
+
+ BOOST_CHECK(uri.parse("tcp://random.host.name"));
+ BOOST_CHECK_EQUAL(uri.getScheme(), "tcp");
+ BOOST_CHECK_EQUAL(uri.getHost(), "random.host.name");
+ BOOST_CHECK_EQUAL(uri.getPort(), "");
+ BOOST_CHECK_EQUAL(uri.getPath(), "");
+
+ BOOST_CHECK_EQUAL(uri.parse("tcp://192.0.2.1:"), false);
+ BOOST_CHECK_EQUAL(uri.parse("tcp://[::zzzz]"), false);
+
+ namespace ip = boost::asio::ip;
+
+ ip::tcp::endpoint endpoint4(ip::address_v4::from_string("192.0.2.1"), 7777);
+ uri = FaceUri(endpoint4);
+ BOOST_CHECK_EQUAL(uri.toString(), "tcp4://192.0.2.1:7777");
+
+ uri = FaceUri(endpoint4, "wsclient");
+ BOOST_CHECK_EQUAL(uri.toString(), "wsclient://192.0.2.1:7777");
+
+ ip::tcp::endpoint endpoint6(ip::address_v6::from_string("2001:DB8::1"), 7777);
+ uri = FaceUri(endpoint6);
+ BOOST_CHECK_EQUAL(uri.toString(), "tcp6://[2001:db8::1]:7777");
+
+ BOOST_CHECK(uri.parse("tcp6://[fe80::1%25eth1]:6363"));
+ BOOST_CHECK_EQUAL(uri.getHost(), "fe80::1%25eth1");
+
+ BOOST_CHECK(uri.parse("tcp6://[fe80::1%eth1]:6363"));
+ BOOST_CHECK_EQUAL(uri.getHost(), "fe80::1%eth1");
+
+ BOOST_CHECK(uri.parse("tcp6://[fe80::1%1]:6363"));
+ BOOST_CHECK(uri.parse("tcp6://[fe80::1%eth1]"));
+}
+
+BOOST_FIXTURE_TEST_CASE(IsCanonicalTcp, CanonizeFixture)
+{
+ BOOST_CHECK_EQUAL(FaceUri::canCanonize("tcp"), true);
+ BOOST_CHECK_EQUAL(FaceUri::canCanonize("tcp4"), true);
+ BOOST_CHECK_EQUAL(FaceUri::canCanonize("tcp6"), true);
+
+ BOOST_CHECK_EQUAL(FaceUri("tcp4://192.0.2.1:6363").isCanonical(), true);
+ BOOST_CHECK_EQUAL(FaceUri("tcp://192.0.2.1:6363").isCanonical(), false);
+ BOOST_CHECK_EQUAL(FaceUri("tcp4://192.0.2.1").isCanonical(), false);
+ BOOST_CHECK_EQUAL(FaceUri("tcp4://192.0.2.1:6363/").isCanonical(), false);
+ BOOST_CHECK_EQUAL(FaceUri("tcp6://[2001:db8::1]:6363").isCanonical(), true);
+ BOOST_CHECK_EQUAL(FaceUri("tcp6://[2001:db8::01]:6363").isCanonical(), false);
+ BOOST_CHECK_EQUAL(FaceUri("tcp://[2001:db8::1]:6363").isCanonical(), false);
+ BOOST_CHECK_EQUAL(FaceUri("tcp://example.net:6363").isCanonical(), false);
+ BOOST_CHECK_EQUAL(FaceUri("tcp4://example.net:6363").isCanonical(), false);
+ BOOST_CHECK_EQUAL(FaceUri("tcp6://example.net:6363").isCanonical(), false);
+ BOOST_CHECK_EQUAL(FaceUri("tcp4://224.0.23.170:56363").isCanonical(), false);
+ BOOST_CHECK_EQUAL(FaceUri("tcp4://[2001:db8::1]:6363").isCanonical(), false);
+ BOOST_CHECK_EQUAL(FaceUri("tcp6://192.0.2.1:6363").isCanonical(), false);
+
+ const auto& networkInterfaces = ndn::net::tests::collectNetworkInterfaces();
+ if (!networkInterfaces.empty()) {
+ const auto& netif = networkInterfaces.front();
+ auto name = netif->getName();
+ auto index = to_string(netif->getIndex());
+
+ BOOST_CHECK_EQUAL(FaceUri("tcp6://[fe80::1%" + name + "]:6363").isCanonical(), true);
+ BOOST_CHECK_EQUAL(FaceUri("tcp6://[fe80::1%" + index + "]:6363").isCanonical(), false);
+ BOOST_CHECK_EQUAL(FaceUri("tcp6://[fe80::1%" + name + "]").isCanonical(), false);
+ BOOST_CHECK_EQUAL(FaceUri("tcp6://[fe80::1068:dddb:fe26:fe3f%25en0]:6363").isCanonical(), false);
+ }
+}
+
+BOOST_AUTO_TEST_CASE_EXPECTED_FAILURES(CanonizeTcpV4, 1)
+BOOST_FIXTURE_TEST_CASE(CanonizeTcpV4, CanonizeFixture)
+{
+ SKIP_IF_IPV4_UNAVAILABLE();
+
+ // IPv4 unicast
+ addTest("tcp4://192.0.2.1:6363", true, "tcp4://192.0.2.1:6363");
+ addTest("tcp://192.0.2.2:6363", true, "tcp4://192.0.2.2:6363");
+ addTest("tcp4://192.0.2.3", true, "tcp4://192.0.2.3:6363");
+ addTest("tcp4://192.0.2.4:6363/", true, "tcp4://192.0.2.4:6363");
+ addTest("tcp4://192.0.2.5:9695", true, "tcp4://192.0.2.5:9695");
+ addTest("tcp4://192.0.2.666:6363", false, "");
+ addTest("tcp4://192.0.2.7:99999", false, ""); // Bug #3897
+ addTest("tcp4://google-public-dns-a.google.com", true, "tcp4://8.8.8.8:6363");
+ addTest("tcp4://google-public-dns-a.google.com:70000", false, "");
+ addTest("tcp4://invalid.invalid", false, "");
+
+ // IPv4 multicast
+ addTest("tcp4://224.0.23.170:56363", false, "");
+ addTest("tcp4://224.0.23.170", false, "");
+ addTest("tcp4://all-routers.mcast.net:56363", false, "");
+
+ // IPv6 used with tcp4 protocol - not canonical
+ addTest("tcp4://[2001:db8::1]:6363", false, "");
+
+ const auto& networkInterfaces = ndn::net::tests::collectNetworkInterfaces();
+ if (!networkInterfaces.empty()) {
+ const auto& netif = networkInterfaces.front();
+ auto name = netif->getName();
+ auto index = to_string(netif->getIndex());
+
+ addTest("tcp6://[fe80::1068:dddb:fe26:fe3f%25" + name + "]:6363", true,
+ "tcp6://[fe80::1068:dddb:fe26:fe3f%" + name + "]:6363");
+
+ addTest("tcp6://[fe80::1068:dddb:fe26:fe3f%" + index + "]:6363", true,
+ "tcp6://[fe80::1068:dddb:fe26:fe3f%" + name + "]:6363");
+ }
+
+ runTests();
+}
+
+BOOST_AUTO_TEST_CASE_EXPECTED_FAILURES(CanonizeTcpV6, 1)
+BOOST_FIXTURE_TEST_CASE(CanonizeTcpV6, CanonizeFixture)
+{
+ SKIP_IF_IPV6_UNAVAILABLE();
+
+ // IPv6 unicast
+ addTest("tcp6://[2001:db8::1]:6363", true, "tcp6://[2001:db8::1]:6363");
+ addTest("tcp6://[2001:db8::1]", true, "tcp6://[2001:db8::1]:6363");
+ addTest("tcp://[2001:db8::1]:6363", true, "tcp6://[2001:db8::1]:6363");
+ addTest("tcp6://[2001:db8::01]:6363", true, "tcp6://[2001:db8::1]:6363");
+ addTest("tcp6://[2001::db8::1]:6363", false, "");
+ addTest("tcp6://[2001:db8::1]:99999", false, ""); // Bug #3897
+ addTest("tcp6://google-public-dns-a.google.com", true, "tcp6://[2001:4860:4860::8888]:6363");
+ addTest("tcp6://google-public-dns-a.google.com:70000", false, "");
+ addTest("tcp6://invalid.invalid", false, "");
+ addTest("tcp://invalid.invalid", false, "");
+
+ // IPv6 multicast
+ addTest("tcp6://[ff02::2]:56363", false, "");
+ addTest("tcp6://[ff02::2]", false, "");
+
+ // IPv4 used with tcp6 protocol - not canonical
+ addTest("tcp6://192.0.2.1:6363", false, "");
+
+ runTests();
+}
+
+BOOST_AUTO_TEST_CASE_EXPECTED_FAILURES(ParseUnix, 1)
+BOOST_AUTO_TEST_CASE(ParseUnix)
+{
+ FaceUri uri;
+
+ BOOST_CHECK(uri.parse("unix:///var/run/example.sock"));
+ BOOST_CHECK_EQUAL(uri.getScheme(), "unix");
+ BOOST_CHECK_EQUAL(uri.getHost(), "");
+ BOOST_CHECK_EQUAL(uri.getPort(), "");
+ BOOST_CHECK_EQUAL(uri.getPath(), "/var/run/example.sock");
+
+ // This is not a valid unix:// URI, but the parse would treat "var" as host
+ BOOST_CHECK_EQUAL(uri.parse("unix://var/run/example.sock"), false); // Bug #3896
+
+#ifdef BOOST_ASIO_HAS_LOCAL_SOCKETS
+ using boost::asio::local::stream_protocol;
+ stream_protocol::endpoint endpoint("/var/run/example.sock");
+ uri = FaceUri(endpoint);
+ BOOST_CHECK_EQUAL(uri.toString(), "unix:///var/run/example.sock");
+#endif // BOOST_ASIO_HAS_LOCAL_SOCKETS
+}
+
+BOOST_AUTO_TEST_CASE(ParseFd)
+{
+ FaceUri uri;
+
+ BOOST_CHECK(uri.parse("fd://6"));
+ BOOST_CHECK_EQUAL(uri.getScheme(), "fd");
+ BOOST_CHECK_EQUAL(uri.getHost(), "6");
+ BOOST_CHECK_EQUAL(uri.getPort(), "");
+ BOOST_CHECK_EQUAL(uri.getPath(), "");
+
+ int fd = 21;
+ uri = FaceUri::fromFd(fd);
+ BOOST_CHECK_EQUAL(uri.toString(), "fd://21");
+}
+
+BOOST_AUTO_TEST_CASE(ParseEther)
+{
+ FaceUri uri;
+
+ BOOST_CHECK(uri.parse("ether://[08:00:27:01:dd:01]"));
+ BOOST_CHECK_EQUAL(uri.getScheme(), "ether");
+ BOOST_CHECK_EQUAL(uri.getHost(), "08:00:27:01:dd:01");
+ BOOST_CHECK_EQUAL(uri.getPort(), "");
+ BOOST_CHECK_EQUAL(uri.getPath(), "");
+
+ BOOST_CHECK_EQUAL(uri.parse("ether://[08:00:27:zz:dd:01]"), false);
+
+ auto address = ethernet::Address::fromString("33:33:01:01:01:01");
+ uri = FaceUri(address);
+ BOOST_CHECK_EQUAL(uri.toString(), "ether://[33:33:01:01:01:01]");
+}
+
+BOOST_FIXTURE_TEST_CASE(CanonizeEther, CanonizeFixture)
+{
+ BOOST_CHECK_EQUAL(FaceUri::canCanonize("ether"), true);
+
+ BOOST_CHECK_EQUAL(FaceUri("ether://[08:00:27:01:01:01]").isCanonical(), true);
+ BOOST_CHECK_EQUAL(FaceUri("ether://[08:00:27:1:1:1]").isCanonical(), false);
+ BOOST_CHECK_EQUAL(FaceUri("ether://[08:00:27:01:01:01]/").isCanonical(), false);
+ BOOST_CHECK_EQUAL(FaceUri("ether://[33:33:01:01:01:01]").isCanonical(), true);
+
+ addTest("ether://[08:00:27:01:01:01]", true, "ether://[08:00:27:01:01:01]");
+ addTest("ether://[08:00:27:1:1:1]", true, "ether://[08:00:27:01:01:01]");
+ addTest("ether://[08:00:27:01:01:01]/", true, "ether://[08:00:27:01:01:01]");
+ addTest("ether://[33:33:01:01:01:01]", true, "ether://[33:33:01:01:01:01]");
+
+ runTests();
+}
+
+BOOST_AUTO_TEST_CASE_EXPECTED_FAILURES(ParseDev, 1)
+BOOST_AUTO_TEST_CASE(ParseDev)
+{
+ FaceUri uri;
+
+ BOOST_CHECK(uri.parse("dev://eth0"));
+ BOOST_CHECK_EQUAL(uri.getScheme(), "dev");
+ BOOST_CHECK_EQUAL(uri.getHost(), "eth0");
+ BOOST_CHECK_EQUAL(uri.getPort(), "");
+ BOOST_CHECK_EQUAL(uri.getPath(), "");
+
+ BOOST_CHECK_EQUAL(uri.parse("dev://eth0:8888"), false); // Bug #3896
+
+ std::string ifname = "en1";
+ uri = FaceUri::fromDev(ifname);
+ BOOST_CHECK_EQUAL(uri.toString(), "dev://en1");
+}
+
+BOOST_AUTO_TEST_CASE(IsCanonicalDev)
+{
+ BOOST_CHECK_EQUAL(FaceUri::canCanonize("dev"), true);
+
+ BOOST_CHECK_EQUAL(FaceUri("dev://eth0").isCanonical(), true);
+ BOOST_CHECK_EQUAL(FaceUri("dev://").isCanonical(), false);
+ BOOST_CHECK_EQUAL(FaceUri("dev://eth0:8888").isCanonical(), false);
+ BOOST_CHECK_EQUAL(FaceUri("dev://eth0/").isCanonical(), false);
+ BOOST_CHECK_EQUAL(FaceUri("dev://eth0/A").isCanonical(), false);
+}
+
+BOOST_FIXTURE_TEST_CASE(CanonizeDev, CanonizeFixture)
+{
+ addTest("dev://eth0", true, "dev://eth0");
+ addTest("dev://", false, "");
+ addTest("dev://eth0:8888", false, "");
+ addTest("dev://eth0/", true, "dev://eth0");
+ addTest("dev://eth0/A", false, "");
+
+ runTests();
+}
+
+BOOST_AUTO_TEST_CASE(ParseUdpDev)
+{
+ FaceUri uri;
+
+ BOOST_CHECK(uri.parse("udp4+dev://eth0:7777"));
+ BOOST_CHECK_EQUAL(uri.getScheme(), "udp4+dev");
+ BOOST_CHECK_EQUAL(uri.getHost(), "eth0");
+ BOOST_CHECK_EQUAL(uri.getPort(), "7777");
+ BOOST_CHECK_EQUAL(uri.getPath(), "");
+
+ BOOST_CHECK(uri.parse("udp6+dev://eth1:7777"));
+ BOOST_CHECK_EQUAL(uri.getScheme(), "udp6+dev");
+ BOOST_CHECK_EQUAL(uri.getHost(), "eth1");
+ BOOST_CHECK_EQUAL(uri.getPort(), "7777");
+ BOOST_CHECK_EQUAL(uri.getPath(), "");
+
+ BOOST_CHECK(uri.parse("abc+efg://eth0"));
+ BOOST_CHECK(!uri.parse("abc+://eth0"));
+ BOOST_CHECK(!uri.parse("+abc://eth0"));
+
+ namespace ip = boost::asio::ip;
+
+ ip::udp::endpoint endpoint4(ip::udp::v4(), 7777);
+ uri = FaceUri::fromUdpDev(endpoint4, "en1");
+ BOOST_CHECK_EQUAL(uri.toString(), "udp4+dev://en1:7777");
+
+ ip::udp::endpoint endpoint6(ip::udp::v6(), 7777);
+ uri = FaceUri::fromUdpDev(endpoint6, "en2");
+ BOOST_CHECK_EQUAL(uri.toString(), "udp6+dev://en2:7777");
+}
+
+BOOST_FIXTURE_TEST_CASE(CanonizeUdpDev, CanonizeFixture)
+{
+ BOOST_CHECK_EQUAL(FaceUri("udp4+dev://eth0:7777").isCanonical(), true);
+ BOOST_CHECK_EQUAL(FaceUri("udp6+dev://eth1:7777").isCanonical(), true);
+ BOOST_CHECK_EQUAL(FaceUri("udp+dev://eth1:7777").isCanonical(), false);
+ BOOST_CHECK_EQUAL(FaceUri("udp6+dev://eth1").isCanonical(), false);
+
+ addTest("udp4+dev://en0:7777", true, "udp4+dev://en0:7777");
+ addTest("udp6+dev://en0:7777", true, "udp6+dev://en0:7777");
+ addTest("udp+dev://en1:7777", false, "");
+ addTest("udp6+dev://en2", false, "");
+}
+
+BOOST_AUTO_TEST_CASE(CanonizeEmptyCallback)
+{
+ boost::asio::io_service io;
+
+ // unsupported scheme
+ FaceUri("null://").canonize(FaceUri::CanonizeSuccessCallback(),
+ FaceUri::CanonizeFailureCallback(),
+ io, 1_ms);
+
+ // cannot resolve
+ FaceUri("udp://192.0.2.333").canonize(FaceUri::CanonizeSuccessCallback(),
+ FaceUri::CanonizeFailureCallback(),
+ io, 1_ms);
+
+ // already canonical
+ FaceUri("udp4://192.0.2.1:6363").canonize(FaceUri::CanonizeSuccessCallback(),
+ FaceUri::CanonizeFailureCallback(),
+ io, 1_ms);
+
+ // need DNS resolution
+ FaceUri("udp://192.0.2.1:6363").canonize(FaceUri::CanonizeSuccessCallback(),
+ FaceUri::CanonizeFailureCallback(),
+ io, 1_ms);
+
+ io.run(); // should not crash
+
+ // avoid "test case [...] did not check any assertions" message from Boost.Test
+ BOOST_CHECK(true);
+}
+
+BOOST_FIXTURE_TEST_CASE(CanonizeUnsupported, CanonizeFixture)
+{
+ BOOST_CHECK_EQUAL(FaceUri::canCanonize("internal"), false);
+ BOOST_CHECK_EQUAL(FaceUri::canCanonize("null"), false);
+ BOOST_CHECK_EQUAL(FaceUri::canCanonize("unix"), false);
+ BOOST_CHECK_EQUAL(FaceUri::canCanonize("fd"), false);
+
+ BOOST_CHECK_EQUAL(FaceUri("internal://").isCanonical(), false);
+ BOOST_CHECK_EQUAL(FaceUri("null://").isCanonical(), false);
+ BOOST_CHECK_EQUAL(FaceUri("unix:///var/run/nfd.sock").isCanonical(), false);
+ BOOST_CHECK_EQUAL(FaceUri("fd://0").isCanonical(), false);
+
+ addTest("internal://", false, "");
+ addTest("null://", false, "");
+ addTest("unix:///var/run/nfd.sock", false, "");
+ addTest("fd://0", false, "");
+
+ runTests();
+}
+
+BOOST_AUTO_TEST_CASE(Bug1635)
+{
+ FaceUri uri;
+
+ BOOST_CHECK(uri.parse("wsclient://[::ffff:76.90.11.239]:56366"));
+ BOOST_CHECK_EQUAL(uri.getScheme(), "wsclient");
+ BOOST_CHECK_EQUAL(uri.getHost(), "76.90.11.239");
+ BOOST_CHECK_EQUAL(uri.getPort(), "56366");
+ BOOST_CHECK_EQUAL(uri.getPath(), "");
+ BOOST_CHECK_EQUAL(uri.toString(), "wsclient://76.90.11.239:56366");
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestFaceUri
+BOOST_AUTO_TEST_SUITE_END() // Net
+
+} // namespace tests
+} // namespace ndn
diff --git a/tests/unit/net/network-configuration-detector.cpp b/tests/unit/net/network-configuration-detector.cpp
new file mode 100644
index 0000000..3e4f128
--- /dev/null
+++ b/tests/unit/net/network-configuration-detector.cpp
@@ -0,0 +1,87 @@
+/* -*- 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 "network-configuration-detector.hpp"
+
+#include <boost/asio/ip/address.hpp>
+#include <boost/asio/ip/udp.hpp>
+#include <boost/asio/ip/basic_resolver.hpp>
+#include <boost/asio/io_service.hpp>
+#include <boost/range/iterator_range_core.hpp>
+
+namespace ndn {
+namespace tests {
+
+bool NetworkConfigurationDetector::m_isInitialized = false;
+bool NetworkConfigurationDetector::m_hasIpv4 = false;
+bool NetworkConfigurationDetector::m_hasIpv6 = false;
+
+bool
+NetworkConfigurationDetector::hasIpv4()
+{
+ if (!m_isInitialized) {
+ detect();
+ }
+ return m_hasIpv4;
+}
+
+bool
+NetworkConfigurationDetector::hasIpv6()
+{
+ if (!m_isInitialized) {
+ detect();
+ }
+ return m_hasIpv6;
+}
+
+void
+NetworkConfigurationDetector::detect()
+{
+ typedef boost::asio::ip::basic_resolver<boost::asio::ip::udp> BoostResolver;
+
+ boost::asio::io_service ioService;
+ BoostResolver resolver(ioService);
+
+ // The specified hostname must contain both A and AAAA records
+ BoostResolver::query query("a.root-servers.net", "");
+
+ boost::system::error_code errorCode;
+ BoostResolver::iterator begin = resolver.resolve(query, errorCode);
+ if (errorCode) {
+ m_isInitialized = true;
+ return;
+ }
+ BoostResolver::iterator end;
+
+ for (const auto& i : boost::make_iterator_range(begin, end)) {
+ if (i.endpoint().address().is_v4()) {
+ m_hasIpv4 = true;
+ }
+ else if (i.endpoint().address().is_v6()) {
+ m_hasIpv6 = true;
+ }
+ }
+
+ m_isInitialized = true;
+}
+
+} // namespace tests
+} // namespace ndn
diff --git a/tests/unit/net/network-configuration-detector.hpp b/tests/unit/net/network-configuration-detector.hpp
new file mode 100644
index 0000000..58715c8
--- /dev/null
+++ b/tests/unit/net/network-configuration-detector.hpp
@@ -0,0 +1,75 @@
+/* -*- 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.
+ */
+
+#ifndef NDN_TESTS_NET_NETWORK_CONFIGURATION_DETECTOR_HPP
+#define NDN_TESTS_NET_NETWORK_CONFIGURATION_DETECTOR_HPP
+
+#define SKIP_IF_IPV4_UNAVAILABLE() \
+ do { \
+ if (!::ndn::tests::NetworkConfigurationDetector::hasIpv4()) { \
+ BOOST_WARN_MESSAGE(false, "skipping assertions that require IPv4 support"); \
+ return; \
+ } \
+ } while (false)
+
+#define SKIP_IF_IPV6_UNAVAILABLE() \
+ do { \
+ if (!::ndn::tests::NetworkConfigurationDetector::hasIpv6()) { \
+ BOOST_WARN_MESSAGE(false, "skipping assertions that require IPv6 support"); \
+ return; \
+ } \
+ } while (false)
+
+#define SKIP_IF_IP_UNAVAILABLE() \
+ do { \
+ if (!::ndn::tests::NetworkConfigurationDetector::hasIpv4() && \
+ !::ndn::tests::NetworkConfigurationDetector::hasIpv6()) { \
+ BOOST_WARN_MESSAGE(false, "skipping assertions that require either IPv4 or IPv6 support"); \
+ return; \
+ } \
+ } while (false)
+
+namespace ndn {
+namespace tests {
+
+class NetworkConfigurationDetector
+{
+public:
+ static bool
+ hasIpv4();
+
+ static bool
+ hasIpv6();
+
+private:
+ static void
+ detect();
+
+private:
+ static bool m_isInitialized;
+ static bool m_hasIpv4;
+ static bool m_hasIpv6;
+};
+
+} // namespace tests
+} // namespace ndn
+
+#endif // NDN_TESTS_NET_NETWORK_CONFIGURATION_DETECTOR_HPP
diff --git a/tests/unit/net/network-monitor-stub.t.cpp b/tests/unit/net/network-monitor-stub.t.cpp
new file mode 100644
index 0000000..8fbf3eb
--- /dev/null
+++ b/tests/unit/net/network-monitor-stub.t.cpp
@@ -0,0 +1,125 @@
+/* -*- 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 "net/network-monitor-stub.hpp"
+
+#include "boost-test.hpp"
+
+namespace ndn {
+namespace net {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Net)
+BOOST_AUTO_TEST_SUITE(TestNetworkMonitorStub)
+
+BOOST_AUTO_TEST_CASE(Capabilities)
+{
+ NetworkMonitorStub stub(NetworkMonitor::CAP_ENUM | NetworkMonitor::CAP_IF_ADD_REMOVE);
+ BOOST_CHECK_EQUAL(stub.getCapabilities(),
+ NetworkMonitor::CAP_ENUM | NetworkMonitor::CAP_IF_ADD_REMOVE);
+}
+
+class StubFixture
+{
+public:
+ StubFixture()
+ : stub(~0)
+ {
+ stub.onEnumerationCompleted.connect([this] { signals.push_back("EnumerationCompleted"); });
+ stub.onInterfaceAdded.connect([this] (shared_ptr<const NetworkInterface> netif) {
+ signals.push_back("InterfaceAdded " + netif->getName()); });
+ stub.onInterfaceRemoved.connect([this] (shared_ptr<const NetworkInterface> netif) {
+ signals.push_back("InterfaceRemoved " + netif->getName()); });
+ }
+
+public:
+ NetworkMonitorStub stub;
+ std::vector<std::string> signals;
+};
+
+BOOST_FIXTURE_TEST_CASE(AddInterface, StubFixture)
+{
+ BOOST_CHECK_EQUAL(stub.listNetworkInterfaces().size(), 0);
+ BOOST_CHECK(stub.getNetworkInterface("eth1") == nullptr);
+
+ shared_ptr<NetworkInterface> if1 = stub.makeNetworkInterface();
+ if1->setIndex(13697);
+ if1->setName("eth1");
+ stub.addInterface(if1);
+ if1.reset();
+ BOOST_REQUIRE(stub.getNetworkInterface("eth1") != nullptr);
+ BOOST_CHECK_EQUAL(stub.getNetworkInterface("eth1")->getIndex(), 13697);
+ BOOST_CHECK_EQUAL(stub.listNetworkInterfaces().at(0)->getIndex(), 13697);
+ BOOST_REQUIRE_EQUAL(signals.size(), 1);
+ BOOST_CHECK_EQUAL(signals.back(), "InterfaceAdded eth1");
+
+ shared_ptr<NetworkInterface> if1b = stub.makeNetworkInterface();
+ if1b->setIndex(3280);
+ if1b->setName("eth1");
+ BOOST_CHECK_THROW(stub.addInterface(if1b), std::invalid_argument);
+ BOOST_CHECK(stub.getNetworkInterface("eth1") != nullptr);
+ BOOST_CHECK_EQUAL(stub.getNetworkInterface("eth1")->getIndex(), 13697);
+ BOOST_CHECK_EQUAL(stub.listNetworkInterfaces().at(0)->getIndex(), 13697);
+ BOOST_CHECK_EQUAL(signals.size(), 1);
+
+ stub.emitEnumerationCompleted();
+ BOOST_REQUIRE_EQUAL(signals.size(), 2);
+ BOOST_CHECK_EQUAL(signals.back(), "EnumerationCompleted");
+
+ shared_ptr<NetworkInterface> if2 = stub.makeNetworkInterface();
+ if2->setIndex(19243);
+ if2->setName("eth2");
+ stub.addInterface(if2);
+ if2.reset();
+ BOOST_REQUIRE(stub.getNetworkInterface("eth2") != nullptr);
+ BOOST_CHECK_EQUAL(stub.getNetworkInterface("eth2")->getIndex(), 19243);
+ BOOST_CHECK_EQUAL(stub.listNetworkInterfaces().size(), 2);
+ BOOST_REQUIRE_EQUAL(signals.size(), 3);
+ BOOST_CHECK_EQUAL(signals.back(), "InterfaceAdded eth2");
+}
+
+BOOST_FIXTURE_TEST_CASE(RemoveInterface, StubFixture)
+{
+ shared_ptr<NetworkInterface> if1 = stub.makeNetworkInterface();
+ if1->setIndex(13697);
+ if1->setName("eth1");
+ stub.addInterface(if1);
+
+ stub.emitEnumerationCompleted();
+ BOOST_REQUIRE_EQUAL(signals.size(), 2);
+ BOOST_CHECK_EQUAL(signals.back(), "EnumerationCompleted");
+
+ stub.removeInterface("eth1");
+ BOOST_CHECK(stub.getNetworkInterface("eth1") == nullptr);
+ BOOST_CHECK_EQUAL(stub.listNetworkInterfaces().size(), 0);
+ BOOST_REQUIRE_EQUAL(signals.size(), 3);
+ BOOST_CHECK_EQUAL(signals.back(), "InterfaceRemoved eth1");
+
+ stub.removeInterface("eth2"); // non-existent
+ BOOST_CHECK_EQUAL(signals.size(), 3);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestNetworkMonitorStub
+BOOST_AUTO_TEST_SUITE_END() // Net
+
+} // namespace tests
+} // namespace net
+} // namespace ndn
diff --git a/tests/unit/net/network-monitor.t.cpp b/tests/unit/net/network-monitor.t.cpp
new file mode 100644
index 0000000..b522ee6
--- /dev/null
+++ b/tests/unit/net/network-monitor.t.cpp
@@ -0,0 +1,73 @@
+/* -*- 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 "net/network-monitor.hpp"
+
+#include "boost-test.hpp"
+#include <boost/asio/io_service.hpp>
+
+namespace ndn {
+namespace net {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Net)
+BOOST_AUTO_TEST_SUITE(TestNetworkMonitor)
+
+#define NM_REQUIRE_CAP(capability) \
+ do { \
+ if ((nm->getCapabilities() & NetworkMonitor::CAP_ ## capability) == 0) { \
+ BOOST_WARN_MESSAGE(false, "skipping assertions that require " #capability " capability"); \
+ return; \
+ } \
+ } while (false)
+
+BOOST_AUTO_TEST_CASE(DestructWithoutRun)
+{
+ boost::asio::io_service io;
+ auto nm = make_unique<NetworkMonitor>(io);
+ nm.reset();
+ BOOST_CHECK(true); // if we got this far, the test passed
+}
+
+BOOST_AUTO_TEST_CASE(DestructWhileEnumerating)
+{
+ boost::asio::io_service io;
+ auto nm = make_unique<NetworkMonitor>(io);
+ NM_REQUIRE_CAP(ENUM);
+
+ nm->onInterfaceAdded.connect([&] (const shared_ptr<const NetworkInterface>&) {
+ io.post([&] { nm.reset(); });
+ });
+ nm->onEnumerationCompleted.connect([&] {
+ // make sure the test case terminates even if we have zero interfaces
+ io.post([&] { nm.reset(); });
+ });
+
+ io.run();
+ BOOST_CHECK(true); // if we got this far, the test passed
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestNetworkMonitor
+BOOST_AUTO_TEST_SUITE_END() // Net
+
+} // namespace tests
+} // namespace net
+} // namespace ndn
diff --git a/tests/unit/packet-base.t.cpp b/tests/unit/packet-base.t.cpp
new file mode 100644
index 0000000..8b4dfa2
--- /dev/null
+++ b/tests/unit/packet-base.t.cpp
@@ -0,0 +1,64 @@
+/* -*- 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 "packet-base.hpp"
+
+#include "../boost-test.hpp"
+#include "interest.hpp"
+#include "lp/tags.hpp"
+
+namespace ndn {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(TestPacketBase)
+
+BOOST_AUTO_TEST_CASE(CongestionMark)
+{
+ Interest interest;
+
+ BOOST_CHECK_EQUAL(interest.getCongestionMark(), 0);
+
+ auto tag = interest.getTag<lp::CongestionMarkTag>();
+ BOOST_CHECK(!tag);
+
+ interest.setCongestionMark(true);
+ tag = interest.getTag<lp::CongestionMarkTag>();
+ BOOST_REQUIRE(tag);
+ BOOST_CHECK_EQUAL(*tag, 1);
+
+ interest.setCongestionMark(false);
+ tag = interest.getTag<lp::CongestionMarkTag>();
+ BOOST_CHECK(!tag);
+
+ interest.setCongestionMark(300);
+ tag = interest.getTag<lp::CongestionMarkTag>();
+ BOOST_REQUIRE(tag);
+ BOOST_CHECK_EQUAL(*tag, 300);
+
+ interest.setCongestionMark(0);
+ tag = interest.getTag<lp::CongestionMarkTag>();
+ BOOST_CHECK(!tag);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestPacketBase
+
+} // namespace tests
+} // namespace ndn
diff --git a/tests/unit/prefix-announcement.t.cpp b/tests/unit/prefix-announcement.t.cpp
new file mode 100644
index 0000000..fa69eb3
--- /dev/null
+++ b/tests/unit/prefix-announcement.t.cpp
@@ -0,0 +1,247 @@
+/* -*- 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 "prefix-announcement.hpp"
+#include "encoding/tlv-nfd.hpp"
+
+#include "boost-test.hpp"
+#include "identity-management-fixture.hpp"
+#include "make-interest-data.hpp"
+
+namespace ndn {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(TestPrefixAnnouncement)
+
+static Data
+makePrefixAnnData()
+{
+ return Data(
+ "067A 071A announced-name=/net/example 08036E6574 08076578616D706C65"
+ " keyword-prefix-ann=20025041 version=0802FD01 segment=08020000"
+ " 1403 content-type=prefix-ann 180105"
+ " 1530 expire in one hour 6D040036EE80"
+ " validity FD00FD26 FD00FE0F323031383130333054303030303030"
+ " FD00FF0F323031383131323454323335393539"
+ " 1603 1B0100 signature"
+ " 1720 0000000000000000000000000000000000000000000000000000000000000000"_block);
+}
+
+BOOST_AUTO_TEST_CASE(DecodeGood)
+{
+ Data data0 = makePrefixAnnData();
+ PrefixAnnouncement pa0(data0);
+ BOOST_CHECK_EQUAL(pa0.getAnnouncedName(), "/net/example");
+ BOOST_CHECK_EQUAL(pa0.getExpiration(), 1_h);
+ BOOST_CHECK(pa0.getValidityPeriod());
+ BOOST_CHECK_EQUAL(*pa0.getValidityPeriod(),
+ security::ValidityPeriod(time::fromIsoString("20181030T000000"),
+ time::fromIsoString("20181124T235959")));
+
+ // reorder ExpirationPeriod and ValidityPeriod, unrecognized non-critical element
+ Data data1 = makePrefixAnnData();
+ Block payload1 = data1.getContent();
+ payload1.parse();
+ Block expirationElement = payload1.get(tlv::nfd::ExpirationPeriod);
+ payload1.remove(tlv::nfd::ExpirationPeriod);
+ payload1.push_back(expirationElement);
+ payload1.push_back("2000"_block);
+ payload1.encode();
+ data1.setContent(payload1);
+ PrefixAnnouncement pa1(data1);
+ BOOST_CHECK_EQUAL(pa1.getAnnouncedName(), "/net/example");
+ BOOST_CHECK_EQUAL(pa1.getExpiration(), 1_h);
+ BOOST_CHECK(pa1.getValidityPeriod());
+ BOOST_CHECK_EQUAL(*pa1.getValidityPeriod(),
+ security::ValidityPeriod(time::fromIsoString("20181030T000000"),
+ time::fromIsoString("20181124T235959")));
+
+ // no ValidityPeriod
+ Data data2 = makePrefixAnnData();
+ Block payload2 = data2.getContent();
+ payload2.parse();
+ payload2.remove(tlv::ValidityPeriod);
+ payload2.encode();
+ data2.setContent(payload2);
+ PrefixAnnouncement pa2(data2);
+ BOOST_CHECK_EQUAL(pa2.getAnnouncedName(), "/net/example");
+ BOOST_CHECK_EQUAL(pa2.getExpiration(), 1_h);
+ BOOST_CHECK(!pa2.getValidityPeriod());
+}
+
+BOOST_AUTO_TEST_CASE(DecodeBad)
+{
+ // wrong ContentType
+ Data data0 = makePrefixAnnData();
+ data0.setContentType(tlv::ContentType_Blob);
+ BOOST_CHECK_THROW(PrefixAnnouncement pa0(data0), tlv::Error);
+
+ // Name has no "32=PA" keyword
+ Data data1 = makePrefixAnnData();
+ setNameComponent(data1, -3, name::Component::fromEscapedString("32=not-PA"));
+ BOOST_CHECK_THROW(PrefixAnnouncement pa1(data1), tlv::Error);
+
+ // Name has no version component
+ Data data2 = makePrefixAnnData();
+ setNameComponent(data2, -2, "not-version");
+ BOOST_CHECK_THROW(PrefixAnnouncement pa2(data2), tlv::Error);
+
+ // Name has no segment number component
+ Data data3 = makePrefixAnnData();
+ setNameComponent(data3, -2, "not-segment");
+ BOOST_CHECK_THROW(PrefixAnnouncement pa3(data3), tlv::Error);
+
+ // Content has no ExpirationPeriod element
+ Data data4 = makePrefixAnnData();
+ Block payload4 = data4.getContent();
+ payload4.parse();
+ payload4.remove(tlv::nfd::ExpirationPeriod);
+ payload4.encode();
+ data4.setContent(payload4);
+ BOOST_CHECK_THROW(PrefixAnnouncement pa4(data4), tlv::Error);
+
+ // ExpirationPeriod is malformed
+ Data data5 = makePrefixAnnData();
+ Block payload5 = data5.getContent();
+ payload5.parse();
+ payload5.remove(tlv::nfd::ExpirationPeriod);
+ payload5.push_back("6D03010101"_block);
+ payload5.encode();
+ data5.setContent(payload5);
+ BOOST_CHECK_THROW(PrefixAnnouncement pa5(data5), tlv::Error);
+
+ // ValidityPeriod is malformed
+ Data data6 = makePrefixAnnData();
+ Block payload6 = data6.getContent();
+ payload6.parse();
+ payload6.remove(tlv::ValidityPeriod);
+ payload6.push_back("FD00FD00"_block);
+ payload6.encode();
+ data6.setContent(payload6);
+ BOOST_CHECK_THROW(PrefixAnnouncement pa6(data6), tlv::Error);
+
+ // Content has unrecognized critical element
+ Data data7 = makePrefixAnnData();
+ Block payload7 = data7.getContent();
+ payload7.parse();
+ payload7.push_back("0200"_block);
+ payload7.encode();
+ data7.setContent(payload7);
+ BOOST_CHECK_THROW(PrefixAnnouncement pa7(data7), tlv::Error);
+}
+
+BOOST_FIXTURE_TEST_CASE(EncodeEmpty, IdentityManagementFixture)
+{
+ PrefixAnnouncement pa;
+ BOOST_CHECK(!pa.getData());
+ BOOST_CHECK_EQUAL(pa.getAnnouncedName(), "/");
+ BOOST_CHECK_EQUAL(pa.getExpiration(), 0_ms);
+ BOOST_CHECK(!pa.getValidityPeriod());
+
+ const Data& data = pa.toData(m_keyChain, signingWithSha256(), 5);
+ BOOST_CHECK_EQUAL(data.getName(), "/32=PA/%FD%05/%00%00");
+ BOOST_CHECK_EQUAL(data.getContentType(), tlv::ContentType_PrefixAnn);
+ BOOST_REQUIRE(pa.getData());
+ BOOST_CHECK_EQUAL(*pa.getData(), data);
+
+ PrefixAnnouncement decoded(data);
+ BOOST_CHECK_EQUAL(decoded.getAnnouncedName(), "/");
+ BOOST_CHECK_EQUAL(decoded.getExpiration(), 0_s);
+ BOOST_CHECK(!decoded.getValidityPeriod());
+
+ BOOST_CHECK_EQUAL(pa, decoded);
+}
+
+BOOST_FIXTURE_TEST_CASE(EncodeNoValidity, IdentityManagementFixture)
+{
+ PrefixAnnouncement pa;
+ pa.setAnnouncedName("/net/example");
+ BOOST_CHECK_THROW(pa.setExpiration(-1_ms), std::invalid_argument);
+ pa.setExpiration(1_h);
+
+ const Data& data = pa.toData(m_keyChain, signingWithSha256(), 1);
+ BOOST_CHECK_EQUAL(data.getName(), "/net/example/32=PA/%FD%01/%00%00");
+ BOOST_CHECK_EQUAL(data.getContentType(), tlv::ContentType_PrefixAnn);
+
+ const Block& payload = data.getContent();
+ payload.parse();
+ BOOST_CHECK_EQUAL(readNonNegativeInteger(payload.get(tlv::nfd::ExpirationPeriod)), 3600000);
+ BOOST_CHECK(payload.find(tlv::ValidityPeriod) == payload.elements_end());
+
+ PrefixAnnouncement decoded(data);
+ BOOST_CHECK_EQUAL(decoded.getAnnouncedName(), "/net/example");
+ BOOST_CHECK_EQUAL(decoded.getExpiration(), 1_h);
+ BOOST_CHECK(!decoded.getValidityPeriod());
+
+ BOOST_CHECK_EQUAL(pa, decoded);
+}
+
+BOOST_FIXTURE_TEST_CASE(EncodeWithValidity, IdentityManagementFixture)
+{
+ PrefixAnnouncement pa;
+ pa.setAnnouncedName("/net/example");
+ pa.setExpiration(1_h);
+ security::ValidityPeriod validity(time::fromIsoString("20181030T000000"),
+ time::fromIsoString("20181124T235959"));
+ pa.setValidityPeriod(validity);
+
+ const Data& data = pa.toData(m_keyChain);
+ const Block& payload = data.getContent();
+ payload.parse();
+ BOOST_CHECK_EQUAL(readNonNegativeInteger(payload.get(tlv::nfd::ExpirationPeriod)), 3600000);
+ BOOST_CHECK_EQUAL(payload.get(tlv::ValidityPeriod), validity.wireEncode());
+
+ PrefixAnnouncement decoded(data);
+ BOOST_CHECK_EQUAL(decoded.getAnnouncedName(), "/net/example");
+ BOOST_CHECK_EQUAL(decoded.getExpiration(), 1_h);
+ BOOST_REQUIRE(decoded.getValidityPeriod());
+ BOOST_CHECK_EQUAL(*decoded.getValidityPeriod(), validity);
+
+ BOOST_CHECK_EQUAL(pa, decoded);
+}
+
+BOOST_AUTO_TEST_CASE(Modify)
+{
+ PrefixAnnouncement pa(makePrefixAnnData());
+
+ PrefixAnnouncement pa0(pa);
+ BOOST_REQUIRE(pa0.getData());
+ BOOST_CHECK_EQUAL(*pa0.getData(), makePrefixAnnData());
+ pa0.setAnnouncedName("/com/example");
+ BOOST_CHECK(!pa0.getData());
+ BOOST_CHECK_NE(pa0, pa);
+
+ PrefixAnnouncement pa1(makePrefixAnnData());
+ pa1.setExpiration(5_min);
+ BOOST_CHECK(!pa1.getData());
+ BOOST_CHECK_NE(pa1, pa);
+
+ PrefixAnnouncement pa2(makePrefixAnnData());
+ pa2.setValidityPeriod(security::ValidityPeriod(time::fromIsoString("20180118T000000"),
+ time::fromIsoString("20180212T235959")));
+ BOOST_CHECK(!pa2.getData());
+ BOOST_CHECK_NE(pa2, pa);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestPrefixAnnouncement
+
+} // namespace tests
+} // namespace ndn
diff --git a/tests/unit/security/command-interest-signer.t.cpp b/tests/unit/security/command-interest-signer.t.cpp
new file mode 100644
index 0000000..784a209
--- /dev/null
+++ b/tests/unit/security/command-interest-signer.t.cpp
@@ -0,0 +1,69 @@
+/* -*- 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 "security/command-interest-signer.hpp"
+#include "security/signing-helpers.hpp"
+
+#include "boost-test.hpp"
+#include "../identity-management-time-fixture.hpp"
+
+namespace ndn {
+namespace security {
+namespace tests {
+
+using namespace ndn::tests;
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_FIXTURE_TEST_SUITE(TestCommandInterestSigner, IdentityManagementTimeFixture)
+
+BOOST_AUTO_TEST_CASE(Basic)
+{
+ addIdentity("/test");
+
+ CommandInterestSigner signer(m_keyChain);
+ Interest i1 = signer.makeCommandInterest("/hello/world");
+ BOOST_CHECK_EQUAL(i1.getName().size(), 6);
+ BOOST_CHECK_EQUAL(i1.getName().at(command_interest::POS_SIG_VALUE).blockFromValue().type(), tlv::SignatureValue);
+ BOOST_CHECK_EQUAL(i1.getName().at(command_interest::POS_SIG_INFO).blockFromValue().type(), tlv::SignatureInfo);
+
+ time::milliseconds timestamp = toUnixTimestamp(time::system_clock::now());
+ BOOST_CHECK_EQUAL(i1.getName().at(command_interest::POS_TIMESTAMP).toNumber(), timestamp.count());
+
+ Interest i2 = signer.makeCommandInterest("/hello/world/!", signingByIdentity("/test"));
+ BOOST_CHECK_EQUAL(i2.getName().size(), 7);
+ BOOST_CHECK_EQUAL(i2.getName().at(command_interest::POS_SIG_VALUE).blockFromValue().type(), tlv::SignatureValue);
+ BOOST_CHECK_EQUAL(i2.getName().at(command_interest::POS_SIG_INFO).blockFromValue().type(), tlv::SignatureInfo);
+ BOOST_CHECK_GT(i2.getName().at(command_interest::POS_TIMESTAMP), i1.getName().at(command_interest::POS_TIMESTAMP));
+ BOOST_CHECK_NE(i2.getName().at(command_interest::POS_RANDOM_VAL),
+ i1.getName().at(command_interest::POS_RANDOM_VAL)); // this sometimes can fail
+
+ advanceClocks(100_s);
+
+ i2 = signer.makeCommandInterest("/hello/world/!");
+ BOOST_CHECK_GT(i2.getName().at(command_interest::POS_TIMESTAMP), i1.getName().at(command_interest::POS_TIMESTAMP));
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestCommandInterestSigner
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/config-file-readme.txt b/tests/unit/security/config-file-readme.txt
new file mode 100644
index 0000000..44e5f61
--- /dev/null
+++ b/tests/unit/security/config-file-readme.txt
@@ -0,0 +1,11 @@
+In test, we set a test-specific "HOME", which cause OS X keychain look for the
+default keychain of a "different" user. If the default keychain does not exist,
+all subsequent calls to OS X keychain will fail. User interaction (such as
+specifying password) is required to create a keychain. However, user interaction
+is not feasible in automated tests.
+
+This problem is caused by the OS X system assumption that one user must have a
+login keychain, which is also the user's default keychain, because a user
+account is always created with a login keychain as default. Thus OS X system
+infers a user according to the HOME env, and did not expect user to change the
+HOME env in normal use.
diff --git a/tests/unit/security/digest-sha256.t.cpp b/tests/unit/security/digest-sha256.t.cpp
new file mode 100644
index 0000000..7c1d236
--- /dev/null
+++ b/tests/unit/security/digest-sha256.t.cpp
@@ -0,0 +1,74 @@
+/* -*- 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 "security/digest-sha256.hpp"
+#include "security/verification-helpers.hpp"
+#include "util/sha256.hpp"
+#include "util/string-helper.hpp"
+
+#include "identity-management-fixture.hpp"
+#include "boost-test.hpp"
+
+namespace ndn {
+namespace security {
+namespace tests {
+
+using namespace ndn::tests;
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_FIXTURE_TEST_SUITE(TestDigestSha256, IdentityManagementFixture)
+
+BOOST_AUTO_TEST_CASE(Sha256)
+{
+ char content[6] = "1234\n";
+ ConstBufferPtr buf = util::Sha256::computeDigest(reinterpret_cast<uint8_t*>(content), 5);
+
+ BOOST_CHECK_EQUAL(toHex(buf->data(), buf->size(), false),
+ "a883dafc480d466ee04e0d6da986bd78eb1fdd2178d04693723da3a8f95d42f4");
+}
+
+BOOST_AUTO_TEST_CASE(DataSignature)
+{
+ Name name("/TestSignatureSha/Basic");
+ Data testData(name);
+ char content[5] = "1234";
+ testData.setContent(reinterpret_cast<uint8_t*>(content), 5);
+ m_keyChain.sign(testData, security::SigningInfo(security::SigningInfo::SIGNER_TYPE_SHA256));
+
+ BOOST_CHECK_THROW(testData.getSignature().getKeyLocator(), ndn::SignatureInfo::Error);
+ verifyDigest(testData, DigestAlgorithm::SHA256);
+}
+
+BOOST_AUTO_TEST_CASE(InterestSignature)
+{
+ Name name("/SecurityTestDigestSha256/InterestSignature/Interest1");
+ Interest testInterest(name);
+
+ m_keyChain.sign(testInterest, security::SigningInfo(security::SigningInfo::SIGNER_TYPE_SHA256));
+ verifyDigest(testInterest, DigestAlgorithm::SHA256);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestDigestSha256
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/key-params.t.cpp b/tests/unit/security/key-params.t.cpp
new file mode 100644
index 0000000..991c346
--- /dev/null
+++ b/tests/unit/security/key-params.t.cpp
@@ -0,0 +1,122 @@
+/* -*- 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 "security/key-params.hpp"
+
+#include "boost-test.hpp"
+
+#include <boost/lexical_cast.hpp>
+
+namespace ndn {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(TestKeyParams)
+
+BOOST_AUTO_TEST_CASE(Rsa)
+{
+ RsaKeyParams params;
+ BOOST_CHECK_EQUAL(params.getKeyType(), KeyType::RSA);
+ BOOST_CHECK_EQUAL(params.getKeySize(), 2048);
+ BOOST_CHECK(params.getKeyIdType() == KeyIdType::RANDOM);
+
+ RsaKeyParams params2(4096, KeyIdType::SHA256);
+ BOOST_CHECK_EQUAL(params2.getKeyType(), KeyType::RSA);
+ BOOST_CHECK_EQUAL(params2.getKeySize(), 4096);
+ BOOST_CHECK(params2.getKeyIdType() == KeyIdType::SHA256);
+
+ BOOST_CHECK_THROW(RsaKeyParams(1024), KeyParams::Error);
+
+ name::Component keyId("keyId");
+ RsaKeyParams params4(keyId);
+ BOOST_CHECK(params4.getKeyType() == KeyType::RSA);
+ BOOST_CHECK_EQUAL(params4.getKeySize(), 2048);
+ BOOST_CHECK(params4.getKeyIdType() == KeyIdType::USER_SPECIFIED);
+ BOOST_CHECK_EQUAL(params4.getKeyId(), keyId);
+}
+
+BOOST_AUTO_TEST_CASE(Ec)
+{
+ EcKeyParams params;
+ BOOST_CHECK_EQUAL(params.getKeyType(), KeyType::EC);
+ BOOST_CHECK_EQUAL(params.getKeySize(), 256);
+ BOOST_CHECK(params.getKeyIdType() == KeyIdType::RANDOM);
+
+ EcKeyParams params2(384, KeyIdType::SHA256);
+ BOOST_CHECK_EQUAL(params2.getKeyType(), KeyType::EC);
+ BOOST_CHECK_EQUAL(params2.getKeySize(), 384);
+ BOOST_CHECK(params2.getKeyIdType() == KeyIdType::SHA256);
+
+ BOOST_CHECK_THROW(EcKeyParams(3), KeyParams::Error);
+
+ name::Component keyId("keyId");
+ EcKeyParams params4(keyId);
+ BOOST_CHECK(params4.getKeyType() == KeyType::EC);
+ BOOST_CHECK_EQUAL(params4.getKeySize(), 256);
+ BOOST_CHECK(params4.getKeyIdType() == KeyIdType::USER_SPECIFIED);
+ BOOST_CHECK_EQUAL(params4.getKeyId(), keyId);
+}
+
+BOOST_AUTO_TEST_CASE(Aes)
+{
+ name::Component keyId("keyId");
+ AesKeyParams params(keyId);
+ BOOST_CHECK_EQUAL(params.getKeyType(), KeyType::AES);
+ BOOST_CHECK_EQUAL(params.getKeySize(), 128);
+ BOOST_CHECK_EQUAL(params.getKeyIdType(), KeyIdType::USER_SPECIFIED);
+
+ AesKeyParams params2(keyId, 192);
+ BOOST_CHECK(params2.getKeyType() == KeyType::AES);
+ BOOST_CHECK_EQUAL(params2.getKeySize(), 192);
+ BOOST_CHECK(params.getKeyIdType() == KeyIdType::USER_SPECIFIED);
+
+ AesKeyParams params3(keyId, 256);
+ BOOST_CHECK_EQUAL(params3.getKeyType(), KeyType::AES);
+ BOOST_CHECK_EQUAL(params3.getKeySize(), 256);
+ BOOST_CHECK(params.getKeyIdType() == KeyIdType::USER_SPECIFIED);
+
+ BOOST_CHECK_THROW(AesKeyParams(keyId, 4), KeyParams::Error);
+
+ AesKeyParams params5(keyId);
+ BOOST_CHECK_EQUAL(params5.getKeyType(), KeyType::AES);
+ BOOST_CHECK_EQUAL(params5.getKeySize(), 128);
+ BOOST_CHECK_EQUAL(params5.getKeyIdType(), KeyIdType::USER_SPECIFIED);
+ BOOST_CHECK_EQUAL(params5.getKeyId(), keyId);
+
+ AesKeyParams params6(192);
+ BOOST_CHECK(params6.getKeyType() == KeyType::AES);
+ BOOST_CHECK_EQUAL(params6.getKeySize(), 192);
+ BOOST_CHECK(params6.getKeyIdType() == KeyIdType::RANDOM);
+}
+
+BOOST_AUTO_TEST_CASE(KeyIdTypeInfo)
+{
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(KeyIdType::USER_SPECIFIED), "USER_SPECIFIED");
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(KeyIdType::SHA256), "SHA256");
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(KeyIdType::RANDOM), "RANDOM");
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(static_cast<KeyIdType>(12345)), "12345");
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestKeyParams
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace ndn
diff --git a/tests/unit/security/pib/certificate-container.t.cpp b/tests/unit/security/pib/certificate-container.t.cpp
new file mode 100644
index 0000000..6614531
--- /dev/null
+++ b/tests/unit/security/pib/certificate-container.t.cpp
@@ -0,0 +1,167 @@
+/* -*- 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 "security/pib/certificate-container.hpp"
+#include "security/pib/pib.hpp"
+#include "security/pib/pib-memory.hpp"
+
+#include "boost-test.hpp"
+#include "pib-data-fixture.hpp"
+
+namespace ndn {
+namespace security {
+namespace pib {
+namespace tests {
+
+using namespace ndn::security::tests;
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(Pib)
+BOOST_FIXTURE_TEST_SUITE(TestCertificateContainer, PibDataFixture)
+
+using pib::Pib;
+
+BOOST_AUTO_TEST_CASE(Basic)
+{
+ auto pibImpl = make_shared<PibMemory>();
+
+ // start with an empty container
+ CertificateContainer container(id1Key1Name, pibImpl);
+ BOOST_CHECK_EQUAL(container.size(), 0);
+ BOOST_CHECK_EQUAL(container.getCache().size(), 0);
+
+ // add one cert
+ container.add(id1Key1Cert1);
+ BOOST_CHECK_EQUAL(container.size(), 1);
+ BOOST_CHECK_EQUAL(container.getCache().size(), 1);
+ BOOST_CHECK(container.find(id1Key1Cert1.getName()) != container.end());
+
+ // add the same cert again
+ container.add(id1Key1Cert1);
+ BOOST_CHECK_EQUAL(container.size(), 1);
+ BOOST_CHECK_EQUAL(container.getCache().size(), 1);
+ BOOST_CHECK(container.find(id1Key1Cert1.getName()) != container.end());
+
+ // add another cert
+ container.add(id1Key1Cert2);
+ BOOST_CHECK_EQUAL(container.size(), 2);
+ BOOST_CHECK_EQUAL(container.getCache().size(), 2);
+ BOOST_CHECK(container.find(id1Key1Cert1.getName()) != container.end());
+ BOOST_CHECK(container.find(id1Key1Cert2.getName()) != container.end());
+
+ // get certs
+ BOOST_REQUIRE_NO_THROW(container.get(id1Key1Cert1.getName()));
+ BOOST_REQUIRE_NO_THROW(container.get(id1Key1Cert2.getName()));
+ Name id1Key1Cert3Name = id1Key1Name;
+ id1Key1Cert3Name.append("issuer").appendVersion(3);
+ BOOST_CHECK_THROW(container.get(id1Key1Cert3Name), Pib::Error);
+
+ // check cert
+ v2::Certificate cert1 = container.get(id1Key1Cert1.getName());
+ v2::Certificate cert2 = container.get(id1Key1Cert2.getName());
+ BOOST_CHECK_EQUAL(cert1, id1Key1Cert1);
+ BOOST_CHECK_EQUAL(cert2, id1Key1Cert2);
+
+ // create another container from the same PibImpl
+ // cache should be empty
+ CertificateContainer container2(id1Key1Name, pibImpl);
+ BOOST_CHECK_EQUAL(container2.size(), 2);
+ BOOST_CHECK_EQUAL(container2.getCache().size(), 0);
+
+ // get certificate, cache should be filled
+ BOOST_REQUIRE_NO_THROW(container2.get(id1Key1Cert1.getName()));
+ BOOST_CHECK_EQUAL(container2.size(), 2);
+ BOOST_CHECK_EQUAL(container2.getCache().size(), 1);
+
+ BOOST_REQUIRE_NO_THROW(container2.get(id1Key1Cert2.getName()));
+ BOOST_CHECK_EQUAL(container2.size(), 2);
+ BOOST_CHECK_EQUAL(container2.getCache().size(), 2);
+
+ // remove a certificate
+ container2.remove(id1Key1Cert1.getName());
+ BOOST_CHECK_EQUAL(container2.size(), 1);
+ BOOST_CHECK_EQUAL(container2.getCache().size(), 1);
+ BOOST_CHECK(container2.find(id1Key1Cert1.getName()) == container2.end());
+ BOOST_CHECK(container2.find(id1Key1Cert2.getName()) != container2.end());
+
+ // remove another certificate
+ container2.remove(id1Key1Cert2.getName());
+ BOOST_CHECK_EQUAL(container2.size(), 0);
+ BOOST_CHECK_EQUAL(container2.getCache().size(), 0);
+ BOOST_CHECK(container2.find(id1Key1Cert2.getName()) == container2.end());
+}
+
+BOOST_AUTO_TEST_CASE(Errors)
+{
+ auto pibImpl = make_shared<PibMemory>();
+
+ CertificateContainer container(id1Key1Name, pibImpl);
+
+ BOOST_CHECK_THROW(container.add(id1Key2Cert1), std::invalid_argument);
+ BOOST_CHECK_THROW(container.remove(id1Key2Cert1.getName()), std::invalid_argument);
+ BOOST_CHECK_THROW(container.get(id1Key2Cert1.getName()), std::invalid_argument);
+}
+
+BOOST_AUTO_TEST_CASE(Iterator)
+{
+ auto pibImpl = make_shared<PibMemory>();
+
+ // start with an empty container
+ CertificateContainer container(id1Key1Name, pibImpl);
+ container.add(id1Key1Cert1);
+ container.add(id1Key1Cert2);
+
+ std::set<Name> certNames;
+ certNames.insert(id1Key1Cert1.getName());
+ certNames.insert(id1Key1Cert2.getName());
+
+ CertificateContainer::const_iterator it = container.begin();
+ std::set<Name>::const_iterator testIt = certNames.begin();
+ BOOST_CHECK_EQUAL((*it).getName(), *testIt);
+ it++;
+ testIt++;
+ BOOST_CHECK_EQUAL((*it).getName(), *testIt);
+ ++it;
+ testIt++;
+ BOOST_CHECK(it == container.end());
+
+ size_t count = 0;
+ testIt = certNames.begin();
+ for (const auto& cert : container) {
+ BOOST_CHECK_EQUAL(cert.getName(), *testIt);
+ testIt++;
+ count++;
+ }
+ BOOST_CHECK_EQUAL(count, 2);
+
+ BOOST_CHECK(CertificateContainer::const_iterator() == CertificateContainer::const_iterator());
+ BOOST_CHECK(CertificateContainer::const_iterator() == container.end());
+ BOOST_CHECK(container.end() == CertificateContainer::const_iterator());
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestCertificateContainer
+BOOST_AUTO_TEST_SUITE_END() // Pib
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace pib
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/pib/detail/identity-impl.t.cpp b/tests/unit/security/pib/detail/identity-impl.t.cpp
new file mode 100644
index 0000000..209a6b6
--- /dev/null
+++ b/tests/unit/security/pib/detail/identity-impl.t.cpp
@@ -0,0 +1,160 @@
+/* -*- 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 "security/pib/detail/identity-impl.hpp"
+#include "security/pib/pib.hpp"
+#include "security/pib/pib-memory.hpp"
+
+#include "boost-test.hpp"
+#include "../pib-data-fixture.hpp"
+
+namespace ndn {
+namespace security {
+namespace pib {
+namespace detail {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(Pib)
+BOOST_AUTO_TEST_SUITE(Detail)
+BOOST_FIXTURE_TEST_SUITE(TestIdentityImpl, ndn::security::tests::PibDataFixture)
+
+using security::Pib;
+
+BOOST_AUTO_TEST_CASE(Basic)
+{
+ auto pibImpl = make_shared<pib::PibMemory>();
+ IdentityImpl identity1(id1, pibImpl, true);
+
+ BOOST_CHECK_EQUAL(identity1.getName(), id1);
+}
+
+BOOST_AUTO_TEST_CASE(KeyOperation)
+{
+ auto pibImpl = make_shared<pib::PibMemory>();
+ IdentityImpl identity1(id1, pibImpl, true);
+ BOOST_CHECK_NO_THROW(IdentityImpl(id1, pibImpl, false));
+
+ // identity does not have any key
+ BOOST_CHECK_EQUAL(identity1.getKeys().size(), 0);
+
+ // get non-existing key, throw Pib::Error
+ BOOST_CHECK_THROW(identity1.getKey(id1Key1Name), Pib::Error);
+ // get default key, throw Pib::Error
+ BOOST_CHECK_THROW(identity1.getDefaultKey(), Pib::Error);
+ // set non-existing key as default key, throw Pib::Error
+ BOOST_REQUIRE_THROW(identity1.setDefaultKey(id1Key1Name), Pib::Error);
+
+ // add key
+ identity1.addKey(id1Key1.data(), id1Key1.size(), id1Key1Name);
+ BOOST_CHECK_NO_THROW(identity1.getKey(id1Key1Name));
+
+ // new key becomes default key when there is no default key
+ BOOST_REQUIRE_NO_THROW(identity1.getDefaultKey());
+ const Key& defaultKey0 = identity1.getDefaultKey();
+ BOOST_CHECK_EQUAL(defaultKey0.getName(), id1Key1Name);
+ BOOST_CHECK(defaultKey0.getPublicKey() == id1Key1);
+
+ // remove key
+ identity1.removeKey(id1Key1Name);
+ BOOST_CHECK_THROW(identity1.getKey(id1Key1Name), Pib::Error);
+ BOOST_CHECK_THROW(identity1.getDefaultKey(), Pib::Error);
+
+ // set default key directly
+ BOOST_REQUIRE_NO_THROW(identity1.setDefaultKey(id1Key1.data(), id1Key1.size(), id1Key1Name));
+ BOOST_REQUIRE_NO_THROW(identity1.getDefaultKey());
+ BOOST_CHECK_NO_THROW(identity1.getKey(id1Key1Name));
+
+ // check default key
+ const Key& defaultKey1 = identity1.getDefaultKey();
+ BOOST_CHECK_EQUAL(defaultKey1.getName(), id1Key1Name);
+ BOOST_CHECK(defaultKey1.getPublicKey() == id1Key1);
+
+ // add another key
+ identity1.addKey(id1Key2.data(), id1Key2.size(), id1Key2Name);
+ BOOST_CHECK_EQUAL(identity1.getKeys().size(), 2);
+
+ // set default key through name
+ BOOST_REQUIRE_NO_THROW(identity1.setDefaultKey(id1Key2Name));
+ BOOST_REQUIRE_NO_THROW(identity1.getDefaultKey());
+ const Key& defaultKey2 = identity1.getDefaultKey();
+ BOOST_CHECK_EQUAL(defaultKey2.getName(), id1Key2Name);
+ BOOST_CHECK(defaultKey2.getPublicKey() == id1Key2);
+
+ // remove key
+ identity1.removeKey(id1Key1Name);
+ BOOST_CHECK_THROW(identity1.getKey(id1Key1Name), Pib::Error);
+ BOOST_CHECK_EQUAL(identity1.getKeys().size(), 1);
+
+ // set default key directly again, change the default setting
+ BOOST_REQUIRE_NO_THROW(identity1.setDefaultKey(id1Key1.data(), id1Key1.size(), id1Key1Name));
+ const Key& defaultKey3 = identity1.getDefaultKey();
+ BOOST_CHECK_EQUAL(defaultKey3.getName(), id1Key1Name);
+ BOOST_CHECK(defaultKey3.getPublicKey() == id1Key1);
+ BOOST_CHECK_EQUAL(identity1.getKeys().size(), 2);
+
+ // remove all keys
+ identity1.removeKey(id1Key1Name);
+ BOOST_CHECK_THROW(identity1.getKey(id1Key1Name), Pib::Error);
+ BOOST_CHECK_EQUAL(identity1.getKeys().size(), 1);
+ identity1.removeKey(id1Key2Name);
+ BOOST_CHECK_THROW(identity1.getKey(id1Key2Name), Pib::Error);
+ BOOST_CHECK_EQUAL(identity1.getKeys().size(), 0);
+ BOOST_CHECK_THROW(identity1.getDefaultKey(), Pib::Error);
+}
+
+BOOST_AUTO_TEST_CASE(Overwrite)
+{
+ auto pibImpl = make_shared<pib::PibMemory>();
+ IdentityImpl identity1(id1, pibImpl, true);
+
+ identity1.addKey(id1Key1.data(), id1Key1.size(), id1Key1Name);
+ BOOST_CHECK(identity1.getKey(id1Key1Name).getPublicKey() == id1Key1);
+
+ identity1.addKey(id1Key2.data(), id1Key2.size(), id1Key1Name); // overwriting key should work
+ BOOST_CHECK(identity1.getKey(id1Key1Name).getPublicKey() == id1Key2);
+}
+
+BOOST_AUTO_TEST_CASE(Errors)
+{
+ auto pibImpl = make_shared<pib::PibMemory>();
+
+ BOOST_CHECK_THROW(IdentityImpl(id1, pibImpl, false), Pib::Error);
+ IdentityImpl identity1(id1, pibImpl, true);
+
+ identity1.addKey(id1Key1.data(), id1Key1.size(), id1Key1Name);
+ BOOST_CHECK_THROW(identity1.addKey(id2Key1.data(), id2Key1.size(), id2Key1Name), std::invalid_argument);
+ BOOST_CHECK_THROW(identity1.removeKey(id2Key1Name), std::invalid_argument);
+ BOOST_CHECK_THROW(identity1.getKey(id2Key1Name), std::invalid_argument);
+ BOOST_CHECK_THROW(identity1.setDefaultKey(id2Key1.data(), id2Key1.size(), id2Key1Name), std::invalid_argument);
+ BOOST_CHECK_THROW(identity1.setDefaultKey(id2Key1Name), std::invalid_argument);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestIdentityImpl
+BOOST_AUTO_TEST_SUITE_END() // Detail
+BOOST_AUTO_TEST_SUITE_END() // Pib
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace detail
+} // namespace pib
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/pib/detail/key-impl.t.cpp b/tests/unit/security/pib/detail/key-impl.t.cpp
new file mode 100644
index 0000000..517ea60
--- /dev/null
+++ b/tests/unit/security/pib/detail/key-impl.t.cpp
@@ -0,0 +1,200 @@
+/* -*- 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 "security/pib/detail/key-impl.hpp"
+#include "security/pib/pib.hpp"
+#include "security/pib/pib-memory.hpp"
+#include "../pib-data-fixture.hpp"
+
+#include "boost-test.hpp"
+#include "identity-management-fixture.hpp"
+
+namespace ndn {
+namespace security {
+namespace pib {
+namespace detail {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(Pib)
+BOOST_AUTO_TEST_SUITE(Detail)
+BOOST_FIXTURE_TEST_SUITE(TestKeyImpl, security::tests::PibDataFixture)
+
+using security::Pib;
+
+BOOST_AUTO_TEST_CASE(Basic)
+{
+ auto pibImpl = make_shared<pib::PibMemory>();
+ KeyImpl key11(id1Key1Name, id1Key1.data(), id1Key1.size(), pibImpl);
+
+ BOOST_CHECK_EQUAL(key11.getName(), id1Key1Name);
+ BOOST_CHECK_EQUAL(key11.getIdentity(), id1);
+ BOOST_CHECK_EQUAL(key11.getKeyType(), KeyType::EC);
+ BOOST_CHECK(key11.getPublicKey() == id1Key1);
+
+ KeyImpl key11Bak(id1Key1Name, pibImpl);
+ BOOST_CHECK_EQUAL(key11Bak.getName(), id1Key1Name);
+ BOOST_CHECK_EQUAL(key11Bak.getIdentity(), id1);
+ BOOST_CHECK_EQUAL(key11Bak.getKeyType(), KeyType::EC);
+ BOOST_CHECK(key11Bak.getPublicKey() == id1Key1);
+}
+
+BOOST_AUTO_TEST_CASE(CertificateOperation)
+{
+ auto pibImpl = make_shared<pib::PibMemory>();
+ KeyImpl key11(id1Key1Name, id1Key1.data(), id1Key1.size(), pibImpl);
+ BOOST_CHECK_NO_THROW(KeyImpl(id1Key1Name, pibImpl));
+
+ // key does not have any certificate
+ BOOST_CHECK_EQUAL(key11.getCertificates().size(), 0);
+
+ // get non-existing certificate, throw Pib::Error
+ BOOST_CHECK_THROW(key11.getCertificate(id1Key1Cert1.getName()), Pib::Error);
+ // get default certificate, throw Pib::Error
+ BOOST_CHECK_THROW(key11.getDefaultCertificate(), Pib::Error);
+ // set non-existing certificate as default certificate, throw Pib::Error
+ BOOST_REQUIRE_THROW(key11.setDefaultCertificate(id1Key1Cert1.getName()), Pib::Error);
+
+ // add certificate
+ key11.addCertificate(id1Key1Cert1);
+ BOOST_CHECK_NO_THROW(key11.getCertificate(id1Key1Cert1.getName()));
+
+ // new certificate becomes default certificate when there was no default certificate
+ BOOST_REQUIRE_NO_THROW(key11.getDefaultCertificate());
+ const auto& defaultCert0 = key11.getDefaultCertificate();
+ BOOST_CHECK_EQUAL(defaultCert0.getName(), id1Key1Cert1.getName());
+ BOOST_CHECK_EQUAL(defaultCert0, id1Key1Cert1);
+
+ // remove certificate
+ key11.removeCertificate(id1Key1Cert1.getName());
+ BOOST_CHECK_THROW(key11.getCertificate(id1Key1Cert1.getName()), Pib::Error);
+ BOOST_CHECK_THROW(key11.getDefaultCertificate(), Pib::Error);
+
+ // set default certificate directly
+ BOOST_REQUIRE_NO_THROW(key11.setDefaultCertificate(id1Key1Cert1));
+ BOOST_REQUIRE_NO_THROW(key11.getDefaultCertificate());
+ BOOST_CHECK_NO_THROW(key11.getCertificate(id1Key1Cert1.getName()));
+
+ // check default cert
+ const auto& defaultCert1 = key11.getDefaultCertificate();
+ BOOST_CHECK_EQUAL(defaultCert1.getName(), id1Key1Cert1.getName());
+ BOOST_CHECK_EQUAL(defaultCert1, id1Key1Cert1);
+
+ // add another certificate
+ key11.addCertificate(id1Key1Cert2);
+ BOOST_CHECK_EQUAL(key11.getCertificates().size(), 2);
+
+ // set default certificate through name
+ BOOST_REQUIRE_NO_THROW(key11.setDefaultCertificate(id1Key1Cert2.getName()));
+ BOOST_REQUIRE_NO_THROW(key11.getDefaultCertificate());
+ const auto& defaultCert2 = key11.getDefaultCertificate();
+ BOOST_CHECK_EQUAL(defaultCert2.getName(), id1Key1Cert2.getName());
+ BOOST_CHECK_EQUAL(defaultCert2, id1Key1Cert2);
+
+ // remove certificate
+ key11.removeCertificate(id1Key1Cert1.getName());
+ BOOST_CHECK_THROW(key11.getCertificate(id1Key1Cert1.getName()), Pib::Error);
+ BOOST_CHECK_EQUAL(key11.getCertificates().size(), 1);
+
+ // set default certificate directly again, change the default setting
+ BOOST_REQUIRE_NO_THROW(key11.setDefaultCertificate(id1Key1Cert1));
+ const auto& defaultCert3 = key11.getDefaultCertificate();
+ BOOST_CHECK_EQUAL(defaultCert3.getName(), id1Key1Cert1.getName());
+ BOOST_CHECK_EQUAL(defaultCert3, id1Key1Cert1);
+ BOOST_CHECK_EQUAL(key11.getCertificates().size(), 2);
+
+ // remove all certificates
+ key11.removeCertificate(id1Key1Cert1.getName());
+ BOOST_CHECK_THROW(key11.getCertificate(id1Key1Cert1.getName()), Pib::Error);
+ BOOST_CHECK_EQUAL(key11.getCertificates().size(), 1);
+ key11.removeCertificate(id1Key1Cert2.getName());
+ BOOST_CHECK_THROW(key11.getCertificate(id1Key1Cert2.getName()), Pib::Error);
+ BOOST_CHECK_THROW(key11.getDefaultCertificate(), Pib::Error);
+ BOOST_CHECK_EQUAL(key11.getCertificates().size(), 0);
+}
+
+class OverwriteFixture : public ndn::security::tests::PibDataFixture,
+ public ndn::tests::IdentityManagementFixture
+{
+};
+
+BOOST_FIXTURE_TEST_CASE(Overwrite, OverwriteFixture)
+{
+ auto pibImpl = make_shared<pib::PibMemory>();
+
+ BOOST_CHECK_THROW(KeyImpl(id1Key1Name, pibImpl), Pib::Error);
+ KeyImpl(id1Key1Name, id1Key1.data(), id1Key1.size(), pibImpl);
+ KeyImpl key1(id1Key1Name, pibImpl);
+
+ KeyImpl(id1Key1Name, id1Key2.data(), id1Key2.size(), pibImpl); // overwriting of the key should work
+ KeyImpl key2(id1Key1Name, pibImpl);
+
+ BOOST_CHECK(key1.getPublicKey() != key2.getPublicKey()); // key1 cached the original public key
+ BOOST_CHECK(key2.getPublicKey() == id1Key2);
+
+ key1.addCertificate(id1Key1Cert1);
+ BOOST_CHECK_EQUAL(key1.getCertificate(id1Key1Cert1.getName()), id1Key1Cert1);
+
+ auto otherCert = id1Key1Cert1;
+ SignatureInfo info;
+ info.setValidityPeriod(ValidityPeriod(time::system_clock::now(),
+ time::system_clock::now() + 1_s));
+ m_keyChain.sign(otherCert, SigningInfo().setSignatureInfo(info));
+
+ BOOST_CHECK_EQUAL(otherCert.getName(), id1Key1Cert1.getName());
+ BOOST_CHECK(otherCert.getContent() == id1Key1Cert1.getContent());
+ BOOST_CHECK_NE(otherCert, id1Key1Cert1);
+
+ key1.addCertificate(otherCert);
+
+ BOOST_CHECK_EQUAL(key1.getCertificate(id1Key1Cert1.getName()), otherCert);
+}
+
+BOOST_AUTO_TEST_CASE(Errors)
+{
+ auto pibImpl = make_shared<pib::PibMemory>();
+
+ BOOST_CHECK_THROW(KeyImpl(id1Key1Name, pibImpl), Pib::Error);
+ KeyImpl key11(id1Key1Name, id1Key1.data(), id1Key1.size(), pibImpl);
+
+ BOOST_CHECK_THROW(KeyImpl(Name("/wrong"), pibImpl), std::invalid_argument);
+ BOOST_CHECK_THROW(KeyImpl(Name("/wrong"), id1Key1.data(), id1Key1.size(), pibImpl), std::invalid_argument);
+ Buffer wrongKey;
+ BOOST_CHECK_THROW(KeyImpl(id1Key2Name, wrongKey.data(), wrongKey.size(), pibImpl), std::invalid_argument);
+
+ key11.addCertificate(id1Key1Cert1);
+ BOOST_CHECK_THROW(key11.addCertificate(id1Key2Cert1), std::invalid_argument);
+ BOOST_CHECK_THROW(key11.removeCertificate(id1Key2Cert1.getName()), std::invalid_argument);
+ BOOST_CHECK_THROW(key11.getCertificate(id1Key2Cert1.getName()), std::invalid_argument);
+ BOOST_CHECK_THROW(key11.setDefaultCertificate(id1Key2Cert1), std::invalid_argument);
+ BOOST_CHECK_THROW(key11.setDefaultCertificate(id1Key2Cert1.getName()), std::invalid_argument);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestKeyImpl
+BOOST_AUTO_TEST_SUITE_END() // Detail
+BOOST_AUTO_TEST_SUITE_END() // Pib
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace detail
+} // namespace pib
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/pib/identity-container.t.cpp b/tests/unit/security/pib/identity-container.t.cpp
new file mode 100644
index 0000000..e821970
--- /dev/null
+++ b/tests/unit/security/pib/identity-container.t.cpp
@@ -0,0 +1,156 @@
+/* -*- 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 "security/pib/identity-container.hpp"
+#include "security/pib/pib.hpp"
+#include "security/pib/pib-memory.hpp"
+
+#include "boost-test.hpp"
+#include "pib-data-fixture.hpp"
+
+namespace ndn {
+namespace security {
+namespace pib {
+namespace tests {
+
+using namespace ndn::security::tests;
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(Pib)
+BOOST_FIXTURE_TEST_SUITE(TestIdentityContainer, PibDataFixture)
+
+using pib::Pib;
+
+BOOST_AUTO_TEST_CASE(Basic)
+{
+ auto pibImpl = make_shared<PibMemory>();
+
+ // start with an empty container
+ IdentityContainer container(pibImpl);
+ BOOST_CHECK_EQUAL(container.size(), 0);
+ BOOST_CHECK_EQUAL(container.getLoadedIdentities().size(), 0);
+
+ // add the first identity
+ Identity identity11 = container.add(id1);
+ BOOST_CHECK_EQUAL(identity11.getName(), id1);
+ BOOST_CHECK_EQUAL(container.size(), 1);
+ BOOST_CHECK_EQUAL(container.getLoadedIdentities().size(), 1);
+ BOOST_CHECK(container.find(id1) != container.end());
+
+ // add the same identity again
+ Identity identity12 = container.add(id1);
+ BOOST_CHECK_EQUAL(identity12.getName(), id1);
+ BOOST_CHECK_EQUAL(container.size(), 1);
+ BOOST_CHECK_EQUAL(container.getLoadedIdentities().size(), 1);
+ BOOST_CHECK(container.find(id1) != container.end());
+
+ // add the second identity
+ Identity identity21 = container.add(id2);
+ BOOST_CHECK_EQUAL(identity21.getName(), id2);
+ BOOST_CHECK_EQUAL(container.size(), 2);
+ BOOST_CHECK_EQUAL(container.getLoadedIdentities().size(), 2);
+ BOOST_CHECK(container.find(id1) != container.end());
+ BOOST_CHECK(container.find(id2) != container.end());
+
+ // get identities
+ BOOST_REQUIRE_NO_THROW(container.get(id1));
+ BOOST_REQUIRE_NO_THROW(container.get(id2));
+ BOOST_CHECK_THROW(container.get(Name("/non-existing")), Pib::Error);
+
+ // check identity
+ Identity identity1 = container.get(id1);
+ Identity identity2 = container.get(id2);
+ BOOST_CHECK_EQUAL(identity1.getName(), id1);
+ BOOST_CHECK_EQUAL(identity2.getName(), id2);
+
+ // create another container from the same PibImpl
+ // cache should be empty
+ IdentityContainer container2(pibImpl);
+ BOOST_CHECK_EQUAL(container2.size(), 2);
+ BOOST_CHECK_EQUAL(container2.getLoadedIdentities().size(), 0);
+
+ // get key, cache should be filled
+ BOOST_REQUIRE_NO_THROW(container2.get(id1));
+ BOOST_CHECK_EQUAL(container2.size(), 2);
+ BOOST_CHECK_EQUAL(container2.getLoadedIdentities().size(), 1);
+
+ BOOST_REQUIRE_NO_THROW(container2.get(id2));
+ BOOST_CHECK_EQUAL(container2.size(), 2);
+ BOOST_CHECK_EQUAL(container2.getLoadedIdentities().size(), 2);
+
+ // remove a key
+ container2.remove(id1);
+ BOOST_CHECK_EQUAL(container2.size(), 1);
+ BOOST_CHECK_EQUAL(container2.getLoadedIdentities().size(), 1);
+ BOOST_CHECK(container2.find(id1) == container2.end());
+ BOOST_CHECK(container2.find(id2) != container2.end());
+
+ // remove another key
+ container2.remove(id2);
+ BOOST_CHECK_EQUAL(container2.size(), 0);
+ BOOST_CHECK_EQUAL(container2.getLoadedIdentities().size(), 0);
+ BOOST_CHECK(container2.find(id2) == container2.end());
+
+}
+
+BOOST_AUTO_TEST_CASE(Iterator)
+{
+ auto pibImpl = make_shared<PibMemory>();
+ IdentityContainer container(pibImpl);
+ container.add(id1);
+ container.add(id2);
+
+ std::set<Name> idNames;
+ idNames.insert(id1);
+ idNames.insert(id2);
+
+ IdentityContainer::const_iterator it = container.begin();
+ std::set<Name>::const_iterator testIt = idNames.begin();
+ BOOST_CHECK_EQUAL((*it).getName(), *testIt);
+ it++;
+ testIt++;
+ BOOST_CHECK_EQUAL((*it).getName(), *testIt);
+ ++it;
+ testIt++;
+ BOOST_CHECK(it == container.end());
+
+ size_t count = 0;
+ testIt = idNames.begin();
+ for (const auto& identity : container) {
+ BOOST_CHECK_EQUAL(identity.getName(), *testIt);
+ testIt++;
+ count++;
+ }
+ BOOST_CHECK_EQUAL(count, 2);
+
+ BOOST_CHECK(IdentityContainer::const_iterator() == IdentityContainer::const_iterator());
+ BOOST_CHECK(IdentityContainer::const_iterator() == container.end());
+ BOOST_CHECK(container.end() == IdentityContainer::const_iterator());
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestIdentityContainer
+BOOST_AUTO_TEST_SUITE_END() // Pib
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace pib
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/pib/identity.t.cpp b/tests/unit/security/pib/identity.t.cpp
new file mode 100644
index 0000000..d10c7bf
--- /dev/null
+++ b/tests/unit/security/pib/identity.t.cpp
@@ -0,0 +1,101 @@
+/* -*- 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 "security/pib/identity.hpp"
+#include "security/pib/pib.hpp"
+#include "security/pib/pib-memory.hpp"
+#include "security/pib/detail/identity-impl.hpp"
+
+#include "boost-test.hpp"
+#include "pib-data-fixture.hpp"
+
+namespace ndn {
+namespace security {
+namespace pib {
+namespace tests {
+
+using namespace ndn::security::tests;
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(Pib)
+BOOST_FIXTURE_TEST_SUITE(TestIdentity, PibDataFixture)
+
+using pib::Pib;
+
+BOOST_AUTO_TEST_CASE(ValidityChecking)
+{
+ using security::pib::detail::IdentityImpl;
+
+ Identity id;
+
+ BOOST_CHECK_EQUAL(static_cast<bool>(id), false);
+ BOOST_CHECK_EQUAL(!id, true);
+
+ if (id)
+ BOOST_CHECK(false);
+ else
+ BOOST_CHECK(true);
+
+ auto identityImpl = make_shared<IdentityImpl>(id1, make_shared<PibMemory>(), true);
+ id = Identity(identityImpl);
+
+ BOOST_CHECK_EQUAL(static_cast<bool>(id), true);
+ BOOST_CHECK_EQUAL(!id, false);
+
+ if (id)
+ BOOST_CHECK(true);
+ else
+ BOOST_CHECK(false);
+}
+
+/**
+ * pib::Identity is a wrapper of pib::detail::IdentityImpl. Since the functionalities of
+ * IdentityImpl have already been tested in detail/identity-impl.t.cpp, we only test the shared
+ * property of pib::Identity in this test case.
+ */
+BOOST_AUTO_TEST_CASE(Share)
+{
+ using security::pib::detail::IdentityImpl;
+
+ auto identityImpl = make_shared<IdentityImpl>(id1, make_shared<pib::PibMemory>(), true);
+ Identity identity1(identityImpl);
+ Identity identity2(identityImpl);
+ BOOST_CHECK_EQUAL(identity1, identity2);
+ BOOST_CHECK_NE(identity1, Identity());
+ BOOST_CHECK_EQUAL(Identity(), Identity());
+
+ identity1.addKey(id1Key1.data(), id1Key1.size(), id1Key1Name);
+ BOOST_CHECK_NO_THROW(identity2.getKey(id1Key1Name));
+ identity2.removeKey(id1Key1Name);
+ BOOST_CHECK_THROW(identity1.getKey(id1Key1Name), Pib::Error);
+
+ identity1.setDefaultKey(id1Key1.data(), id1Key1.size(), id1Key1Name);
+ BOOST_CHECK_NO_THROW(identity2.getDefaultKey());
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestIdentity
+BOOST_AUTO_TEST_SUITE_END() // Pib
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace pib
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/pib/key-container.t.cpp b/tests/unit/security/pib/key-container.t.cpp
new file mode 100644
index 0000000..2afd3e2
--- /dev/null
+++ b/tests/unit/security/pib/key-container.t.cpp
@@ -0,0 +1,174 @@
+/* -*- 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 "security/pib/key-container.hpp"
+#include "security/pib/pib.hpp"
+#include "security/pib/pib-memory.hpp"
+
+#include "boost-test.hpp"
+#include "pib-data-fixture.hpp"
+
+namespace ndn {
+namespace security {
+namespace pib {
+namespace tests {
+
+using namespace ndn::security::tests;
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(Pib)
+BOOST_FIXTURE_TEST_SUITE(TestKeyContainer, PibDataFixture)
+
+using pib::Pib;
+
+BOOST_AUTO_TEST_CASE(Basic)
+{
+ auto pibImpl = make_shared<PibMemory>();
+
+ // start with an empty container
+ KeyContainer container(id1, pibImpl);
+ BOOST_CHECK_EQUAL(container.size(), 0);
+ BOOST_CHECK_EQUAL(container.getLoadedKeys().size(), 0);
+
+ // add the first key
+ Key key11 = container.add(id1Key1.data(), id1Key1.size(), id1Key1Name);
+ BOOST_CHECK_EQUAL(key11.getName(), id1Key1Name);
+ BOOST_CHECK(key11.getPublicKey() == id1Key1);
+ BOOST_CHECK_EQUAL(container.size(), 1);
+ BOOST_CHECK_EQUAL(container.getLoadedKeys().size(), 1);
+ BOOST_CHECK(container.find(id1Key1Name) != container.end());
+
+ // add the same key again
+ Key key12 = container.add(id1Key1.data(), id1Key1.size(), id1Key1Name);
+ BOOST_CHECK_EQUAL(key12.getName(), id1Key1Name);
+ BOOST_CHECK(key12.getPublicKey() == id1Key1);
+ BOOST_CHECK_EQUAL(container.size(), 1);
+ BOOST_CHECK_EQUAL(container.getLoadedKeys().size(), 1);
+ BOOST_CHECK(container.find(id1Key1Name) != container.end());
+
+ // add the second key
+ Key key21 = container.add(id1Key2.data(), id1Key2.size(), id1Key2Name);
+ BOOST_CHECK_EQUAL(key21.getName(), id1Key2Name);
+ BOOST_CHECK(key21.getPublicKey() == id1Key2);
+ BOOST_CHECK_EQUAL(container.size(), 2);
+ BOOST_CHECK_EQUAL(container.getLoadedKeys().size(), 2);
+ BOOST_CHECK(container.find(id1Key1Name) != container.end());
+ BOOST_CHECK(container.find(id1Key2Name) != container.end());
+
+ // get keys
+ BOOST_REQUIRE_NO_THROW(container.get(id1Key1Name));
+ BOOST_REQUIRE_NO_THROW(container.get(id1Key2Name));
+ Name id1Key3Name = v2::constructKeyName(id1, name::Component("non-existing-id"));
+ BOOST_CHECK_THROW(container.get(id1Key3Name), Pib::Error);
+
+ // check key
+ Key key1 = container.get(id1Key1Name);
+ Key key2 = container.get(id1Key2Name);
+ BOOST_CHECK_EQUAL(key1.getName(), id1Key1Name);
+ BOOST_CHECK(key1.getPublicKey() == id1Key1);
+ BOOST_CHECK_EQUAL(key2.getName(), id1Key2Name);
+ BOOST_CHECK(key2.getPublicKey() == id1Key2);
+
+ // create another container from the same PibImpl
+ // cache should be empty
+ KeyContainer container2(id1, pibImpl);
+ BOOST_CHECK_EQUAL(container2.size(), 2);
+ BOOST_CHECK_EQUAL(container2.getLoadedKeys().size(), 0);
+
+ // get key, cache should be filled
+ BOOST_REQUIRE_NO_THROW(container2.get(id1Key1Name));
+ BOOST_CHECK_EQUAL(container2.size(), 2);
+ BOOST_CHECK_EQUAL(container2.getLoadedKeys().size(), 1);
+
+ BOOST_REQUIRE_NO_THROW(container2.get(id1Key2Name));
+ BOOST_CHECK_EQUAL(container2.size(), 2);
+ BOOST_CHECK_EQUAL(container2.getLoadedKeys().size(), 2);
+
+ // remove a key
+ container2.remove(id1Key1Name);
+ BOOST_CHECK_EQUAL(container2.size(), 1);
+ BOOST_CHECK_EQUAL(container2.getLoadedKeys().size(), 1);
+ BOOST_CHECK(container2.find(id1Key1Name) == container2.end());
+ BOOST_CHECK(container2.find(id1Key2Name) != container2.end());
+
+ // remove another key
+ container2.remove(id1Key2Name);
+ BOOST_CHECK_EQUAL(container2.size(), 0);
+ BOOST_CHECK_EQUAL(container2.getLoadedKeys().size(), 0);
+ BOOST_CHECK(container2.find(id1Key2Name) == container2.end());
+}
+
+BOOST_AUTO_TEST_CASE(Errors)
+{
+ auto pibImpl = make_shared<PibMemory>();
+
+ KeyContainer container(id1, pibImpl);
+
+ BOOST_CHECK_THROW(container.add(id2Key1.data(), id2Key1.size(), id2Key1Name), std::invalid_argument);
+ BOOST_CHECK_THROW(container.remove(id2Key1Name), std::invalid_argument);
+ BOOST_CHECK_THROW(container.get(id2Key1Name), std::invalid_argument);
+}
+
+BOOST_AUTO_TEST_CASE(Iterator)
+{
+ auto pibImpl = make_shared<PibMemory>();
+ KeyContainer container(id1, pibImpl);
+
+ container.add(id1Key1.data(), id1Key1.size(), id1Key1Name);
+ container.add(id1Key2.data(), id1Key2.size(), id1Key2Name);
+
+ std::set<Name> keyNames;
+ keyNames.insert(id1Key1Name);
+ keyNames.insert(id1Key2Name);
+
+ KeyContainer::const_iterator it = container.begin();
+ std::set<Name>::const_iterator testIt = keyNames.begin();
+ BOOST_CHECK_EQUAL((*it).getName(), *testIt);
+ it++;
+ testIt++;
+ BOOST_CHECK_EQUAL((*it).getName(), *testIt);
+ ++it;
+ testIt++;
+ BOOST_CHECK(it == container.end());
+
+ size_t count = 0;
+ testIt = keyNames.begin();
+ for (const auto& key : container) {
+ BOOST_CHECK_EQUAL(key.getIdentity(), id1);
+ BOOST_CHECK_EQUAL(key.getName(), *testIt);
+ testIt++;
+ count++;
+ }
+ BOOST_CHECK_EQUAL(count, 2);
+
+ BOOST_CHECK(KeyContainer::const_iterator() == KeyContainer::const_iterator());
+ BOOST_CHECK(KeyContainer::const_iterator() == container.end());
+ BOOST_CHECK(container.end() == KeyContainer::const_iterator());
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestKeyContainer
+BOOST_AUTO_TEST_SUITE_END() // Pib
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace pib
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/pib/key.t.cpp b/tests/unit/security/pib/key.t.cpp
new file mode 100644
index 0000000..3c5d207
--- /dev/null
+++ b/tests/unit/security/pib/key.t.cpp
@@ -0,0 +1,122 @@
+/* -*- 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 "security/pib/key.hpp"
+#include "security/pib/pib.hpp"
+#include "security/pib/pib-memory.hpp"
+#include "security/pib/detail/key-impl.hpp"
+
+#include "boost-test.hpp"
+#include "pib-data-fixture.hpp"
+
+namespace ndn {
+namespace security {
+namespace pib {
+namespace tests {
+
+using namespace ndn::security::tests;
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(Pib)
+BOOST_FIXTURE_TEST_SUITE(TestKey, PibDataFixture)
+
+using pib::Pib;
+
+BOOST_AUTO_TEST_CASE(ValidityChecking)
+{
+ using security::pib::detail::KeyImpl;
+
+ Key key;
+
+ BOOST_CHECK_EQUAL(static_cast<bool>(key), false);
+ BOOST_CHECK_EQUAL(!key, true);
+
+ if (key)
+ BOOST_CHECK(false);
+ else
+ BOOST_CHECK(true);
+
+ auto keyImpl = make_shared<KeyImpl>(id1Key1Name, id1Key1.data(), id1Key1.size(),
+ make_shared<pib::PibMemory>());
+ key = Key(keyImpl);
+
+ BOOST_CHECK_EQUAL(static_cast<bool>(key), true);
+ BOOST_CHECK_EQUAL(!key, false);
+
+ if (key)
+ BOOST_CHECK(true);
+ else
+ BOOST_CHECK(false);
+}
+
+/**
+ * pib::Key is a wrapper of pib::detail::KeyImpl. Since the functionalities of KeyImpl
+ * have already been tested in detail/key-impl.t.cpp, we only test the shared property
+ * of pib::Key in this test case.
+ */
+
+BOOST_AUTO_TEST_CASE(Share)
+{
+ using security::pib::detail::KeyImpl;
+
+ auto keyImpl = make_shared<KeyImpl>(id1Key1Name, id1Key1.data(), id1Key1.size(),
+ make_shared<pib::PibMemory>());
+ Key key1(keyImpl);
+ Key key2(keyImpl);
+ BOOST_CHECK_EQUAL(key1, key2);
+ BOOST_CHECK_NE(key1, Key());
+ BOOST_CHECK_EQUAL(Key(), Key());
+
+ key1.addCertificate(id1Key1Cert1);
+ BOOST_CHECK_NO_THROW(key2.getCertificate(id1Key1Cert1.getName()));
+ key2.removeCertificate(id1Key1Cert1.getName());
+ BOOST_CHECK_THROW(key1.getCertificate(id1Key1Cert1.getName()), Pib::Error);
+
+ key1.setDefaultCertificate(id1Key1Cert1);
+ BOOST_CHECK_NO_THROW(key2.getDefaultCertificate());
+}
+
+BOOST_AUTO_TEST_CASE(Helpers)
+{
+ BOOST_CHECK_EQUAL(v2::constructKeyName("/hello", name::Component("world")), "/hello/KEY/world");
+
+ BOOST_CHECK_EQUAL(v2::isValidKeyName("/hello"), false);
+ BOOST_CHECK_EQUAL(v2::isValidKeyName("/hello/KEY"), false);
+ BOOST_CHECK_EQUAL(v2::isValidKeyName("/hello/KEY/world"), true);
+
+ BOOST_CHECK_EQUAL(v2::isValidKeyName("/KEY/hello"), true);
+ BOOST_CHECK_EQUAL(v2::isValidKeyName("/hello/world/KEY/!"), true);
+
+ BOOST_CHECK_EQUAL(v2::extractIdentityFromKeyName("/KEY/hello"), "/");
+ BOOST_CHECK_EQUAL(v2::extractIdentityFromKeyName("/hello/KEY/world"), "/hello");
+ BOOST_CHECK_EQUAL(v2::extractIdentityFromKeyName("/hello/world/KEY/!"), "/hello/world");
+
+ BOOST_CHECK_THROW(v2::extractIdentityFromKeyName("/hello"), std::invalid_argument);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestKey
+BOOST_AUTO_TEST_SUITE_END() // Pib
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace pib
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/pib/pib-data-fixture.cpp b/tests/unit/security/pib/pib-data-fixture.cpp
new file mode 100644
index 0000000..91e5b34
--- /dev/null
+++ b/tests/unit/security/pib/pib-data-fixture.cpp
@@ -0,0 +1,429 @@
+/* -*- 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 "pib-data-fixture.hpp"
+#include "../../identity-management-time-fixture.hpp"
+
+// #include "security/pib/pib-memory.hpp"
+// #include "security/tpm/tpm.hpp"
+// #include "security/tpm/back-end-mem.hpp"
+// #include <fstream>
+
+namespace ndn {
+namespace security {
+namespace tests {
+
+// class TestCertDataGenerator
+// {
+// public:
+// TestCertDataGenerator()
+// : tpm("test", "test", make_unique<tpm::BackEndMem>())
+// {
+// }
+
+// void
+// printTestDataForId(const std::string& prefix, const Name& id)
+// {
+// for (int keyId : {1, 2}) {
+// Name keyName = tpm.createKey(id, EcKeyParams(name::Component::fromNumber(keyId)));
+
+// for (int certVersion : {1, 2}) {
+// Name certName = keyName;
+// certName
+// .append("issuer")
+// .appendVersion(certVersion);
+// v2::Certificate cert;
+// cert.setName(certName);
+// cert.setFreshnessPeriod(1_h);
+// cert.setContent(tpm.getPublicKey(keyName));
+
+// // @TODO sign using the new KeyChain
+// SignatureInfo info;
+// info.setSignatureType(tlv::SignatureSha256WithEcdsa);
+// info.setKeyLocator(KeyLocator(keyName));
+// info.setValidityPeriod(ValidityPeriod(time::fromIsoString("20170102T000000"),
+// time::fromIsoString("20180102T000000")));
+// cert.setSignature(Signature(info, Block()));
+
+// EncodingBuffer buf;
+// cert.wireEncode(buf, true);
+
+// cert.setSignatureValue(Block(tlv::SignatureValue,
+// tpm.sign(buf.buf(), buf.size(), keyName, DigestAlgorithm::SHA256)));
+
+// printBytes(prefix + "_KEY" + to_string(keyId) + "_CERT" + to_string(certVersion),
+// cert.wireEncode());
+// }
+// }
+// }
+
+// static void
+// printBytes(const std::string& name, const Block& block)
+// {
+// printBytes(name, block.wire(), block.size());
+// }
+
+// static void
+// printBytes(const std::string& name, const Buffer& buffer)
+// {
+// printBytes(name, buffer.buf(), buffer.size());
+// }
+
+// static void
+// printBytes(const std::string& name, const uint8_t* buf, size_t size)
+// {
+// std::cout << "\nconst uint8_t " << name << "[] = {\n"
+// << " ";
+
+// std::string hex = toHex(buf, size);
+
+// for (size_t i = 0; i < hex.size(); i++) {
+// if (i > 0 && i % 40 == 0)
+// std::cout << "\n ";
+
+// std::cout << "0x" << hex[i];
+// std::cout << hex[++i];
+
+// if ((i + 1) != hex.size())
+// std::cout << ", ";
+// }
+// std::cout << "\n"
+// << "};" << std::endl;
+// }
+
+// public:
+// pib::PibMemory pib;
+// Tpm tpm;
+// };
+
+// // The test data can be generated using this test case
+// BOOST_FIXTURE_TEST_CASE(GenerateTestCertData, TestCertDataGenerator)
+// {
+// printTestDataForId("ID1", Name("/pib/interface/id/1"));
+// printTestDataForId("ID2", Name("/pib/interface/id/2"));
+// }
+
+const uint8_t ID1_KEY1_CERT1[] = {
+ 0x06, 0xFD, 0x02, 0x25, 0x07, 0x2B, 0x08, 0x03, 0x70, 0x69, 0x62, 0x08, 0x09, 0x69, 0x6E, 0x74, 0x65, 0x72, 0x66, 0x61,
+ 0x63, 0x65, 0x08, 0x02, 0x69, 0x64, 0x08, 0x01, 0x31, 0x08, 0x03, 0x4B, 0x45, 0x59, 0x08, 0x01, 0x01, 0x08, 0x06, 0x69,
+ 0x73, 0x73, 0x75, 0x65, 0x72, 0x08, 0x02, 0xFD, 0x01, 0x14, 0x09, 0x18, 0x01, 0x02, 0x19, 0x04, 0x00, 0x36, 0xEE, 0x80,
+ 0x15, 0xFD, 0x01, 0x4F, 0x30, 0x82, 0x01, 0x4B, 0x30, 0x82, 0x01, 0x03, 0x06, 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02,
+ 0x01, 0x30, 0x81, 0xF7, 0x02, 0x01, 0x01, 0x30, 0x2C, 0x06, 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x01, 0x01, 0x02, 0x21,
+ 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x30, 0x5B, 0x04, 0x20, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x04, 0x20, 0x5A, 0xC6, 0x35, 0xD8, 0xAA, 0x3A, 0x93, 0xE7, 0xB3,
+ 0xEB, 0xBD, 0x55, 0x76, 0x98, 0x86, 0xBC, 0x65, 0x1D, 0x06, 0xB0, 0xCC, 0x53, 0xB0, 0xF6, 0x3B, 0xCE, 0x3C, 0x3E, 0x27,
+ 0xD2, 0x60, 0x4B, 0x03, 0x15, 0x00, 0xC4, 0x9D, 0x36, 0x08, 0x86, 0xE7, 0x04, 0x93, 0x6A, 0x66, 0x78, 0xE1, 0x13, 0x9D,
+ 0x26, 0xB7, 0x81, 0x9F, 0x7E, 0x90, 0x04, 0x41, 0x04, 0x6B, 0x17, 0xD1, 0xF2, 0xE1, 0x2C, 0x42, 0x47, 0xF8, 0xBC, 0xE6,
+ 0xE5, 0x63, 0xA4, 0x40, 0xF2, 0x77, 0x03, 0x7D, 0x81, 0x2D, 0xEB, 0x33, 0xA0, 0xF4, 0xA1, 0x39, 0x45, 0xD8, 0x98, 0xC2,
+ 0x96, 0x4F, 0xE3, 0x42, 0xE2, 0xFE, 0x1A, 0x7F, 0x9B, 0x8E, 0xE7, 0xEB, 0x4A, 0x7C, 0x0F, 0x9E, 0x16, 0x2B, 0xCE, 0x33,
+ 0x57, 0x6B, 0x31, 0x5E, 0xCE, 0xCB, 0xB6, 0x40, 0x68, 0x37, 0xBF, 0x51, 0xF5, 0x02, 0x21, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xBC, 0xE6, 0xFA, 0xAD, 0xA7, 0x17, 0x9E, 0x84,
+ 0xF3, 0xB9, 0xCA, 0xC2, 0xFC, 0x63, 0x25, 0x51, 0x02, 0x01, 0x01, 0x03, 0x42, 0x00, 0x04, 0xCB, 0x46, 0xF7, 0x16, 0x2E,
+ 0x83, 0x3D, 0x5E, 0x4A, 0x80, 0x6A, 0x78, 0xB7, 0xA8, 0x7A, 0x15, 0x95, 0x2D, 0x23, 0xA8, 0x41, 0xF7, 0x62, 0xE4, 0x0E,
+ 0x66, 0x36, 0xB3, 0xF3, 0x14, 0xD6, 0xB3, 0xAB, 0x19, 0x26, 0x9D, 0x5A, 0x8A, 0x51, 0xD4, 0x4E, 0xBA, 0xBE, 0x13, 0x96,
+ 0xCA, 0x38, 0x52, 0x16, 0xE4, 0x3D, 0xB0, 0x88, 0xBA, 0xBB, 0x7B, 0x97, 0x00, 0xA5, 0x95, 0x97, 0x4E, 0xE8, 0xF6, 0x16,
+ 0x50, 0x1B, 0x01, 0x03, 0x1C, 0x21, 0x07, 0x1F, 0x08, 0x03, 0x70, 0x69, 0x62, 0x08, 0x09, 0x69, 0x6E, 0x74, 0x65, 0x72,
+ 0x66, 0x61, 0x63, 0x65, 0x08, 0x02, 0x69, 0x64, 0x08, 0x01, 0x31, 0x08, 0x03, 0x4B, 0x45, 0x59, 0x08, 0x01, 0x01, 0xFD,
+ 0x00, 0xFD, 0x26, 0xFD, 0x00, 0xFE, 0x0F, 0x32, 0x30, 0x31, 0x37, 0x30, 0x31, 0x30, 0x32, 0x54, 0x30, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0xFD, 0x00, 0xFF, 0x0F, 0x32, 0x30, 0x31, 0x38, 0x30, 0x31, 0x30, 0x32, 0x54, 0x30, 0x30, 0x30, 0x30, 0x30,
+ 0x30, 0x17, 0x46, 0x30, 0x44, 0x02, 0x20, 0x53, 0xC8, 0xAD, 0x88, 0xBA, 0x52, 0x29, 0x68, 0xFF, 0x74, 0xA8, 0x39, 0x7F,
+ 0x2C, 0xE2, 0x8E, 0x04, 0xC1, 0x78, 0x36, 0x46, 0x89, 0x38, 0x58, 0x45, 0x22, 0x44, 0xA3, 0xC8, 0xC1, 0xFF, 0x72, 0x02,
+ 0x20, 0x23, 0x9D, 0xE4, 0x92, 0x00, 0xF1, 0x43, 0x69, 0xF7, 0x32, 0xF6, 0xAA, 0x8C, 0xFD, 0x7F, 0x2B, 0xFB, 0xD2, 0x40,
+ 0x6A, 0x1E, 0xA3, 0xE5, 0xF0, 0xF8, 0x2B, 0x92, 0x99, 0x6B, 0xDB, 0xE2, 0x6D
+};
+
+const uint8_t ID1_KEY1_CERT2[] = {
+ 0x06, 0xFD, 0x02, 0x26, 0x07, 0x2B, 0x08, 0x03, 0x70, 0x69, 0x62, 0x08, 0x09, 0x69, 0x6E, 0x74, 0x65, 0x72, 0x66, 0x61,
+ 0x63, 0x65, 0x08, 0x02, 0x69, 0x64, 0x08, 0x01, 0x31, 0x08, 0x03, 0x4B, 0x45, 0x59, 0x08, 0x01, 0x01, 0x08, 0x06, 0x69,
+ 0x73, 0x73, 0x75, 0x65, 0x72, 0x08, 0x02, 0xFD, 0x02, 0x14, 0x09, 0x18, 0x01, 0x02, 0x19, 0x04, 0x00, 0x36, 0xEE, 0x80,
+ 0x15, 0xFD, 0x01, 0x4F, 0x30, 0x82, 0x01, 0x4B, 0x30, 0x82, 0x01, 0x03, 0x06, 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02,
+ 0x01, 0x30, 0x81, 0xF7, 0x02, 0x01, 0x01, 0x30, 0x2C, 0x06, 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x01, 0x01, 0x02, 0x21,
+ 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x30, 0x5B, 0x04, 0x20, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x04, 0x20, 0x5A, 0xC6, 0x35, 0xD8, 0xAA, 0x3A, 0x93, 0xE7, 0xB3,
+ 0xEB, 0xBD, 0x55, 0x76, 0x98, 0x86, 0xBC, 0x65, 0x1D, 0x06, 0xB0, 0xCC, 0x53, 0xB0, 0xF6, 0x3B, 0xCE, 0x3C, 0x3E, 0x27,
+ 0xD2, 0x60, 0x4B, 0x03, 0x15, 0x00, 0xC4, 0x9D, 0x36, 0x08, 0x86, 0xE7, 0x04, 0x93, 0x6A, 0x66, 0x78, 0xE1, 0x13, 0x9D,
+ 0x26, 0xB7, 0x81, 0x9F, 0x7E, 0x90, 0x04, 0x41, 0x04, 0x6B, 0x17, 0xD1, 0xF2, 0xE1, 0x2C, 0x42, 0x47, 0xF8, 0xBC, 0xE6,
+ 0xE5, 0x63, 0xA4, 0x40, 0xF2, 0x77, 0x03, 0x7D, 0x81, 0x2D, 0xEB, 0x33, 0xA0, 0xF4, 0xA1, 0x39, 0x45, 0xD8, 0x98, 0xC2,
+ 0x96, 0x4F, 0xE3, 0x42, 0xE2, 0xFE, 0x1A, 0x7F, 0x9B, 0x8E, 0xE7, 0xEB, 0x4A, 0x7C, 0x0F, 0x9E, 0x16, 0x2B, 0xCE, 0x33,
+ 0x57, 0x6B, 0x31, 0x5E, 0xCE, 0xCB, 0xB6, 0x40, 0x68, 0x37, 0xBF, 0x51, 0xF5, 0x02, 0x21, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xBC, 0xE6, 0xFA, 0xAD, 0xA7, 0x17, 0x9E, 0x84,
+ 0xF3, 0xB9, 0xCA, 0xC2, 0xFC, 0x63, 0x25, 0x51, 0x02, 0x01, 0x01, 0x03, 0x42, 0x00, 0x04, 0xCB, 0x46, 0xF7, 0x16, 0x2E,
+ 0x83, 0x3D, 0x5E, 0x4A, 0x80, 0x6A, 0x78, 0xB7, 0xA8, 0x7A, 0x15, 0x95, 0x2D, 0x23, 0xA8, 0x41, 0xF7, 0x62, 0xE4, 0x0E,
+ 0x66, 0x36, 0xB3, 0xF3, 0x14, 0xD6, 0xB3, 0xAB, 0x19, 0x26, 0x9D, 0x5A, 0x8A, 0x51, 0xD4, 0x4E, 0xBA, 0xBE, 0x13, 0x96,
+ 0xCA, 0x38, 0x52, 0x16, 0xE4, 0x3D, 0xB0, 0x88, 0xBA, 0xBB, 0x7B, 0x97, 0x00, 0xA5, 0x95, 0x97, 0x4E, 0xE8, 0xF6, 0x16,
+ 0x50, 0x1B, 0x01, 0x03, 0x1C, 0x21, 0x07, 0x1F, 0x08, 0x03, 0x70, 0x69, 0x62, 0x08, 0x09, 0x69, 0x6E, 0x74, 0x65, 0x72,
+ 0x66, 0x61, 0x63, 0x65, 0x08, 0x02, 0x69, 0x64, 0x08, 0x01, 0x31, 0x08, 0x03, 0x4B, 0x45, 0x59, 0x08, 0x01, 0x01, 0xFD,
+ 0x00, 0xFD, 0x26, 0xFD, 0x00, 0xFE, 0x0F, 0x32, 0x30, 0x31, 0x37, 0x30, 0x31, 0x30, 0x32, 0x54, 0x30, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0xFD, 0x00, 0xFF, 0x0F, 0x32, 0x30, 0x31, 0x38, 0x30, 0x31, 0x30, 0x32, 0x54, 0x30, 0x30, 0x30, 0x30, 0x30,
+ 0x30, 0x17, 0x47, 0x30, 0x45, 0x02, 0x21, 0x00, 0xB2, 0x93, 0xCD, 0x3D, 0x01, 0x00, 0xB5, 0xF1, 0x75, 0x22, 0x68, 0x9F,
+ 0xE4, 0x5E, 0x0A, 0x76, 0x34, 0xBC, 0x9D, 0xCF, 0x9A, 0x4C, 0x21, 0x3F, 0xA5, 0x12, 0x51, 0xF7, 0x3A, 0x5E, 0x37, 0x7D,
+ 0x02, 0x20, 0x33, 0xA9, 0xA9, 0x8F, 0xD8, 0x2E, 0xED, 0x3C, 0xE5, 0x18, 0x94, 0x59, 0x28, 0xEA, 0x82, 0x38, 0x5B, 0x20,
+ 0xE4, 0xBF, 0x15, 0xF4, 0x0D, 0x45, 0xAE, 0x8B, 0x63, 0x19, 0x79, 0x78, 0x50, 0x3A
+};
+
+const uint8_t ID1_KEY2_CERT1[] = {
+ 0x06, 0xFD, 0x02, 0x25, 0x07, 0x2B, 0x08, 0x03, 0x70, 0x69, 0x62, 0x08, 0x09, 0x69, 0x6E, 0x74, 0x65, 0x72, 0x66, 0x61,
+ 0x63, 0x65, 0x08, 0x02, 0x69, 0x64, 0x08, 0x01, 0x31, 0x08, 0x03, 0x4B, 0x45, 0x59, 0x08, 0x01, 0x02, 0x08, 0x06, 0x69,
+ 0x73, 0x73, 0x75, 0x65, 0x72, 0x08, 0x02, 0xFD, 0x01, 0x14, 0x09, 0x18, 0x01, 0x02, 0x19, 0x04, 0x00, 0x36, 0xEE, 0x80,
+ 0x15, 0xFD, 0x01, 0x4F, 0x30, 0x82, 0x01, 0x4B, 0x30, 0x82, 0x01, 0x03, 0x06, 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02,
+ 0x01, 0x30, 0x81, 0xF7, 0x02, 0x01, 0x01, 0x30, 0x2C, 0x06, 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x01, 0x01, 0x02, 0x21,
+ 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x30, 0x5B, 0x04, 0x20, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x04, 0x20, 0x5A, 0xC6, 0x35, 0xD8, 0xAA, 0x3A, 0x93, 0xE7, 0xB3,
+ 0xEB, 0xBD, 0x55, 0x76, 0x98, 0x86, 0xBC, 0x65, 0x1D, 0x06, 0xB0, 0xCC, 0x53, 0xB0, 0xF6, 0x3B, 0xCE, 0x3C, 0x3E, 0x27,
+ 0xD2, 0x60, 0x4B, 0x03, 0x15, 0x00, 0xC4, 0x9D, 0x36, 0x08, 0x86, 0xE7, 0x04, 0x93, 0x6A, 0x66, 0x78, 0xE1, 0x13, 0x9D,
+ 0x26, 0xB7, 0x81, 0x9F, 0x7E, 0x90, 0x04, 0x41, 0x04, 0x6B, 0x17, 0xD1, 0xF2, 0xE1, 0x2C, 0x42, 0x47, 0xF8, 0xBC, 0xE6,
+ 0xE5, 0x63, 0xA4, 0x40, 0xF2, 0x77, 0x03, 0x7D, 0x81, 0x2D, 0xEB, 0x33, 0xA0, 0xF4, 0xA1, 0x39, 0x45, 0xD8, 0x98, 0xC2,
+ 0x96, 0x4F, 0xE3, 0x42, 0xE2, 0xFE, 0x1A, 0x7F, 0x9B, 0x8E, 0xE7, 0xEB, 0x4A, 0x7C, 0x0F, 0x9E, 0x16, 0x2B, 0xCE, 0x33,
+ 0x57, 0x6B, 0x31, 0x5E, 0xCE, 0xCB, 0xB6, 0x40, 0x68, 0x37, 0xBF, 0x51, 0xF5, 0x02, 0x21, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xBC, 0xE6, 0xFA, 0xAD, 0xA7, 0x17, 0x9E, 0x84,
+ 0xF3, 0xB9, 0xCA, 0xC2, 0xFC, 0x63, 0x25, 0x51, 0x02, 0x01, 0x01, 0x03, 0x42, 0x00, 0x04, 0x34, 0xAA, 0x4B, 0x1A, 0x97,
+ 0x4A, 0x6B, 0x6F, 0x3F, 0xB3, 0xC9, 0xD1, 0x39, 0x9F, 0x1E, 0x49, 0xB6, 0x6E, 0x19, 0x97, 0x13, 0x5E, 0xFA, 0xE6, 0xD3,
+ 0xFE, 0xF3, 0xB0, 0xCA, 0x80, 0x09, 0x31, 0xCA, 0x50, 0x5C, 0xE6, 0x57, 0xBF, 0x13, 0x16, 0xCE, 0x3E, 0xF1, 0xD4, 0x23,
+ 0xF8, 0x7F, 0x31, 0xFA, 0x13, 0x39, 0x09, 0xED, 0xC6, 0x74, 0x3D, 0xFD, 0x1A, 0x0B, 0xC7, 0xC1, 0x01, 0x15, 0x7F, 0x16,
+ 0x50, 0x1B, 0x01, 0x03, 0x1C, 0x21, 0x07, 0x1F, 0x08, 0x03, 0x70, 0x69, 0x62, 0x08, 0x09, 0x69, 0x6E, 0x74, 0x65, 0x72,
+ 0x66, 0x61, 0x63, 0x65, 0x08, 0x02, 0x69, 0x64, 0x08, 0x01, 0x31, 0x08, 0x03, 0x4B, 0x45, 0x59, 0x08, 0x01, 0x02, 0xFD,
+ 0x00, 0xFD, 0x26, 0xFD, 0x00, 0xFE, 0x0F, 0x32, 0x30, 0x31, 0x37, 0x30, 0x31, 0x30, 0x32, 0x54, 0x30, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0xFD, 0x00, 0xFF, 0x0F, 0x32, 0x30, 0x31, 0x38, 0x30, 0x31, 0x30, 0x32, 0x54, 0x30, 0x30, 0x30, 0x30, 0x30,
+ 0x30, 0x17, 0x46, 0x30, 0x44, 0x02, 0x20, 0x71, 0x3A, 0xB4, 0x19, 0x4B, 0xB3, 0x25, 0xA5, 0x03, 0x23, 0x8C, 0xC1, 0xB9,
+ 0x68, 0xC1, 0x41, 0x4B, 0xED, 0x13, 0xCC, 0x87, 0x16, 0xB5, 0x13, 0x87, 0xA0, 0x54, 0xA2, 0x9F, 0xF0, 0xD7, 0x72, 0x02,
+ 0x20, 0x4B, 0xEF, 0xB5, 0x6A, 0x8C, 0x40, 0x71, 0x17, 0xD2, 0x4F, 0xB6, 0x0F, 0xBE, 0x60, 0x1A, 0x46, 0x9B, 0x78, 0x15,
+ 0x46, 0x09, 0xC2, 0x7A, 0x80, 0xD4, 0xE6, 0x71, 0x52, 0xD6, 0x83, 0x4B, 0x04
+};
+
+const uint8_t ID1_KEY2_CERT2[] = {
+ 0x06, 0xFD, 0x02, 0x26, 0x07, 0x2B, 0x08, 0x03, 0x70, 0x69, 0x62, 0x08, 0x09, 0x69, 0x6E, 0x74, 0x65, 0x72, 0x66, 0x61,
+ 0x63, 0x65, 0x08, 0x02, 0x69, 0x64, 0x08, 0x01, 0x31, 0x08, 0x03, 0x4B, 0x45, 0x59, 0x08, 0x01, 0x02, 0x08, 0x06, 0x69,
+ 0x73, 0x73, 0x75, 0x65, 0x72, 0x08, 0x02, 0xFD, 0x02, 0x14, 0x09, 0x18, 0x01, 0x02, 0x19, 0x04, 0x00, 0x36, 0xEE, 0x80,
+ 0x15, 0xFD, 0x01, 0x4F, 0x30, 0x82, 0x01, 0x4B, 0x30, 0x82, 0x01, 0x03, 0x06, 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02,
+ 0x01, 0x30, 0x81, 0xF7, 0x02, 0x01, 0x01, 0x30, 0x2C, 0x06, 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x01, 0x01, 0x02, 0x21,
+ 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x30, 0x5B, 0x04, 0x20, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x04, 0x20, 0x5A, 0xC6, 0x35, 0xD8, 0xAA, 0x3A, 0x93, 0xE7, 0xB3,
+ 0xEB, 0xBD, 0x55, 0x76, 0x98, 0x86, 0xBC, 0x65, 0x1D, 0x06, 0xB0, 0xCC, 0x53, 0xB0, 0xF6, 0x3B, 0xCE, 0x3C, 0x3E, 0x27,
+ 0xD2, 0x60, 0x4B, 0x03, 0x15, 0x00, 0xC4, 0x9D, 0x36, 0x08, 0x86, 0xE7, 0x04, 0x93, 0x6A, 0x66, 0x78, 0xE1, 0x13, 0x9D,
+ 0x26, 0xB7, 0x81, 0x9F, 0x7E, 0x90, 0x04, 0x41, 0x04, 0x6B, 0x17, 0xD1, 0xF2, 0xE1, 0x2C, 0x42, 0x47, 0xF8, 0xBC, 0xE6,
+ 0xE5, 0x63, 0xA4, 0x40, 0xF2, 0x77, 0x03, 0x7D, 0x81, 0x2D, 0xEB, 0x33, 0xA0, 0xF4, 0xA1, 0x39, 0x45, 0xD8, 0x98, 0xC2,
+ 0x96, 0x4F, 0xE3, 0x42, 0xE2, 0xFE, 0x1A, 0x7F, 0x9B, 0x8E, 0xE7, 0xEB, 0x4A, 0x7C, 0x0F, 0x9E, 0x16, 0x2B, 0xCE, 0x33,
+ 0x57, 0x6B, 0x31, 0x5E, 0xCE, 0xCB, 0xB6, 0x40, 0x68, 0x37, 0xBF, 0x51, 0xF5, 0x02, 0x21, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xBC, 0xE6, 0xFA, 0xAD, 0xA7, 0x17, 0x9E, 0x84,
+ 0xF3, 0xB9, 0xCA, 0xC2, 0xFC, 0x63, 0x25, 0x51, 0x02, 0x01, 0x01, 0x03, 0x42, 0x00, 0x04, 0x34, 0xAA, 0x4B, 0x1A, 0x97,
+ 0x4A, 0x6B, 0x6F, 0x3F, 0xB3, 0xC9, 0xD1, 0x39, 0x9F, 0x1E, 0x49, 0xB6, 0x6E, 0x19, 0x97, 0x13, 0x5E, 0xFA, 0xE6, 0xD3,
+ 0xFE, 0xF3, 0xB0, 0xCA, 0x80, 0x09, 0x31, 0xCA, 0x50, 0x5C, 0xE6, 0x57, 0xBF, 0x13, 0x16, 0xCE, 0x3E, 0xF1, 0xD4, 0x23,
+ 0xF8, 0x7F, 0x31, 0xFA, 0x13, 0x39, 0x09, 0xED, 0xC6, 0x74, 0x3D, 0xFD, 0x1A, 0x0B, 0xC7, 0xC1, 0x01, 0x15, 0x7F, 0x16,
+ 0x50, 0x1B, 0x01, 0x03, 0x1C, 0x21, 0x07, 0x1F, 0x08, 0x03, 0x70, 0x69, 0x62, 0x08, 0x09, 0x69, 0x6E, 0x74, 0x65, 0x72,
+ 0x66, 0x61, 0x63, 0x65, 0x08, 0x02, 0x69, 0x64, 0x08, 0x01, 0x31, 0x08, 0x03, 0x4B, 0x45, 0x59, 0x08, 0x01, 0x02, 0xFD,
+ 0x00, 0xFD, 0x26, 0xFD, 0x00, 0xFE, 0x0F, 0x32, 0x30, 0x31, 0x37, 0x30, 0x31, 0x30, 0x32, 0x54, 0x30, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0xFD, 0x00, 0xFF, 0x0F, 0x32, 0x30, 0x31, 0x38, 0x30, 0x31, 0x30, 0x32, 0x54, 0x30, 0x30, 0x30, 0x30, 0x30,
+ 0x30, 0x17, 0x47, 0x30, 0x45, 0x02, 0x21, 0x00, 0xD2, 0x90, 0x8C, 0xA3, 0x52, 0x2F, 0x79, 0xB3, 0xD7, 0x39, 0xE1, 0x6C,
+ 0x7F, 0xA0, 0xDF, 0xD1, 0x3E, 0x0F, 0x70, 0xBE, 0xF5, 0xDB, 0x08, 0xDF, 0xE1, 0x0B, 0xDF, 0x79, 0x99, 0xFE, 0x5C, 0xDC,
+ 0x02, 0x20, 0x3D, 0xD4, 0x7C, 0xD1, 0x83, 0xBE, 0x29, 0xBB, 0x73, 0xA3, 0x82, 0xE5, 0xE6, 0x83, 0xA1, 0xC1, 0xBC, 0xF6,
+ 0x84, 0x42, 0x85, 0x00, 0x92, 0x4B, 0xA2, 0xA8, 0xCA, 0x10, 0x7B, 0x92, 0x89, 0xB0
+};
+
+const uint8_t ID2_KEY1_CERT1[] = {
+ 0x06, 0xFD, 0x02, 0x25, 0x07, 0x2B, 0x08, 0x03, 0x70, 0x69, 0x62, 0x08, 0x09, 0x69, 0x6E, 0x74, 0x65, 0x72, 0x66, 0x61,
+ 0x63, 0x65, 0x08, 0x02, 0x69, 0x64, 0x08, 0x01, 0x32, 0x08, 0x03, 0x4B, 0x45, 0x59, 0x08, 0x01, 0x01, 0x08, 0x06, 0x69,
+ 0x73, 0x73, 0x75, 0x65, 0x72, 0x08, 0x02, 0xFD, 0x01, 0x14, 0x09, 0x18, 0x01, 0x02, 0x19, 0x04, 0x00, 0x36, 0xEE, 0x80,
+ 0x15, 0xFD, 0x01, 0x4F, 0x30, 0x82, 0x01, 0x4B, 0x30, 0x82, 0x01, 0x03, 0x06, 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02,
+ 0x01, 0x30, 0x81, 0xF7, 0x02, 0x01, 0x01, 0x30, 0x2C, 0x06, 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x01, 0x01, 0x02, 0x21,
+ 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x30, 0x5B, 0x04, 0x20, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x04, 0x20, 0x5A, 0xC6, 0x35, 0xD8, 0xAA, 0x3A, 0x93, 0xE7, 0xB3,
+ 0xEB, 0xBD, 0x55, 0x76, 0x98, 0x86, 0xBC, 0x65, 0x1D, 0x06, 0xB0, 0xCC, 0x53, 0xB0, 0xF6, 0x3B, 0xCE, 0x3C, 0x3E, 0x27,
+ 0xD2, 0x60, 0x4B, 0x03, 0x15, 0x00, 0xC4, 0x9D, 0x36, 0x08, 0x86, 0xE7, 0x04, 0x93, 0x6A, 0x66, 0x78, 0xE1, 0x13, 0x9D,
+ 0x26, 0xB7, 0x81, 0x9F, 0x7E, 0x90, 0x04, 0x41, 0x04, 0x6B, 0x17, 0xD1, 0xF2, 0xE1, 0x2C, 0x42, 0x47, 0xF8, 0xBC, 0xE6,
+ 0xE5, 0x63, 0xA4, 0x40, 0xF2, 0x77, 0x03, 0x7D, 0x81, 0x2D, 0xEB, 0x33, 0xA0, 0xF4, 0xA1, 0x39, 0x45, 0xD8, 0x98, 0xC2,
+ 0x96, 0x4F, 0xE3, 0x42, 0xE2, 0xFE, 0x1A, 0x7F, 0x9B, 0x8E, 0xE7, 0xEB, 0x4A, 0x7C, 0x0F, 0x9E, 0x16, 0x2B, 0xCE, 0x33,
+ 0x57, 0x6B, 0x31, 0x5E, 0xCE, 0xCB, 0xB6, 0x40, 0x68, 0x37, 0xBF, 0x51, 0xF5, 0x02, 0x21, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xBC, 0xE6, 0xFA, 0xAD, 0xA7, 0x17, 0x9E, 0x84,
+ 0xF3, 0xB9, 0xCA, 0xC2, 0xFC, 0x63, 0x25, 0x51, 0x02, 0x01, 0x01, 0x03, 0x42, 0x00, 0x04, 0xAC, 0x62, 0xD7, 0x31, 0x3B,
+ 0x19, 0x1F, 0x44, 0x76, 0x6E, 0x79, 0x03, 0xB9, 0xC8, 0x26, 0xC4, 0x1E, 0x38, 0x3A, 0x41, 0xFE, 0xB4, 0x72, 0xA2, 0x36,
+ 0xBB, 0x82, 0x9C, 0xB9, 0x07, 0x62, 0x6F, 0x1C, 0x79, 0x12, 0xCA, 0x9C, 0x3D, 0xAA, 0x7A, 0x96, 0xFF, 0xAF, 0x5B, 0x6F,
+ 0xE1, 0x72, 0x60, 0xB0, 0x7F, 0x44, 0x38, 0x05, 0x21, 0xCC, 0x49, 0x78, 0x89, 0xC1, 0xEF, 0xEE, 0x81, 0x8E, 0xF5, 0x16,
+ 0x50, 0x1B, 0x01, 0x03, 0x1C, 0x21, 0x07, 0x1F, 0x08, 0x03, 0x70, 0x69, 0x62, 0x08, 0x09, 0x69, 0x6E, 0x74, 0x65, 0x72,
+ 0x66, 0x61, 0x63, 0x65, 0x08, 0x02, 0x69, 0x64, 0x08, 0x01, 0x32, 0x08, 0x03, 0x4B, 0x45, 0x59, 0x08, 0x01, 0x01, 0xFD,
+ 0x00, 0xFD, 0x26, 0xFD, 0x00, 0xFE, 0x0F, 0x32, 0x30, 0x31, 0x37, 0x30, 0x31, 0x30, 0x32, 0x54, 0x30, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0xFD, 0x00, 0xFF, 0x0F, 0x32, 0x30, 0x31, 0x38, 0x30, 0x31, 0x30, 0x32, 0x54, 0x30, 0x30, 0x30, 0x30, 0x30,
+ 0x30, 0x17, 0x46, 0x30, 0x44, 0x02, 0x20, 0x16, 0xC0, 0xF4, 0xE3, 0x15, 0x43, 0x6E, 0x27, 0x33, 0x7C, 0x46, 0x4D, 0x35,
+ 0xA7, 0x8B, 0x0C, 0xE3, 0x27, 0x63, 0x4B, 0xB2, 0xB6, 0x4F, 0x06, 0x90, 0x2A, 0xD8, 0x54, 0x92, 0xE8, 0xBA, 0xBE, 0x02,
+ 0x20, 0x67, 0xA6, 0x55, 0x8D, 0x16, 0x0E, 0x1E, 0x9E, 0x10, 0x0E, 0xB9, 0x3C, 0xEF, 0xEE, 0xB5, 0xF7, 0x9C, 0xB3, 0x1D,
+ 0x04, 0xF1, 0xD4, 0xB5, 0x9F, 0xD4, 0x13, 0xBB, 0xFF, 0xA7, 0x58, 0xAE, 0xCB
+};
+
+const uint8_t ID2_KEY1_CERT2[] = {
+ 0x06, 0xFD, 0x02, 0x26, 0x07, 0x2B, 0x08, 0x03, 0x70, 0x69, 0x62, 0x08, 0x09, 0x69, 0x6E, 0x74, 0x65, 0x72, 0x66, 0x61,
+ 0x63, 0x65, 0x08, 0x02, 0x69, 0x64, 0x08, 0x01, 0x32, 0x08, 0x03, 0x4B, 0x45, 0x59, 0x08, 0x01, 0x01, 0x08, 0x06, 0x69,
+ 0x73, 0x73, 0x75, 0x65, 0x72, 0x08, 0x02, 0xFD, 0x02, 0x14, 0x09, 0x18, 0x01, 0x02, 0x19, 0x04, 0x00, 0x36, 0xEE, 0x80,
+ 0x15, 0xFD, 0x01, 0x4F, 0x30, 0x82, 0x01, 0x4B, 0x30, 0x82, 0x01, 0x03, 0x06, 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02,
+ 0x01, 0x30, 0x81, 0xF7, 0x02, 0x01, 0x01, 0x30, 0x2C, 0x06, 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x01, 0x01, 0x02, 0x21,
+ 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x30, 0x5B, 0x04, 0x20, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x04, 0x20, 0x5A, 0xC6, 0x35, 0xD8, 0xAA, 0x3A, 0x93, 0xE7, 0xB3,
+ 0xEB, 0xBD, 0x55, 0x76, 0x98, 0x86, 0xBC, 0x65, 0x1D, 0x06, 0xB0, 0xCC, 0x53, 0xB0, 0xF6, 0x3B, 0xCE, 0x3C, 0x3E, 0x27,
+ 0xD2, 0x60, 0x4B, 0x03, 0x15, 0x00, 0xC4, 0x9D, 0x36, 0x08, 0x86, 0xE7, 0x04, 0x93, 0x6A, 0x66, 0x78, 0xE1, 0x13, 0x9D,
+ 0x26, 0xB7, 0x81, 0x9F, 0x7E, 0x90, 0x04, 0x41, 0x04, 0x6B, 0x17, 0xD1, 0xF2, 0xE1, 0x2C, 0x42, 0x47, 0xF8, 0xBC, 0xE6,
+ 0xE5, 0x63, 0xA4, 0x40, 0xF2, 0x77, 0x03, 0x7D, 0x81, 0x2D, 0xEB, 0x33, 0xA0, 0xF4, 0xA1, 0x39, 0x45, 0xD8, 0x98, 0xC2,
+ 0x96, 0x4F, 0xE3, 0x42, 0xE2, 0xFE, 0x1A, 0x7F, 0x9B, 0x8E, 0xE7, 0xEB, 0x4A, 0x7C, 0x0F, 0x9E, 0x16, 0x2B, 0xCE, 0x33,
+ 0x57, 0x6B, 0x31, 0x5E, 0xCE, 0xCB, 0xB6, 0x40, 0x68, 0x37, 0xBF, 0x51, 0xF5, 0x02, 0x21, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xBC, 0xE6, 0xFA, 0xAD, 0xA7, 0x17, 0x9E, 0x84,
+ 0xF3, 0xB9, 0xCA, 0xC2, 0xFC, 0x63, 0x25, 0x51, 0x02, 0x01, 0x01, 0x03, 0x42, 0x00, 0x04, 0xAC, 0x62, 0xD7, 0x31, 0x3B,
+ 0x19, 0x1F, 0x44, 0x76, 0x6E, 0x79, 0x03, 0xB9, 0xC8, 0x26, 0xC4, 0x1E, 0x38, 0x3A, 0x41, 0xFE, 0xB4, 0x72, 0xA2, 0x36,
+ 0xBB, 0x82, 0x9C, 0xB9, 0x07, 0x62, 0x6F, 0x1C, 0x79, 0x12, 0xCA, 0x9C, 0x3D, 0xAA, 0x7A, 0x96, 0xFF, 0xAF, 0x5B, 0x6F,
+ 0xE1, 0x72, 0x60, 0xB0, 0x7F, 0x44, 0x38, 0x05, 0x21, 0xCC, 0x49, 0x78, 0x89, 0xC1, 0xEF, 0xEE, 0x81, 0x8E, 0xF5, 0x16,
+ 0x50, 0x1B, 0x01, 0x03, 0x1C, 0x21, 0x07, 0x1F, 0x08, 0x03, 0x70, 0x69, 0x62, 0x08, 0x09, 0x69, 0x6E, 0x74, 0x65, 0x72,
+ 0x66, 0x61, 0x63, 0x65, 0x08, 0x02, 0x69, 0x64, 0x08, 0x01, 0x32, 0x08, 0x03, 0x4B, 0x45, 0x59, 0x08, 0x01, 0x01, 0xFD,
+ 0x00, 0xFD, 0x26, 0xFD, 0x00, 0xFE, 0x0F, 0x32, 0x30, 0x31, 0x37, 0x30, 0x31, 0x30, 0x32, 0x54, 0x30, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0xFD, 0x00, 0xFF, 0x0F, 0x32, 0x30, 0x31, 0x38, 0x30, 0x31, 0x30, 0x32, 0x54, 0x30, 0x30, 0x30, 0x30, 0x30,
+ 0x30, 0x17, 0x47, 0x30, 0x45, 0x02, 0x21, 0x00, 0xF2, 0x0D, 0x1D, 0x60, 0x0B, 0x2D, 0x97, 0x3A, 0x6B, 0xEE, 0xEC, 0x56,
+ 0xD1, 0x64, 0xBF, 0xED, 0x68, 0xB7, 0x10, 0x0B, 0xDF, 0x81, 0x29, 0xCD, 0xB0, 0xBB, 0x87, 0x0D, 0xDA, 0x12, 0x52, 0xCC,
+ 0x02, 0x20, 0x64, 0x33, 0x4E, 0x91, 0xAF, 0x81, 0xF4, 0xE7, 0xAD, 0x38, 0x8E, 0xBF, 0x79, 0xA7, 0x70, 0x1E, 0xD6, 0x71,
+ 0x7E, 0xF5, 0xEB, 0x92, 0x56, 0x5F, 0xC7, 0x05, 0xDC, 0x27, 0xE5, 0x11, 0xC2, 0x43
+};
+
+const uint8_t ID2_KEY2_CERT1[] = {
+ 0x06, 0xFD, 0x02, 0x26, 0x07, 0x2B, 0x08, 0x03, 0x70, 0x69, 0x62, 0x08, 0x09, 0x69, 0x6E, 0x74, 0x65, 0x72, 0x66, 0x61,
+ 0x63, 0x65, 0x08, 0x02, 0x69, 0x64, 0x08, 0x01, 0x32, 0x08, 0x03, 0x4B, 0x45, 0x59, 0x08, 0x01, 0x02, 0x08, 0x06, 0x69,
+ 0x73, 0x73, 0x75, 0x65, 0x72, 0x08, 0x02, 0xFD, 0x01, 0x14, 0x09, 0x18, 0x01, 0x02, 0x19, 0x04, 0x00, 0x36, 0xEE, 0x80,
+ 0x15, 0xFD, 0x01, 0x4F, 0x30, 0x82, 0x01, 0x4B, 0x30, 0x82, 0x01, 0x03, 0x06, 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02,
+ 0x01, 0x30, 0x81, 0xF7, 0x02, 0x01, 0x01, 0x30, 0x2C, 0x06, 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x01, 0x01, 0x02, 0x21,
+ 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x30, 0x5B, 0x04, 0x20, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x04, 0x20, 0x5A, 0xC6, 0x35, 0xD8, 0xAA, 0x3A, 0x93, 0xE7, 0xB3,
+ 0xEB, 0xBD, 0x55, 0x76, 0x98, 0x86, 0xBC, 0x65, 0x1D, 0x06, 0xB0, 0xCC, 0x53, 0xB0, 0xF6, 0x3B, 0xCE, 0x3C, 0x3E, 0x27,
+ 0xD2, 0x60, 0x4B, 0x03, 0x15, 0x00, 0xC4, 0x9D, 0x36, 0x08, 0x86, 0xE7, 0x04, 0x93, 0x6A, 0x66, 0x78, 0xE1, 0x13, 0x9D,
+ 0x26, 0xB7, 0x81, 0x9F, 0x7E, 0x90, 0x04, 0x41, 0x04, 0x6B, 0x17, 0xD1, 0xF2, 0xE1, 0x2C, 0x42, 0x47, 0xF8, 0xBC, 0xE6,
+ 0xE5, 0x63, 0xA4, 0x40, 0xF2, 0x77, 0x03, 0x7D, 0x81, 0x2D, 0xEB, 0x33, 0xA0, 0xF4, 0xA1, 0x39, 0x45, 0xD8, 0x98, 0xC2,
+ 0x96, 0x4F, 0xE3, 0x42, 0xE2, 0xFE, 0x1A, 0x7F, 0x9B, 0x8E, 0xE7, 0xEB, 0x4A, 0x7C, 0x0F, 0x9E, 0x16, 0x2B, 0xCE, 0x33,
+ 0x57, 0x6B, 0x31, 0x5E, 0xCE, 0xCB, 0xB6, 0x40, 0x68, 0x37, 0xBF, 0x51, 0xF5, 0x02, 0x21, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xBC, 0xE6, 0xFA, 0xAD, 0xA7, 0x17, 0x9E, 0x84,
+ 0xF3, 0xB9, 0xCA, 0xC2, 0xFC, 0x63, 0x25, 0x51, 0x02, 0x01, 0x01, 0x03, 0x42, 0x00, 0x04, 0x2C, 0xC3, 0xAF, 0xA8, 0x73,
+ 0xF2, 0x61, 0xCF, 0x48, 0x04, 0x0F, 0x9D, 0xD3, 0xAF, 0x0B, 0xC6, 0x2F, 0x4D, 0xDA, 0x0E, 0x4C, 0x66, 0x1D, 0x03, 0x9D,
+ 0xFE, 0x2C, 0x0B, 0xB6, 0x25, 0x60, 0xBC, 0xFA, 0xDA, 0xFE, 0x6F, 0x43, 0xFA, 0x95, 0x45, 0x57, 0x8A, 0x25, 0xEC, 0x0F,
+ 0xF2, 0xB7, 0x43, 0x85, 0x0D, 0x0B, 0x8D, 0x97, 0x40, 0x83, 0x4C, 0x28, 0x1B, 0xD4, 0x2E, 0x99, 0x2C, 0x73, 0x7D, 0x16,
+ 0x50, 0x1B, 0x01, 0x03, 0x1C, 0x21, 0x07, 0x1F, 0x08, 0x03, 0x70, 0x69, 0x62, 0x08, 0x09, 0x69, 0x6E, 0x74, 0x65, 0x72,
+ 0x66, 0x61, 0x63, 0x65, 0x08, 0x02, 0x69, 0x64, 0x08, 0x01, 0x32, 0x08, 0x03, 0x4B, 0x45, 0x59, 0x08, 0x01, 0x02, 0xFD,
+ 0x00, 0xFD, 0x26, 0xFD, 0x00, 0xFE, 0x0F, 0x32, 0x30, 0x31, 0x37, 0x30, 0x31, 0x30, 0x32, 0x54, 0x30, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0xFD, 0x00, 0xFF, 0x0F, 0x32, 0x30, 0x31, 0x38, 0x30, 0x31, 0x30, 0x32, 0x54, 0x30, 0x30, 0x30, 0x30, 0x30,
+ 0x30, 0x17, 0x47, 0x30, 0x45, 0x02, 0x20, 0x56, 0x34, 0x49, 0xAC, 0x72, 0x4E, 0x58, 0x24, 0x6F, 0x14, 0xEE, 0xD3, 0x01,
+ 0x5B, 0xD4, 0x0A, 0x26, 0x2B, 0x6A, 0xD1, 0xB3, 0x33, 0x69, 0x4D, 0x64, 0x0C, 0xAA, 0xAE, 0x63, 0x59, 0x6A, 0xFD, 0x02,
+ 0x21, 0x00, 0xFD, 0xB9, 0x9E, 0x37, 0x70, 0x9C, 0xE2, 0x7A, 0x0A, 0xFD, 0x64, 0x99, 0x1B, 0xA3, 0x78, 0x83, 0x09, 0xC6,
+ 0xA0, 0x6D, 0x7A, 0x55, 0x8F, 0x6C, 0x35, 0xAB, 0x63, 0x78, 0x9D, 0xF3, 0xDC, 0xBC
+};
+
+const uint8_t ID2_KEY2_CERT2[] = {
+ 0x06, 0xFD, 0x02, 0x27, 0x07, 0x2B, 0x08, 0x03, 0x70, 0x69, 0x62, 0x08, 0x09, 0x69, 0x6E, 0x74, 0x65, 0x72, 0x66, 0x61,
+ 0x63, 0x65, 0x08, 0x02, 0x69, 0x64, 0x08, 0x01, 0x32, 0x08, 0x03, 0x4B, 0x45, 0x59, 0x08, 0x01, 0x02, 0x08, 0x06, 0x69,
+ 0x73, 0x73, 0x75, 0x65, 0x72, 0x08, 0x02, 0xFD, 0x02, 0x14, 0x09, 0x18, 0x01, 0x02, 0x19, 0x04, 0x00, 0x36, 0xEE, 0x80,
+ 0x15, 0xFD, 0x01, 0x4F, 0x30, 0x82, 0x01, 0x4B, 0x30, 0x82, 0x01, 0x03, 0x06, 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02,
+ 0x01, 0x30, 0x81, 0xF7, 0x02, 0x01, 0x01, 0x30, 0x2C, 0x06, 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x01, 0x01, 0x02, 0x21,
+ 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x30, 0x5B, 0x04, 0x20, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x04, 0x20, 0x5A, 0xC6, 0x35, 0xD8, 0xAA, 0x3A, 0x93, 0xE7, 0xB3,
+ 0xEB, 0xBD, 0x55, 0x76, 0x98, 0x86, 0xBC, 0x65, 0x1D, 0x06, 0xB0, 0xCC, 0x53, 0xB0, 0xF6, 0x3B, 0xCE, 0x3C, 0x3E, 0x27,
+ 0xD2, 0x60, 0x4B, 0x03, 0x15, 0x00, 0xC4, 0x9D, 0x36, 0x08, 0x86, 0xE7, 0x04, 0x93, 0x6A, 0x66, 0x78, 0xE1, 0x13, 0x9D,
+ 0x26, 0xB7, 0x81, 0x9F, 0x7E, 0x90, 0x04, 0x41, 0x04, 0x6B, 0x17, 0xD1, 0xF2, 0xE1, 0x2C, 0x42, 0x47, 0xF8, 0xBC, 0xE6,
+ 0xE5, 0x63, 0xA4, 0x40, 0xF2, 0x77, 0x03, 0x7D, 0x81, 0x2D, 0xEB, 0x33, 0xA0, 0xF4, 0xA1, 0x39, 0x45, 0xD8, 0x98, 0xC2,
+ 0x96, 0x4F, 0xE3, 0x42, 0xE2, 0xFE, 0x1A, 0x7F, 0x9B, 0x8E, 0xE7, 0xEB, 0x4A, 0x7C, 0x0F, 0x9E, 0x16, 0x2B, 0xCE, 0x33,
+ 0x57, 0x6B, 0x31, 0x5E, 0xCE, 0xCB, 0xB6, 0x40, 0x68, 0x37, 0xBF, 0x51, 0xF5, 0x02, 0x21, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xBC, 0xE6, 0xFA, 0xAD, 0xA7, 0x17, 0x9E, 0x84,
+ 0xF3, 0xB9, 0xCA, 0xC2, 0xFC, 0x63, 0x25, 0x51, 0x02, 0x01, 0x01, 0x03, 0x42, 0x00, 0x04, 0x2C, 0xC3, 0xAF, 0xA8, 0x73,
+ 0xF2, 0x61, 0xCF, 0x48, 0x04, 0x0F, 0x9D, 0xD3, 0xAF, 0x0B, 0xC6, 0x2F, 0x4D, 0xDA, 0x0E, 0x4C, 0x66, 0x1D, 0x03, 0x9D,
+ 0xFE, 0x2C, 0x0B, 0xB6, 0x25, 0x60, 0xBC, 0xFA, 0xDA, 0xFE, 0x6F, 0x43, 0xFA, 0x95, 0x45, 0x57, 0x8A, 0x25, 0xEC, 0x0F,
+ 0xF2, 0xB7, 0x43, 0x85, 0x0D, 0x0B, 0x8D, 0x97, 0x40, 0x83, 0x4C, 0x28, 0x1B, 0xD4, 0x2E, 0x99, 0x2C, 0x73, 0x7D, 0x16,
+ 0x50, 0x1B, 0x01, 0x03, 0x1C, 0x21, 0x07, 0x1F, 0x08, 0x03, 0x70, 0x69, 0x62, 0x08, 0x09, 0x69, 0x6E, 0x74, 0x65, 0x72,
+ 0x66, 0x61, 0x63, 0x65, 0x08, 0x02, 0x69, 0x64, 0x08, 0x01, 0x32, 0x08, 0x03, 0x4B, 0x45, 0x59, 0x08, 0x01, 0x02, 0xFD,
+ 0x00, 0xFD, 0x26, 0xFD, 0x00, 0xFE, 0x0F, 0x32, 0x30, 0x31, 0x37, 0x30, 0x31, 0x30, 0x32, 0x54, 0x30, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0xFD, 0x00, 0xFF, 0x0F, 0x32, 0x30, 0x31, 0x38, 0x30, 0x31, 0x30, 0x32, 0x54, 0x30, 0x30, 0x30, 0x30, 0x30,
+ 0x30, 0x17, 0x48, 0x30, 0x46, 0x02, 0x21, 0x00, 0xB3, 0xF5, 0x96, 0x19, 0xE7, 0xF9, 0x6B, 0xCF, 0x14, 0x64, 0xB1, 0x08,
+ 0xFA, 0xFF, 0xB3, 0x52, 0x8B, 0x41, 0xCB, 0xE7, 0xE0, 0x3D, 0x14, 0x7B, 0xC2, 0xD0, 0xC8, 0x89, 0x88, 0xFA, 0x95, 0x73,
+ 0x02, 0x21, 0x00, 0xEC, 0x8E, 0x0C, 0x8B, 0x8C, 0x18, 0x8D, 0x00, 0x7C, 0x12, 0x68, 0x57, 0x87, 0xB1, 0x99, 0x69, 0xDA,
+ 0x46, 0xEF, 0x14, 0x2D, 0x04, 0x18, 0xBE, 0x1D, 0xAE, 0x79, 0x49, 0xFD, 0x22, 0x8E, 0xBB
+};
+
+PibDataFixture::PibDataFixture()
+ : id1Key1Cert1(Block(ID1_KEY1_CERT1, sizeof(ID1_KEY1_CERT1)))
+ , id1Key1Cert2(Block(ID1_KEY1_CERT2, sizeof(ID1_KEY1_CERT2)))
+ , id1Key2Cert1(Block(ID1_KEY2_CERT1, sizeof(ID1_KEY2_CERT1)))
+ , id1Key2Cert2(Block(ID1_KEY2_CERT2, sizeof(ID1_KEY2_CERT2)))
+
+ , id2Key1Cert1(Block(ID2_KEY1_CERT1, sizeof(ID2_KEY1_CERT1)))
+ , id2Key1Cert2(Block(ID2_KEY1_CERT2, sizeof(ID2_KEY1_CERT2)))
+ , id2Key2Cert1(Block(ID2_KEY2_CERT1, sizeof(ID2_KEY2_CERT1)))
+ , id2Key2Cert2(Block(ID2_KEY2_CERT2, sizeof(ID2_KEY2_CERT2)))
+
+ , id1(id1Key1Cert1.getIdentity())
+ , id2(id2Key1Cert1.getIdentity())
+
+ , id1Key1Name(id1Key1Cert1.getKeyName())
+ , id1Key2Name(id1Key2Cert1.getKeyName())
+
+ , id2Key1Name(id2Key1Cert1.getKeyName())
+ , id2Key2Name(id2Key2Cert1.getKeyName())
+
+ , id1Key1(id1Key1Cert1.getPublicKey())
+ , id1Key2(id1Key2Cert1.getPublicKey())
+ , id2Key1(id2Key1Cert1.getPublicKey())
+ , id2Key2(id2Key2Cert1.getPublicKey())
+{
+ BOOST_ASSERT(id1Key1Cert1.getPublicKey() == id1Key1Cert2.getPublicKey());
+ BOOST_ASSERT(id1Key2Cert1.getPublicKey() == id1Key2Cert2.getPublicKey());
+ BOOST_ASSERT(id2Key1Cert1.getPublicKey() == id2Key1Cert2.getPublicKey());
+ BOOST_ASSERT(id2Key2Cert1.getPublicKey() == id2Key2Cert2.getPublicKey());
+
+ BOOST_ASSERT(id1Key1Cert1.getPublicKey() == id1Key1);
+ BOOST_ASSERT(id1Key1Cert2.getPublicKey() == id1Key1);
+ BOOST_ASSERT(id1Key2Cert1.getPublicKey() == id1Key2);
+ BOOST_ASSERT(id1Key2Cert2.getPublicKey() == id1Key2);
+
+ BOOST_ASSERT(id2Key1Cert1.getPublicKey() == id2Key1);
+ BOOST_ASSERT(id2Key1Cert2.getPublicKey() == id2Key1);
+ BOOST_ASSERT(id2Key2Cert1.getPublicKey() == id2Key2);
+ BOOST_ASSERT(id2Key2Cert2.getPublicKey() == id2Key2);
+
+ BOOST_ASSERT(id1Key1Cert2.getIdentity() == id1);
+ BOOST_ASSERT(id1Key2Cert1.getIdentity() == id1);
+ BOOST_ASSERT(id1Key2Cert2.getIdentity() == id1);
+
+ BOOST_ASSERT(id2Key1Cert2.getIdentity() == id2);
+ BOOST_ASSERT(id2Key2Cert1.getIdentity() == id2);
+ BOOST_ASSERT(id2Key2Cert2.getIdentity() == id2);
+
+ BOOST_ASSERT(id1Key1Cert2.getKeyName() == id1Key1Name);
+ BOOST_ASSERT(id1Key2Cert2.getKeyName() == id1Key2Name);
+
+ BOOST_ASSERT(id2Key1Cert2.getKeyName() == id2Key1Name);
+ BOOST_ASSERT(id2Key2Cert2.getKeyName() == id2Key2Name);
+}
+
+} // namespace tests
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/pib/pib-data-fixture.hpp b/tests/unit/security/pib/pib-data-fixture.hpp
new file mode 100644
index 0000000..2328702
--- /dev/null
+++ b/tests/unit/security/pib/pib-data-fixture.hpp
@@ -0,0 +1,66 @@
+/* -*- 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.
+ */
+
+#ifndef NDN_TESTS_SECURITY_PIB_DATA_FIXTURE_HPP
+#define NDN_TESTS_SECURITY_PIB_DATA_FIXTURE_HPP
+
+#include "security/v2/certificate.hpp"
+
+#include "boost-test.hpp"
+
+namespace ndn {
+namespace security {
+namespace tests {
+
+class PibDataFixture
+{
+public:
+ PibDataFixture();
+
+public:
+ v2::Certificate id1Key1Cert1;
+ v2::Certificate id1Key1Cert2;
+ v2::Certificate id1Key2Cert1;
+ v2::Certificate id1Key2Cert2;
+ v2::Certificate id2Key1Cert1;
+ v2::Certificate id2Key1Cert2;
+ v2::Certificate id2Key2Cert1;
+ v2::Certificate id2Key2Cert2;
+
+ Name id1;
+ Name id2;
+
+ Name id1Key1Name;
+ Name id1Key2Name;
+ Name id2Key1Name;
+ Name id2Key2Name;
+
+ Buffer id1Key1;
+ Buffer id1Key2;
+ Buffer id2Key1;
+ Buffer id2Key2;
+};
+
+} // namespace tests
+} // namespace security
+} // namespace ndn
+
+#endif // NDN_TESTS_SECURITY_PIB_DATA_FIXTURE_HPP
diff --git a/tests/unit/security/pib/pib-impl.t.cpp b/tests/unit/security/pib/pib-impl.t.cpp
new file mode 100644
index 0000000..6094b0e
--- /dev/null
+++ b/tests/unit/security/pib/pib-impl.t.cpp
@@ -0,0 +1,350 @@
+/* -*- 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 "security/pib/pib-memory.hpp"
+#include "security/pib/pib-sqlite3.hpp"
+#include "security/pib/pib.hpp"
+#include "security/security-common.hpp"
+
+#include "boost-test.hpp"
+#include "pib-data-fixture.hpp"
+
+#include <boost/filesystem.hpp>
+#include <boost/mpl/list.hpp>
+
+namespace ndn {
+namespace security {
+namespace pib {
+namespace tests {
+
+using namespace ndn::security::tests;
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(Pib)
+BOOST_AUTO_TEST_SUITE(TestPibImpl)
+
+using pib::Pib;
+
+class PibMemoryFixture : public PibDataFixture
+{
+public:
+ PibMemory pib;
+};
+
+class PibSqlite3Fixture : public PibDataFixture
+{
+public:
+ PibSqlite3Fixture()
+ : tmpPath(boost::filesystem::path(UNIT_TEST_CONFIG_PATH) / "DbTest")
+ , pib(tmpPath.c_str())
+ {
+ }
+
+ ~PibSqlite3Fixture()
+ {
+ boost::filesystem::remove_all(tmpPath);
+ }
+
+public:
+ boost::filesystem::path tmpPath;
+ PibSqlite3 pib;
+};
+
+typedef boost::mpl::list<PibMemoryFixture,
+ PibSqlite3Fixture> PibImpls;
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE(TpmLocator, T, PibImpls, T)
+{
+ // Basic getting and setting
+ BOOST_CHECK_NO_THROW(this->pib.getTpmLocator());
+
+ BOOST_CHECK_NO_THROW(this->pib.setTpmLocator("tpmLocator"));
+ BOOST_CHECK_EQUAL(this->pib.getTpmLocator(), "tpmLocator");
+
+ // Add cert, and do not change TPM locator
+ this->pib.addCertificate(this->id1Key1Cert1);
+ BOOST_CHECK(this->pib.hasIdentity(this->id1));
+ BOOST_CHECK(this->pib.hasKey(this->id1Key1Name));
+ BOOST_CHECK(this->pib.hasCertificate(this->id1Key1Cert1.getName()));
+
+ // Set TPM locator to the same value, nothing should change
+ this->pib.setTpmLocator("tpmLocator");
+ BOOST_CHECK(this->pib.hasIdentity(this->id1));
+ BOOST_CHECK(this->pib.hasKey(this->id1Key1Name));
+ BOOST_CHECK(this->pib.hasCertificate(this->id1Key1Cert1.getName()));
+
+ // Change TPM locator (contents of PIB should not change)
+ this->pib.setTpmLocator("newTpmLocator");
+ BOOST_CHECK(this->pib.hasIdentity(this->id1));
+ BOOST_CHECK(this->pib.hasKey(this->id1Key1Name));
+ BOOST_CHECK(this->pib.hasCertificate(this->id1Key1Cert1.getName()));
+}
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE(IdentityManagement, T, PibImpls, T)
+{
+ // no default setting, throw Error
+ BOOST_CHECK_THROW(this->pib.getDefaultIdentity(), Pib::Error);
+
+ // check id1, which should not exist
+ BOOST_CHECK_EQUAL(this->pib.hasIdentity(this->id1), false);
+
+ // add id1, should be default
+ this->pib.addIdentity(this->id1);
+ BOOST_CHECK_EQUAL(this->pib.hasIdentity(this->id1), true);
+ BOOST_CHECK_NO_THROW(this->pib.getDefaultIdentity());
+ BOOST_CHECK_EQUAL(this->pib.getDefaultIdentity(), this->id1);
+
+ // add id2, should not be default
+ this->pib.addIdentity(this->id2);
+ BOOST_CHECK_EQUAL(this->pib.hasIdentity(this->id2), true);
+ BOOST_CHECK_EQUAL(this->pib.getDefaultIdentity(), this->id1);
+
+ // set id2 explicitly as default
+ this->pib.setDefaultIdentity(this->id2);
+ BOOST_CHECK_EQUAL(this->pib.getDefaultIdentity(), this->id2);
+
+ // remove id2, should not have default identity
+ this->pib.removeIdentity(this->id2);
+ BOOST_CHECK_EQUAL(this->pib.hasIdentity(this->id2), false);
+ BOOST_CHECK_THROW(this->pib.getDefaultIdentity(), Pib::Error);
+
+ // add id2 again, should be default
+ this->pib.addIdentity(this->id2);
+ BOOST_CHECK_EQUAL(this->pib.getDefaultIdentity(), this->id2);
+
+ // get all identities, should contain id1 and id2
+ std::set<Name> idNames = this->pib.getIdentities();
+ BOOST_CHECK_EQUAL(idNames.size(), 2);
+ BOOST_CHECK_EQUAL(idNames.count(this->id1), 1);
+ BOOST_CHECK_EQUAL(idNames.count(this->id2), 1);
+}
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE(ClearIdentities, T, PibImpls, T)
+{
+ this->pib.setTpmLocator("tpmLocator");
+
+ // Add id, key, and cert
+ this->pib.addCertificate(this->id1Key1Cert1);
+ BOOST_CHECK(this->pib.hasIdentity(this->id1));
+ BOOST_CHECK(this->pib.hasKey(this->id1Key1Name));
+ BOOST_CHECK(this->pib.hasCertificate(this->id1Key1Cert1.getName()));
+
+ // Clear identities
+ this->pib.clearIdentities();
+ BOOST_CHECK_EQUAL(this->pib.getIdentities().size(), 0);
+ BOOST_CHECK_EQUAL(this->pib.getKeysOfIdentity(this->id1).size(), 0);
+ BOOST_CHECK_EQUAL(this->pib.getCertificatesOfKey(this->id1Key1Name).size(), 0);
+ BOOST_CHECK_EQUAL(this->pib.getTpmLocator(), "tpmLocator");
+}
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE(KeyManagement, T, PibImpls, T)
+{
+ // no default setting, throw Error
+ BOOST_CHECK_THROW(this->pib.getDefaultKeyOfIdentity(this->id1), Pib::Error);
+
+ // check id1Key1, should not exist, neither should id1.
+ BOOST_CHECK_EQUAL(this->pib.hasKey(this->id1Key1Name), false);
+ BOOST_CHECK_EQUAL(this->pib.hasIdentity(this->id1), false);
+
+ // add id1Key1, should be default, id1 should be added implicitly
+ this->pib.addKey(this->id1, this->id1Key1Name, this->id1Key1.data(), this->id1Key1.size());
+ BOOST_CHECK_EQUAL(this->pib.hasKey(this->id1Key1Name), true);
+ BOOST_CHECK_EQUAL(this->pib.hasIdentity(this->id1), true);
+ const Buffer& keyBits = this->pib.getKeyBits(this->id1Key1Name);
+ BOOST_CHECK(keyBits == this->id1Key1);
+ BOOST_CHECK_NO_THROW(this->pib.getDefaultKeyOfIdentity(this->id1));
+ BOOST_CHECK_EQUAL(this->pib.getDefaultKeyOfIdentity(this->id1), this->id1Key1Name);
+
+ // add id1Key2, should not be default
+ this->pib.addKey(this->id1, this->id1Key2Name, this->id1Key2.data(), this->id1Key2.size());
+ BOOST_CHECK_EQUAL(this->pib.hasKey(this->id1Key2Name), true);
+ BOOST_CHECK_EQUAL(this->pib.getDefaultKeyOfIdentity(this->id1), this->id1Key1Name);
+
+ // set id1Key2 explicitly as default
+ this->pib.setDefaultKeyOfIdentity(this->id1, this->id1Key2Name);
+ BOOST_CHECK_EQUAL(this->pib.getDefaultKeyOfIdentity(this->id1), this->id1Key2Name);
+
+ // set a non-existing key as default, throw Error
+ BOOST_CHECK_THROW(this->pib.setDefaultKeyOfIdentity(this->id1, Name("/non-existing")),
+ Pib::Error);
+
+ // remove id1Key2, should not have default key
+ this->pib.removeKey(this->id1Key2Name);
+ BOOST_CHECK_EQUAL(this->pib.hasKey(this->id1Key2Name), false);
+ BOOST_CHECK_THROW(this->pib.getKeyBits(this->id1Key2Name), Pib::Error);
+ BOOST_CHECK_THROW(this->pib.getDefaultKeyOfIdentity(this->id1), Pib::Error);
+
+ // add id1Key2 back, should be default
+ this->pib.addKey(this->id1, this->id1Key2Name, this->id1Key2.data(), this->id1Key2.size());
+ BOOST_CHECK_NO_THROW(this->pib.getKeyBits(this->id1Key2Name));
+ BOOST_CHECK_EQUAL(this->pib.getDefaultKeyOfIdentity(this->id1), this->id1Key2Name);
+
+ // get all the keys: id1Key1 and id1Key2
+ std::set<Name> keyNames = this->pib.getKeysOfIdentity(this->id1);
+ BOOST_CHECK_EQUAL(keyNames.size(), 2);
+ BOOST_CHECK_EQUAL(keyNames.count(this->id1Key1Name), 1);
+ BOOST_CHECK_EQUAL(keyNames.count(this->id1Key2Name), 1);
+
+ // remove id1, should remove all the keys
+ this->pib.removeIdentity(this->id1);
+ keyNames = this->pib.getKeysOfIdentity(this->id1);
+ BOOST_CHECK_EQUAL(keyNames.size(), 0);
+}
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE(CertificateManagement, T, PibImpls, T)
+{
+ // no default setting, throw Error
+ BOOST_CHECK_THROW(this->pib.getDefaultCertificateOfKey(this->id1Key1Name), Pib::Error);
+
+ // check id1Key1Cert1, should not exist, neither should id1 and id1Key1
+ BOOST_CHECK_EQUAL(this->pib.hasCertificate(this->id1Key1Cert1.getName()), false);
+ BOOST_CHECK_EQUAL(this->pib.hasIdentity(this->id1), false);
+ BOOST_CHECK_EQUAL(this->pib.hasKey(this->id1Key1Name), false);
+
+ // add id1Key1Cert1, should be default, id1 and id1Key1 should be added implicitly
+ this->pib.addCertificate(this->id1Key1Cert1);
+ BOOST_CHECK_EQUAL(this->pib.hasCertificate(this->id1Key1Cert1.getName()), true);
+ BOOST_CHECK_EQUAL(this->pib.hasIdentity(this->id1), true);
+ BOOST_CHECK_EQUAL(this->pib.hasKey(this->id1Key1Name), true);
+ BOOST_CHECK_EQUAL(this->pib.getCertificate(this->id1Key1Cert1.getName()).wireEncode(),
+ this->id1Key1Cert1.wireEncode());
+ BOOST_CHECK_NO_THROW(this->pib.getDefaultCertificateOfKey(this->id1Key1Name));
+ BOOST_CHECK_EQUAL(this->pib.getDefaultCertificateOfKey(this->id1Key1Name), this->id1Key1Cert1);
+
+ // add id1Key1Cert2, should not be default
+ this->pib.addCertificate(this->id1Key1Cert2);
+ BOOST_CHECK_EQUAL(this->pib.hasCertificate(this->id1Key1Cert2.getName()), true);
+ BOOST_CHECK_EQUAL(this->pib.getDefaultCertificateOfKey(this->id1Key1Name), this->id1Key1Cert1);
+
+ // set id1Key1Cert2 explicitly as default
+ this->pib.setDefaultCertificateOfKey(this->id1Key1Name, this->id1Key1Cert2.getName());
+ BOOST_CHECK_EQUAL(this->pib.getDefaultCertificateOfKey(this->id1Key1Name), this->id1Key1Cert2);
+
+ // set a non-existing cert as default, throw Error
+ BOOST_CHECK_THROW(this->pib.setDefaultCertificateOfKey(this->id1Key1Name, Name("/non-existing")),
+ Pib::Error);
+
+ // remove id1Key1Cert2, should not have default cert
+ this->pib.removeCertificate(this->id1Key1Cert2.getName());
+ BOOST_CHECK_EQUAL(this->pib.hasCertificate(this->id1Key1Cert2.getName()), false);
+ BOOST_CHECK_THROW(this->pib.getCertificate(this->id1Key1Cert2.getName()), Pib::Error);
+ BOOST_CHECK_THROW(this->pib.getDefaultCertificateOfKey(this->id1Key1Name), Pib::Error);
+
+ // add id1Key1Cert2, should be default
+ this->pib.addCertificate(this->id1Key1Cert2);
+ BOOST_CHECK_NO_THROW(this->pib.getCertificate(this->id1Key1Cert1.getName()));
+ BOOST_CHECK_EQUAL(this->pib.getDefaultCertificateOfKey(this->id1Key1Name), this->id1Key1Cert2);
+
+ // get all certificates: id1Key1Cert1 and id1Key1Cert2
+ std::set<Name> certNames = this->pib.getCertificatesOfKey(this->id1Key1Name);
+ BOOST_CHECK_EQUAL(certNames.size(), 2);
+ BOOST_CHECK_EQUAL(certNames.count(this->id1Key1Cert1.getName()), 1);
+ BOOST_CHECK_EQUAL(certNames.count(this->id1Key1Cert2.getName()), 1);
+
+ // remove id1Key1, should remove all the certs
+ this->pib.removeKey(this->id1Key1Name);
+ certNames = this->pib.getCertificatesOfKey(this->id1Key1Name);
+ BOOST_CHECK_EQUAL(certNames.size(), 0);
+}
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE(DefaultsManagement, T, PibImpls, T)
+{
+ this->pib.addIdentity(this->id1);
+ BOOST_CHECK_EQUAL(this->pib.getDefaultIdentity(), this->id1);
+
+ this->pib.addIdentity(this->id2);
+ BOOST_CHECK_EQUAL(this->pib.getDefaultIdentity(), this->id1);
+
+ this->pib.removeIdentity(this->id1);
+ BOOST_CHECK_THROW(this->pib.getDefaultIdentity(), Pib::Error);
+
+ this->pib.addKey(this->id2, this->id2Key1Name, this->id2Key1.data(), this->id2Key1.size());
+ BOOST_CHECK_EQUAL(this->pib.getDefaultIdentity(), this->id2);
+ BOOST_CHECK_EQUAL(this->pib.getDefaultKeyOfIdentity(this->id2), this->id2Key1Name);
+
+ this->pib.addKey(this->id2, this->id2Key2Name, this->id2Key2.data(), this->id2Key2.size());
+ BOOST_CHECK_EQUAL(this->pib.getDefaultKeyOfIdentity(this->id2), this->id2Key1Name);
+
+ this->pib.removeKey(this->id2Key1Name);
+ BOOST_CHECK_THROW(this->pib.getDefaultKeyOfIdentity(this->id2), Pib::Error);
+
+ this->pib.addCertificate(this->id2Key2Cert1);
+ BOOST_CHECK_EQUAL(this->pib.getDefaultKeyOfIdentity(this->id2), this->id2Key2Name);
+ BOOST_CHECK_EQUAL(this->pib.getDefaultCertificateOfKey(this->id2Key2Name).getName(), this->id2Key2Cert1.getName());
+
+ this->pib.addCertificate(this->id2Key2Cert2);
+ BOOST_CHECK_EQUAL(this->pib.getDefaultCertificateOfKey(this->id2Key2Name).getName(), this->id2Key2Cert1.getName());
+
+ this->pib.removeCertificate(this->id2Key2Cert2.getName());
+ BOOST_CHECK_EQUAL(this->pib.getDefaultCertificateOfKey(this->id2Key2Name).getName(), this->id2Key2Cert1.getName());
+}
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE(Overwrite, T, PibImpls, T)
+{
+ // check id1Key1, should not exist
+ this->pib.removeIdentity(this->id1);
+ BOOST_CHECK_EQUAL(this->pib.hasKey(this->id1Key1Name), false);
+
+ // add id1Key1
+ this->pib.addKey(this->id1, this->id1Key1Name, this->id1Key1.data(), this->id1Key1.size());
+ BOOST_CHECK_EQUAL(this->pib.hasKey(this->id1Key1Name), true);
+ const Buffer& keyBits = this->pib.getKeyBits(this->id1Key1Name);
+ BOOST_CHECK(keyBits == this->id1Key1);
+
+ // check overwrite, add a key with the same name.
+ this->pib.addKey(this->id1, this->id1Key1Name, this->id1Key2.data(), this->id1Key2.size());
+ const Buffer& keyBits2 = this->pib.getKeyBits(this->id1Key1Name);
+ BOOST_CHECK(keyBits2 == this->id1Key2);
+
+ // check id1Key1Cert1, should not exist
+ this->pib.removeIdentity(this->id1);
+ BOOST_CHECK_EQUAL(this->pib.hasCertificate(this->id1Key1Cert1.getName()), false);
+
+ // add id1Key1Cert1
+ this->pib.addKey(this->id1, this->id1Key1Name, this->id1Key1.data(), this->id1Key1.size());
+ this->pib.addCertificate(this->id1Key1Cert1);
+ BOOST_CHECK_EQUAL(this->pib.hasCertificate(this->id1Key1Cert1.getName()), true);
+
+ auto cert = this->pib.getCertificate(this->id1Key1Cert1.getName());
+ BOOST_CHECK_EQUAL(cert.wireEncode(), this->id1Key1Cert1.wireEncode());
+
+ // Create a fake cert with the same name
+ auto cert2 = this->id1Key2Cert1;
+ cert2.setName(this->id1Key1Cert1.getName());
+ cert2.setSignature(this->id1Key2Cert1.getSignature());
+ this->pib.addCertificate(cert2);
+
+ auto cert3 = this->pib.getCertificate(this->id1Key1Cert1.getName());
+ BOOST_CHECK_EQUAL(cert3.wireEncode(), cert2.wireEncode());
+
+ // both key and certificate are overwritten
+ Buffer keyBits3 = this->pib.getKeyBits(this->id1Key1Name);
+ BOOST_CHECK(keyBits3 == this->id1Key2);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestPibImpl
+BOOST_AUTO_TEST_SUITE_END() // Pib
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace pib
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/pib/pib-memory.t.cpp b/tests/unit/security/pib/pib-memory.t.cpp
new file mode 100644
index 0000000..72b0db8
--- /dev/null
+++ b/tests/unit/security/pib/pib-memory.t.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 "security/pib/pib-memory.hpp"
+
+#include "boost-test.hpp"
+
+namespace ndn {
+namespace security {
+namespace pib {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(Pib)
+BOOST_AUTO_TEST_SUITE(TestPibMemory)
+
+// Functionality is tested as part of pib-impl.t.cpp
+
+BOOST_AUTO_TEST_SUITE_END() // TestPibMemory
+BOOST_AUTO_TEST_SUITE_END() // Pib
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace pib
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/pib/pib-sqlite3.t.cpp b/tests/unit/security/pib/pib-sqlite3.t.cpp
new file mode 100644
index 0000000..b46cb50
--- /dev/null
+++ b/tests/unit/security/pib/pib-sqlite3.t.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 "security/pib/pib-sqlite3.hpp"
+
+#include "boost-test.hpp"
+
+namespace ndn {
+namespace security {
+namespace pib {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(Pib)
+BOOST_AUTO_TEST_SUITE(TestPibSqlite3)
+
+// Functionality is tested as part of pib-impl.t.cpp
+
+BOOST_AUTO_TEST_SUITE_END() // TestPibSqlite3
+BOOST_AUTO_TEST_SUITE_END() // Pib
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace pib
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/pib/pib.t.cpp b/tests/unit/security/pib/pib.t.cpp
new file mode 100644
index 0000000..0ce5afc
--- /dev/null
+++ b/tests/unit/security/pib/pib.t.cpp
@@ -0,0 +1,143 @@
+/* -*- 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 "security/pib/pib.hpp"
+#include "security/pib/pib-memory.hpp"
+
+#include "boost-test.hpp"
+#include "pib-data-fixture.hpp"
+
+namespace ndn {
+namespace security {
+namespace pib {
+namespace tests {
+
+using namespace ndn::security::tests;
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(Pib)
+BOOST_FIXTURE_TEST_SUITE(TestPib, PibDataFixture)
+
+using pib::Pib;
+
+BOOST_AUTO_TEST_CASE(ValidityChecking)
+{
+ Pib pib("pib-memory", "", make_shared<PibMemory>());
+
+ Identity id = pib.addIdentity(id1);
+
+ BOOST_CHECK_EQUAL(static_cast<bool>(id), true);
+ BOOST_CHECK_EQUAL(!id, false);
+
+ if (id)
+ BOOST_CHECK(true);
+ else
+ BOOST_CHECK(false);
+
+ // key
+ Key key = id.addKey(id1Key1.data(), id1Key1.size(), id1Key1Name);
+
+ BOOST_CHECK_EQUAL(static_cast<bool>(key), true);
+ BOOST_CHECK_EQUAL(!key, false);
+
+ if (key)
+ BOOST_CHECK(true);
+ else
+ BOOST_CHECK(false);
+}
+
+BOOST_AUTO_TEST_CASE(TpmLocator)
+{
+ Pib pib("pib-memory", "", make_shared<PibMemory>());
+
+ BOOST_CHECK_EQUAL(pib.getPibLocator(), "pib-memory:");
+ BOOST_CHECK_THROW(pib.getTpmLocator(), Pib::Error);
+
+ pib.setTpmLocator("test-tpm-locator");
+ BOOST_CHECK_NO_THROW(pib.getTpmLocator());
+
+ BOOST_CHECK_THROW(pib.getIdentity(id1), Pib::Error);
+ pib.addIdentity(id1);
+ BOOST_CHECK_NO_THROW(pib.getIdentity(id1));
+
+ pib.setTpmLocator("another-tpm-locator");
+ BOOST_CHECK_THROW(pib.getIdentity(id1), Pib::Error);
+
+ pib.addIdentity(id1);
+ BOOST_CHECK_NO_THROW(pib.getIdentity(id1));
+ pib.reset();
+ BOOST_CHECK_THROW(pib.getIdentity(id1), Pib::Error);
+ BOOST_CHECK_THROW(pib.getTpmLocator(), Pib::Error);
+}
+
+BOOST_AUTO_TEST_CASE(IdentityOperations)
+{
+ Pib pib("pib-memory", "", make_shared<PibMemory>());
+ BOOST_CHECK_EQUAL(pib.getIdentities().size(), 0);
+
+ // get non-existing identity, throw Pib::Error
+ BOOST_CHECK_THROW(pib.getIdentity(id1), Pib::Error);
+ // get default identity when it is not set yet, throw Pib::Error
+ BOOST_CHECK_THROW(pib.getDefaultIdentity(), Pib::Error);
+
+ // add identity
+ pib.addIdentity(id1);
+ BOOST_CHECK_NO_THROW(pib.getIdentity(id1));
+ BOOST_CHECK_EQUAL(pib.getIdentities().size(), 1);
+
+ // new key becomes default key when there was no default key
+ BOOST_REQUIRE_NO_THROW(pib.getDefaultIdentity());
+ BOOST_CHECK_EQUAL(pib.getDefaultIdentity().getName(), id1);
+
+ // remove identity
+ pib.removeIdentity(id1);
+ BOOST_CHECK_THROW(pib.getIdentity(id1), Pib::Error);
+ BOOST_CHECK_THROW(pib.getDefaultIdentity(), Pib::Error);
+ BOOST_CHECK_EQUAL(pib.getIdentities().size(), 0);
+
+ // set default identity
+ BOOST_REQUIRE_NO_THROW(pib.setDefaultIdentity(id1));
+ BOOST_REQUIRE_NO_THROW(pib.getDefaultIdentity());
+ BOOST_CHECK_EQUAL(pib.getDefaultIdentity().getName(), id1);
+ BOOST_CHECK_EQUAL(pib.getIdentities().size(), 1);
+ BOOST_REQUIRE_NO_THROW(pib.setDefaultIdentity(id2));
+ BOOST_REQUIRE_NO_THROW(pib.getDefaultIdentity());
+ BOOST_CHECK_EQUAL(pib.getDefaultIdentity().getName(), id2);
+ BOOST_CHECK_EQUAL(pib.getIdentities().size(), 2);
+
+ // remove default identity
+ pib.removeIdentity(id2);
+ BOOST_CHECK_THROW(pib.getIdentity(id2), Pib::Error);
+ BOOST_CHECK_THROW(pib.getDefaultIdentity(), Pib::Error);
+ BOOST_CHECK_EQUAL(pib.getIdentities().size(), 1);
+ pib.removeIdentity(id1);
+ BOOST_CHECK_THROW(pib.getIdentity(id1), Pib::Error);
+ BOOST_CHECK_EQUAL(pib.getIdentities().size(), 0);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestPib
+BOOST_AUTO_TEST_SUITE_END() // Pib
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace pib
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/safe-bag.t.cpp b/tests/unit/security/safe-bag.t.cpp
new file mode 100644
index 0000000..0423c69
--- /dev/null
+++ b/tests/unit/security/safe-bag.t.cpp
@@ -0,0 +1,170 @@
+/* -*- 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 Zhiyi Zhang <dreamerbarrychang@gmail.com>
+ */
+
+#include "security/safe-bag.hpp"
+
+#include "boost-test.hpp"
+
+namespace ndn {
+namespace security {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(TestSafeBag)
+
+const uint8_t CERT[] = {
+ 0x06, 0xc8, // Data
+ 0x07, 0x14, // Name
+ 0x08, 0x05,
+ 0x6c, 0x6f, 0x63, 0x61, 0x6c,
+ 0x08, 0x03,
+ 0x6e, 0x64, 0x6e,
+ 0x08, 0x06,
+ 0x70, 0x72, 0x65, 0x66, 0x69, 0x78,
+ 0x14, 0x07, // MetaInfo
+ 0x18, 0x01, // ContentType
+ 0x02,
+ 0x19, 0x02, // FreshnessPeriod
+ 0x27, 0x10,
+ 0x15, 0x08, // Content
+ 0x53, 0x55, 0x43, 0x43, 0x45, 0x53, 0x53, 0x21,
+ 0x16, 0x1b, // SignatureInfo
+ 0x1b, 0x01, // SignatureType
+ 0x01,
+ 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,
+ 0x17, 0x80, // SignatureValue
+ 0x2f, 0xd6, 0xf1, 0x6e, 0x80, 0x6f, 0x10, 0xbe, 0xb1, 0x6f, 0x3e, 0x31, 0xec,
+ 0xe3, 0xb9, 0xea, 0x83, 0x30, 0x40, 0x03, 0xfc, 0xa0, 0x13, 0xd9, 0xb3, 0xc6,
+ 0x25, 0x16, 0x2d, 0xa6, 0x58, 0x41, 0x69, 0x62, 0x56, 0xd8, 0xb3, 0x6a, 0x38,
+ 0x76, 0x56, 0xea, 0x61, 0xb2, 0x32, 0x70, 0x1c, 0xb6, 0x4d, 0x10, 0x1d, 0xdc,
+ 0x92, 0x8e, 0x52, 0xa5, 0x8a, 0x1d, 0xd9, 0x96, 0x5e, 0xc0, 0x62, 0x0b, 0xcf,
+ 0x3a, 0x9d, 0x7f, 0xca, 0xbe, 0xa1, 0x41, 0x71, 0x85, 0x7a, 0x8b, 0x5d, 0xa9,
+ 0x64, 0xd6, 0x66, 0xb4, 0xe9, 0x8d, 0x0c, 0x28, 0x43, 0xee, 0xa6, 0x64, 0xe8,
+ 0x55, 0xf6, 0x1c, 0x19, 0x0b, 0xef, 0x99, 0x25, 0x1e, 0xdc, 0x78, 0xb3, 0xa7,
+ 0xaa, 0x0d, 0x14, 0x58, 0x30, 0xe5, 0x37, 0x6a, 0x6d, 0xdb, 0x56, 0xac, 0xa3,
+ 0xfc, 0x90, 0x7a, 0xb8, 0x66, 0x9c, 0x0e, 0xf6, 0xb7, 0x64, 0xd1
+};
+
+const uint8_t ENCRYPTED_KEY_BAG[] = {
+ 0x2f, 0xd6, 0xf1, 0x6e, 0x80, 0x6f, 0x10, 0xbe
+};
+
+const uint8_t SAFE_BAG[] = {
+ 0x80, 0xd4, // SafeBag
+ 0x06, 0xc8, // Data
+ 0x07, 0x14, // Name
+ 0x08, 0x05,
+ 0x6c, 0x6f, 0x63, 0x61, 0x6c,
+ 0x08, 0x03,
+ 0x6e, 0x64, 0x6e,
+ 0x08, 0x06,
+ 0x70, 0x72, 0x65, 0x66, 0x69, 0x78,
+ 0x14, 0x07, // MetaInfo
+ 0x18, 0x01, // ContentType
+ 0x02,
+ 0x19, 0x02, // FreshnessPeriod
+ 0x27, 0x10,
+ 0x15, 0x08, // Content
+ 0x53, 0x55, 0x43, 0x43, 0x45, 0x53, 0x53, 0x21,
+ 0x16, 0x1b, // SignatureInfo
+ 0x1b, 0x01, // SignatureType
+ 0x01,
+ 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,
+ 0x17, 0x80, // SignatureValue
+ 0x2f, 0xd6, 0xf1, 0x6e, 0x80, 0x6f, 0x10, 0xbe, 0xb1, 0x6f, 0x3e, 0x31, 0xec,
+ 0xe3, 0xb9, 0xea, 0x83, 0x30, 0x40, 0x03, 0xfc, 0xa0, 0x13, 0xd9, 0xb3, 0xc6,
+ 0x25, 0x16, 0x2d, 0xa6, 0x58, 0x41, 0x69, 0x62, 0x56, 0xd8, 0xb3, 0x6a, 0x38,
+ 0x76, 0x56, 0xea, 0x61, 0xb2, 0x32, 0x70, 0x1c, 0xb6, 0x4d, 0x10, 0x1d, 0xdc,
+ 0x92, 0x8e, 0x52, 0xa5, 0x8a, 0x1d, 0xd9, 0x96, 0x5e, 0xc0, 0x62, 0x0b, 0xcf,
+ 0x3a, 0x9d, 0x7f, 0xca, 0xbe, 0xa1, 0x41, 0x71, 0x85, 0x7a, 0x8b, 0x5d, 0xa9,
+ 0x64, 0xd6, 0x66, 0xb4, 0xe9, 0x8d, 0x0c, 0x28, 0x43, 0xee, 0xa6, 0x64, 0xe8,
+ 0x55, 0xf6, 0x1c, 0x19, 0x0b, 0xef, 0x99, 0x25, 0x1e, 0xdc, 0x78, 0xb3, 0xa7,
+ 0xaa, 0x0d, 0x14, 0x58, 0x30, 0xe5, 0x37, 0x6a, 0x6d, 0xdb, 0x56, 0xac, 0xa3,
+ 0xfc, 0x90, 0x7a, 0xb8, 0x66, 0x9c, 0x0e, 0xf6, 0xb7, 0x64, 0xd1,
+ 0x81, 0x08, // EncryptedKeyBag
+ 0x2f, 0xd6, 0xf1, 0x6e, 0x80, 0x6f, 0x10, 0xbe
+};
+
+BOOST_AUTO_TEST_CASE(Constructor)
+{
+ Block dataBlock(CERT, sizeof(CERT));
+ Data data(dataBlock);
+ SafeBag safeBag1(data, ENCRYPTED_KEY_BAG, sizeof(ENCRYPTED_KEY_BAG));
+
+ Block safeBagBlock(SAFE_BAG, sizeof(SAFE_BAG));
+ SafeBag safeBag2(safeBagBlock);
+
+ Buffer buffer(ENCRYPTED_KEY_BAG, sizeof(ENCRYPTED_KEY_BAG));
+ SafeBag safeBag3(data, buffer);
+
+ BOOST_CHECK(safeBag1.getCertificate() == data);
+ BOOST_CHECK(safeBag1.getEncryptedKeyBag() == buffer);
+ BOOST_CHECK(safeBag2.getCertificate() == data);
+ BOOST_CHECK(safeBag2.getEncryptedKeyBag() == buffer);
+ BOOST_CHECK(safeBag3.getCertificate() == data);
+ BOOST_CHECK(safeBag3.getEncryptedKeyBag() == buffer);
+}
+
+BOOST_AUTO_TEST_CASE(EncoderAndDecoder)
+{
+ Block dataBlock(CERT, sizeof(CERT));
+ Data data(dataBlock);
+ SafeBag safeBag(data, ENCRYPTED_KEY_BAG, sizeof(ENCRYPTED_KEY_BAG));
+
+ // wire encode
+ Block wireBlock = safeBag.wireEncode();
+ Block block(SAFE_BAG, sizeof(SAFE_BAG));
+
+ // check safe bag block
+ BOOST_CHECK_EQUAL(wireBlock, block);
+
+ // wire decode
+ SafeBag safeBag2;
+ safeBag2.wireDecode(wireBlock);
+
+ // check equal
+ Buffer buffer1 = safeBag2.getEncryptedKeyBag();
+ Buffer buffer2(ENCRYPTED_KEY_BAG, sizeof(ENCRYPTED_KEY_BAG));
+ BOOST_CHECK(buffer1 == buffer2);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestSafeBag
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/signature-sha256-with-ecdsa.t.cpp b/tests/unit/security/signature-sha256-with-ecdsa.t.cpp
new file mode 100644
index 0000000..2f90302
--- /dev/null
+++ b/tests/unit/security/signature-sha256-with-ecdsa.t.cpp
@@ -0,0 +1,154 @@
+/* -*- 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 "security/signature-sha256-with-ecdsa.hpp"
+#include "security/verification-helpers.hpp"
+#include "util/scheduler.hpp"
+
+#include "boost-test.hpp"
+#include "../identity-management-time-fixture.hpp"
+#include "make-interest-data.hpp"
+
+namespace ndn {
+namespace security {
+namespace tests {
+
+using namespace ndn::tests;
+
+class SignatureSha256EcdsaTimeFixture : public IdentityManagementTimeFixture
+{
+public:
+ SignatureSha256EcdsaTimeFixture()
+ : scheduler(io)
+ {
+ }
+
+public:
+ Scheduler scheduler;
+};
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_FIXTURE_TEST_SUITE(TestSignatureSha256WithEcdsa, SignatureSha256EcdsaTimeFixture)
+
+const uint8_t sigInfo[] = {
+ 0x16, 0x1b, // SignatureInfo
+ 0x1b, 0x01, // SignatureType
+ 0x03,
+ 0x1c, 0x16, // KeyLocator
+ 0x07, 0x14, // Name: /test/key/locator
+ 0x08, 0x04,
+ 0x74, 0x65, 0x73, 0x74,
+ 0x08, 0x03,
+ 0x6b, 0x65, 0x79,
+ 0x08, 0x07,
+ 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x6f, 0x72
+};
+
+const uint8_t sigValue[] = {
+ 0x17, 0x40, // SignatureValue
+ 0x2f, 0xd6, 0xf1, 0x6e, 0x80, 0x6f, 0x10, 0xbe, 0xb1, 0x6f, 0x3e, 0x31, 0xec,
+ 0xe3, 0xb9, 0xea, 0x83, 0x30, 0x40, 0x03, 0xfc, 0xa0, 0x13, 0xd9, 0xb3, 0xc6,
+ 0x25, 0x16, 0x2d, 0xa6, 0x58, 0x41, 0x69, 0x62, 0x56, 0xd8, 0xb3, 0x6a, 0x38,
+ 0x76, 0x56, 0xea, 0x61, 0xb2, 0x32, 0x70, 0x1c, 0xb6, 0x4d, 0x10, 0x1d, 0xdc,
+ 0x92, 0x8e, 0x52, 0xa5, 0x8a, 0x1d, 0xd9, 0x96, 0x5e, 0xc0, 0x62, 0x0b
+};
+
+
+BOOST_AUTO_TEST_CASE(Decoding)
+{
+ Block sigInfoBlock(sigInfo, sizeof(sigInfo));
+ Block sigValueBlock(sigValue, sizeof(sigValue));
+
+ Signature sig(sigInfoBlock, sigValueBlock);
+ BOOST_CHECK_NO_THROW(SignatureSha256WithEcdsa{sig});
+ BOOST_CHECK_NO_THROW(sig.getKeyLocator());
+}
+
+BOOST_AUTO_TEST_CASE(Encoding)
+{
+ Name name("/test/key/locator");
+ KeyLocator keyLocator(name);
+
+ SignatureSha256WithEcdsa sig(keyLocator);
+
+ BOOST_CHECK_NO_THROW(sig.getKeyLocator());
+
+ const Block& encodeSigInfoBlock = sig.getInfo();
+
+ Block sigInfoBlock(sigInfo, sizeof(sigInfo));
+
+ BOOST_CHECK_EQUAL_COLLECTIONS(sigInfoBlock.wire(),
+ sigInfoBlock.wire() + sigInfoBlock.size(),
+ encodeSigInfoBlock.wire(),
+ encodeSigInfoBlock.wire() + encodeSigInfoBlock.size());
+
+ sig.setKeyLocator(Name("/test/another/key/locator"));
+
+ const Block& encodeSigInfoBlock2 = sig.getInfo();
+ BOOST_CHECK_NE(sigInfoBlock, encodeSigInfoBlock2);
+}
+
+BOOST_AUTO_TEST_CASE(DataSignature)
+{
+ Identity identity = addIdentity("/SecurityTestSignatureSha256WithEcdsa/DataSignature", EcKeyParams());
+
+ Data testData("/SecurityTestSignatureSha256WithEcdsa/DataSignature/Data1");
+ char content[5] = "1234";
+ testData.setContent(reinterpret_cast<uint8_t*>(content), 5);
+ BOOST_CHECK_NO_THROW(m_keyChain.sign(testData, security::SigningInfo(identity)));
+ Block dataBlock(testData.wireEncode().wire(), testData.wireEncode().size());
+
+ Data testData2;
+ testData2.wireDecode(dataBlock);
+ BOOST_CHECK(verifySignature(testData2, identity.getDefaultKey()));
+}
+
+BOOST_AUTO_TEST_CASE(InterestSignature)
+{
+ Identity identity = addIdentity("/SecurityTestSignatureSha256WithEcdsa/InterestSignature", EcKeyParams());
+
+ auto interest = makeInterest("/SecurityTestSignatureSha256WithEcdsa/InterestSignature/Interest1");
+ auto interest11 = makeInterest("/SecurityTestSignatureSha256WithEcdsa/InterestSignature/Interest1");
+
+ scheduler.scheduleEvent(100_ms, [&] {
+ m_keyChain.sign(*interest, security::SigningInfo(identity));
+ });
+
+ advanceClocks(100_ms);
+ scheduler.scheduleEvent(100_ms, [&] {
+ m_keyChain.sign(*interest11, security::SigningInfo(identity));
+ });
+
+ advanceClocks(100_ms);
+
+ Block interestBlock(interest->wireEncode().wire(), interest->wireEncode().size());
+
+ Interest interest2;
+ interest2.wireDecode(interestBlock);
+ BOOST_CHECK(verifySignature(interest2, identity.getDefaultKey()));
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestSignatureSha256WithEcdsa
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/signature-sha256-with-rsa.t.cpp b/tests/unit/security/signature-sha256-with-rsa.t.cpp
new file mode 100644
index 0000000..7de3774
--- /dev/null
+++ b/tests/unit/security/signature-sha256-with-rsa.t.cpp
@@ -0,0 +1,159 @@
+/* -*- 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 "security/signature-sha256-with-rsa.hpp"
+#include "security/verification-helpers.hpp"
+#include "util/scheduler.hpp"
+
+#include "boost-test.hpp"
+#include "../identity-management-time-fixture.hpp"
+#include "make-interest-data.hpp"
+
+namespace ndn {
+namespace security {
+namespace tests {
+
+using namespace ndn::tests;
+
+class SignatureSha256RsaTimeFixture : public IdentityManagementTimeFixture
+{
+public:
+ SignatureSha256RsaTimeFixture()
+ : scheduler(io)
+ {
+ }
+
+public:
+ Scheduler scheduler;
+};
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_FIXTURE_TEST_SUITE(TestSignatureSha256WithRsa, SignatureSha256RsaTimeFixture)
+
+const uint8_t sigInfo[] = {
+ 0x16, 0x1b, // SignatureInfo
+ 0x1b, 0x01, // SignatureType
+ 0x01,
+ 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
+};
+
+const uint8_t sigValue[] = {
+0x17, 0x80, // SignatureValue
+ 0x2f, 0xd6, 0xf1, 0x6e, 0x80, 0x6f, 0x10, 0xbe, 0xb1, 0x6f, 0x3e, 0x31, 0xec,
+ 0xe3, 0xb9, 0xea, 0x83, 0x30, 0x40, 0x03, 0xfc, 0xa0, 0x13, 0xd9, 0xb3, 0xc6,
+ 0x25, 0x16, 0x2d, 0xa6, 0x58, 0x41, 0x69, 0x62, 0x56, 0xd8, 0xb3, 0x6a, 0x38,
+ 0x76, 0x56, 0xea, 0x61, 0xb2, 0x32, 0x70, 0x1c, 0xb6, 0x4d, 0x10, 0x1d, 0xdc,
+ 0x92, 0x8e, 0x52, 0xa5, 0x8a, 0x1d, 0xd9, 0x96, 0x5e, 0xc0, 0x62, 0x0b, 0xcf,
+ 0x3a, 0x9d, 0x7f, 0xca, 0xbe, 0xa1, 0x41, 0x71, 0x85, 0x7a, 0x8b, 0x5d, 0xa9,
+ 0x64, 0xd6, 0x66, 0xb4, 0xe9, 0x8d, 0x0c, 0x28, 0x43, 0xee, 0xa6, 0x64, 0xe8,
+ 0x55, 0xf6, 0x1c, 0x19, 0x0b, 0xef, 0x99, 0x25, 0x1e, 0xdc, 0x78, 0xb3, 0xa7,
+ 0xaa, 0x0d, 0x14, 0x58, 0x30, 0xe5, 0x37, 0x6a, 0x6d, 0xdb, 0x56, 0xac, 0xa3,
+ 0xfc, 0x90, 0x7a, 0xb8, 0x66, 0x9c, 0x0e, 0xf6, 0xb7, 0x64, 0xd1
+};
+
+
+BOOST_AUTO_TEST_CASE(Decoding)
+{
+ Block sigInfoBlock(sigInfo, sizeof(sigInfo));
+ Block sigValueBlock(sigValue, sizeof(sigValue));
+
+ Signature sig(sigInfoBlock, sigValueBlock);
+ BOOST_CHECK_NO_THROW(SignatureSha256WithRsa{sig});
+ BOOST_CHECK_NO_THROW(sig.getKeyLocator());
+}
+
+BOOST_AUTO_TEST_CASE(Encoding)
+{
+ Name name("/test/key/locator");
+ KeyLocator keyLocator(name);
+
+ SignatureSha256WithRsa sig(keyLocator);
+
+ BOOST_CHECK_NO_THROW(sig.getKeyLocator());
+
+ const Block& encodeSigInfoBlock = sig.getInfo();
+
+ Block sigInfoBlock(sigInfo, sizeof(sigInfo));
+
+ BOOST_CHECK_EQUAL_COLLECTIONS(sigInfoBlock.wire(),
+ sigInfoBlock.wire() + sigInfoBlock.size(),
+ encodeSigInfoBlock.wire(),
+ encodeSigInfoBlock.wire() + encodeSigInfoBlock.size());
+
+ sig.setKeyLocator(Name("/test/another/key/locator"));
+
+ const Block& encodeSigInfoBlock2 = sig.getInfo();
+ BOOST_CHECK_NE(sigInfoBlock, encodeSigInfoBlock2);
+}
+
+BOOST_AUTO_TEST_CASE(DataSignature)
+{
+ Identity identity = addIdentity("/SecurityTestSignatureSha256WithRsa/DataSignature", RsaKeyParams());
+
+ Data testData("/SecurityTestSignatureSha256WithRsa/DataSignature/Data1");
+ char content[5] = "1234";
+ testData.setContent(reinterpret_cast<uint8_t*>(content), 5);
+ BOOST_CHECK_NO_THROW(m_keyChain.sign(testData, security::SigningInfo(identity)));
+ Block dataBlock(testData.wireEncode().wire(), testData.wireEncode().size());
+
+ Data testData2;
+ testData2.wireDecode(dataBlock);
+ BOOST_CHECK(verifySignature(testData2, identity.getDefaultKey()));
+}
+
+BOOST_AUTO_TEST_CASE(InterestSignature)
+{
+ Identity identity = addIdentity("/SecurityTestSignatureSha256WithRsa/InterestSignature", RsaKeyParams());
+
+ auto interest = makeInterest("/SecurityTestSignatureSha256WithRsa/InterestSignature/Interest1");
+ auto interest11 = makeInterest("/SecurityTestSignatureSha256WithRsa/InterestSignature/Interest1");
+
+ scheduler.scheduleEvent(100_ms, [&] {
+ m_keyChain.sign(*interest, security::SigningInfo(identity));
+ });
+
+ advanceClocks(100_ms);
+ scheduler.scheduleEvent(100_ms, [&] {
+ m_keyChain.sign(*interest11, security::SigningInfo(identity));
+ });
+
+ advanceClocks(100_ms);
+
+ Block interestBlock(interest->wireEncode().wire(), interest->wireEncode().size());
+
+ Interest interest2;
+ interest2.wireDecode(interestBlock);
+ BOOST_CHECK(verifySignature(interest2, identity.getDefaultKey()));
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestSignatureSha256WithRsa
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/signing-helpers.t.cpp b/tests/unit/security/signing-helpers.t.cpp
new file mode 100644
index 0000000..af2c568
--- /dev/null
+++ b/tests/unit/security/signing-helpers.t.cpp
@@ -0,0 +1,71 @@
+/* -*- 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 "security/signing-helpers.hpp"
+
+#include "boost-test.hpp"
+
+namespace ndn {
+namespace security {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(TestSigningHelpers)
+
+// update of this test case deferred until the new IdentityManagementFixture is available
+
+BOOST_AUTO_TEST_CASE(Identity)
+{
+ Name identity("/identity");
+ SigningInfo info = signingByIdentity(identity);
+ BOOST_CHECK_EQUAL(info.getSignerType(), SigningInfo::SIGNER_TYPE_ID);
+ BOOST_CHECK_EQUAL(info.getSignerName(), identity);
+}
+
+BOOST_AUTO_TEST_CASE(Key)
+{
+ Name key("/key");
+ SigningInfo info = signingByKey(key);
+ BOOST_CHECK_EQUAL(info.getSignerType(), SigningInfo::SIGNER_TYPE_KEY);
+ BOOST_CHECK_EQUAL(info.getSignerName(), key);
+}
+
+BOOST_AUTO_TEST_CASE(Certificate)
+{
+ Name cert("/cert");
+ SigningInfo info = signingByCertificate(cert);
+ BOOST_CHECK_EQUAL(info.getSignerType(), SigningInfo::SIGNER_TYPE_CERT);
+ BOOST_CHECK_EQUAL(info.getSignerName(), cert);
+}
+
+BOOST_AUTO_TEST_CASE(Sha256)
+{
+ SigningInfo info = signingWithSha256();
+ BOOST_CHECK_EQUAL(info.getSignerType(), SigningInfo::SIGNER_TYPE_SHA256);
+ BOOST_CHECK_EQUAL(info.getSignerName(), SigningInfo::getEmptyName());
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestSigningHelpers
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/signing-info.t.cpp b/tests/unit/security/signing-info.t.cpp
new file mode 100644
index 0000000..fc6f627
--- /dev/null
+++ b/tests/unit/security/signing-info.t.cpp
@@ -0,0 +1,250 @@
+/* -*- 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 "security/signing-info.hpp"
+
+#include "boost-test.hpp"
+
+#include <boost/lexical_cast.hpp>
+#include <sstream>
+
+namespace ndn {
+namespace security {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(TestSigningInfo)
+
+BOOST_AUTO_TEST_CASE(Basic)
+{
+ Name id("/my-identity");
+ Name key("/my-key");
+ Name cert("/my-cert");
+
+ SigningInfo info;
+
+ BOOST_CHECK_EQUAL(info.getSignerType(), SigningInfo::SIGNER_TYPE_NULL);
+ BOOST_CHECK_EQUAL(info.getSignerName(), SigningInfo::getEmptyName());
+ BOOST_CHECK_EQUAL(info.getDigestAlgorithm(), DigestAlgorithm::SHA256);
+
+ const SignatureInfo& sigInfo = info.getSignatureInfo();
+ BOOST_CHECK_EQUAL(sigInfo.getSignatureType(), -1);
+ BOOST_CHECK_EQUAL(sigInfo.hasKeyLocator(), false);
+
+ info.setSigningIdentity(id);
+ BOOST_CHECK_EQUAL(info.getSignerType(), SigningInfo::SIGNER_TYPE_ID);
+ BOOST_CHECK_EQUAL(info.getSignerName(), id);
+ BOOST_CHECK_EQUAL(info.getDigestAlgorithm(), DigestAlgorithm::SHA256);
+
+ SigningInfo infoId(SigningInfo::SIGNER_TYPE_ID, id);
+ BOOST_CHECK_EQUAL(infoId.getSignerType(), SigningInfo::SIGNER_TYPE_ID);
+ BOOST_CHECK_EQUAL(infoId.getSignerName(), id);
+ BOOST_CHECK_EQUAL(infoId.getDigestAlgorithm(), DigestAlgorithm::SHA256);
+
+ info.setSigningKeyName(key);
+ BOOST_CHECK_EQUAL(info.getSignerType(), SigningInfo::SIGNER_TYPE_KEY);
+ BOOST_CHECK_EQUAL(info.getSignerName(), key);
+ BOOST_CHECK_EQUAL(info.getDigestAlgorithm(), DigestAlgorithm::SHA256);
+
+ SigningInfo infoKey(SigningInfo::SIGNER_TYPE_KEY, key);
+ BOOST_CHECK_EQUAL(infoKey.getSignerType(), SigningInfo::SIGNER_TYPE_KEY);
+ BOOST_CHECK_EQUAL(infoKey.getSignerName(), key);
+ BOOST_CHECK_EQUAL(infoKey.getDigestAlgorithm(), DigestAlgorithm::SHA256);
+
+ info.setSigningCertName(cert);
+ BOOST_CHECK_EQUAL(info.getSignerType(), SigningInfo::SIGNER_TYPE_CERT);
+ BOOST_CHECK_EQUAL(info.getSignerName(), cert);
+ BOOST_CHECK_EQUAL(info.getDigestAlgorithm(), DigestAlgorithm::SHA256);
+
+ SigningInfo infoCert(SigningInfo::SIGNER_TYPE_CERT, cert);
+ BOOST_CHECK_EQUAL(infoCert.getSignerType(), SigningInfo::SIGNER_TYPE_CERT);
+ BOOST_CHECK_EQUAL(infoCert.getSignerName(), cert);
+ BOOST_CHECK_EQUAL(infoCert.getDigestAlgorithm(), DigestAlgorithm::SHA256);
+
+ info.setSha256Signing();
+ BOOST_CHECK_EQUAL(info.getSignerType(), SigningInfo::SIGNER_TYPE_SHA256);
+ BOOST_CHECK_EQUAL(info.getSignerName(), SigningInfo::getEmptyName());
+ BOOST_CHECK_EQUAL(info.getDigestAlgorithm(), DigestAlgorithm::SHA256);
+
+ SigningInfo infoSha(SigningInfo::SIGNER_TYPE_SHA256);
+ BOOST_CHECK_EQUAL(infoSha.getSignerType(), SigningInfo::SIGNER_TYPE_SHA256);
+ BOOST_CHECK_EQUAL(infoSha.getSignerName(), SigningInfo::getEmptyName());
+ BOOST_CHECK_EQUAL(infoSha.getDigestAlgorithm(), DigestAlgorithm::SHA256);
+}
+
+BOOST_AUTO_TEST_CASE(CustomSignatureInfo)
+{
+ SigningInfo info1;
+ BOOST_CHECK_EQUAL(info1.getSignatureInfo(), SignatureInfo());
+
+ SignatureInfo si;
+ si.setKeyLocator(Name("ndn:/test/key/locator"));
+ info1.setSignatureInfo(si);
+
+ BOOST_CHECK_EQUAL(info1.getSignatureInfo(), si);
+
+ SigningInfo info2(SigningInfo::SIGNER_TYPE_NULL, SigningInfo::getEmptyName(), si);
+ BOOST_CHECK_EQUAL(info2.getSignatureInfo(), si);
+}
+
+BOOST_AUTO_TEST_CASE(FromString)
+{
+ SigningInfo infoDefault("");
+ BOOST_CHECK_EQUAL(infoDefault.getSignerType(), SigningInfo::SIGNER_TYPE_NULL);
+ BOOST_CHECK_EQUAL(infoDefault.getSignerName(), SigningInfo::getEmptyName());
+ BOOST_CHECK_EQUAL(infoDefault.getDigestAlgorithm(), DigestAlgorithm::SHA256);
+
+ SigningInfo infoId("id:/my-identity");
+ BOOST_CHECK_EQUAL(infoId.getSignerType(), SigningInfo::SIGNER_TYPE_ID);
+ BOOST_CHECK_EQUAL(infoId.getSignerName(), "/my-identity");
+ BOOST_CHECK_EQUAL(infoId.getDigestAlgorithm(), DigestAlgorithm::SHA256);
+
+ SigningInfo infoKey("key:/my-key");
+ BOOST_CHECK_EQUAL(infoKey.getSignerType(), SigningInfo::SIGNER_TYPE_KEY);
+ BOOST_CHECK_EQUAL(infoKey.getSignerName(), "/my-key");
+ BOOST_CHECK_EQUAL(infoKey.getDigestAlgorithm(), DigestAlgorithm::SHA256);
+
+ SigningInfo infoCert("cert:/my-cert");
+ BOOST_CHECK_EQUAL(infoCert.getSignerType(), SigningInfo::SIGNER_TYPE_CERT);
+ BOOST_CHECK_EQUAL(infoCert.getSignerName(), "/my-cert");
+ BOOST_CHECK_EQUAL(infoCert.getDigestAlgorithm(), DigestAlgorithm::SHA256);
+
+ SigningInfo infoSha("id:/localhost/identity/digest-sha256");
+ BOOST_CHECK_EQUAL(infoSha.getSignerType(), SigningInfo::SIGNER_TYPE_SHA256);
+ BOOST_CHECK_EQUAL(infoSha.getSignerName(), SigningInfo::getEmptyName());
+ BOOST_CHECK_EQUAL(infoSha.getDigestAlgorithm(), DigestAlgorithm::SHA256);
+}
+
+BOOST_AUTO_TEST_CASE(ToString)
+{
+ // We can't use lexical_cast due to Boost Bug 6298.
+ std::stringstream ss;
+ ss << SigningInfo();
+ BOOST_CHECK_EQUAL(ss.str(), "");
+
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(
+ SigningInfo(SigningInfo::SIGNER_TYPE_ID, "/my-identity")), "id:/my-identity");
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(
+ SigningInfo(SigningInfo::SIGNER_TYPE_KEY, "/my-key")), "key:/my-key");
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(
+ SigningInfo(SigningInfo::SIGNER_TYPE_CERT, "/my-cert")), "cert:/my-cert");
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(
+ SigningInfo(SigningInfo::SIGNER_TYPE_SHA256)),
+ "id:/localhost/identity/digest-sha256");
+}
+
+BOOST_AUTO_TEST_CASE(Chaining)
+{
+ SigningInfo info = SigningInfo()
+ .setSigningIdentity("/identity")
+ .setSigningKeyName("/key/name")
+ .setSigningCertName("/cert/name")
+ .setPibIdentity(Identity())
+ .setPibKey(Key())
+ .setSha256Signing()
+ .setDigestAlgorithm(DigestAlgorithm::SHA256)
+ .setSignatureInfo(SignatureInfo());
+
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(info), "id:/localhost/identity/digest-sha256");
+}
+
+BOOST_AUTO_TEST_CASE(OperatorEquals)
+{
+ // Check name equality
+ SigningInfo info1("id:/my-id");
+ SigningInfo info2("id:/my-id");
+ BOOST_CHECK_EQUAL(info1, info2);
+ // Change name, check inequality
+ info2 = SigningInfo("id:/not-same-id");
+ BOOST_CHECK_NE(info1, info2);
+
+ // Check name, digest algo equality
+ info1 = SigningInfo("id:/my-id");
+ info2 = SigningInfo("id:/my-id");
+ info1.setDigestAlgorithm(DigestAlgorithm::SHA256);
+ info2.setDigestAlgorithm(DigestAlgorithm::SHA256);
+ BOOST_CHECK_EQUAL(info1, info2);
+ // Change digest algo, check inequality
+ info2.setDigestAlgorithm(DigestAlgorithm::NONE);
+ BOOST_CHECK_NE(info1, info2);
+
+ // Check name, digest algo, signature info equality
+ info1 = SigningInfo("id:/my-id");
+ info2 = SigningInfo("id:/my-id");
+ info1.setDigestAlgorithm(DigestAlgorithm::SHA256);
+ info2.setDigestAlgorithm(DigestAlgorithm::SHA256);
+ SignatureInfo sigInfo1(tlv::DigestSha256);
+ info1.setSignatureInfo(sigInfo1);
+ info2.setSignatureInfo(sigInfo1);
+ BOOST_CHECK_EQUAL(info1, info2);
+ // Change signature info, check inequality
+ SignatureInfo sigInfo2(tlv::SignatureSha256WithRsa);
+ info2.setSignatureInfo(sigInfo2);
+ BOOST_CHECK_NE(info1, info2);
+}
+
+BOOST_AUTO_TEST_CASE(OperatorEqualsDifferentTypes)
+{
+ SigningInfo info1("key:/my-id/KEY/1");
+ SigningInfo info2("key:/my-id/KEY/1");
+ // Check equality for key type
+ BOOST_CHECK_EQUAL(info1, info2);
+ info2 = SigningInfo("id:/my-id");
+ // Change signature type, check inequality
+ BOOST_CHECK_NE(info1, info2);
+ info2 = SigningInfo("key:/not-same-id/KEY/1");
+ // Change key name, check inequality
+ BOOST_CHECK_NE(info1, info2);
+
+ info1 = SigningInfo("cert:/my-id/KEY/1/self/%FD01");
+ info2 = SigningInfo("cert:/my-id/KEY/1/self/%FD01");
+ // Check equality for cert type
+ BOOST_CHECK_EQUAL(info1, info2);
+ info2 = SigningInfo("cert:/not-my-id/KEY/1/other/%FD01");
+ // Change cert name, check inequality
+ BOOST_CHECK_NE(info1, info2);
+ info2 = SigningInfo("id:/my-id");
+ // Change signature type, check inequality
+ BOOST_CHECK_NE(info1, info2);
+
+ info1 = SigningInfo(SigningInfo::SIGNER_TYPE_NULL);
+ info2 = SigningInfo(SigningInfo::SIGNER_TYPE_NULL);
+ // Check equality for null type
+ BOOST_CHECK_EQUAL(info1, info2);
+ info2 = SigningInfo("id:/my-id");
+ // Change signature type, check inequality
+ BOOST_CHECK_NE(info1, info2);
+
+ info1 = SigningInfo(SigningInfo::SIGNER_TYPE_SHA256);
+ info2 = SigningInfo(SigningInfo::SIGNER_TYPE_SHA256);
+ // Check equality for SHA256 digest type
+ BOOST_CHECK_EQUAL(info1, info2);
+ info2 = SigningInfo("id:/my-id");
+ // Change signature type, check inequality
+ BOOST_CHECK_NE(info1, info2);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestSigningInfo
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/tmp-home/.ndn/client.conf b/tests/unit/security/tmp-home/.ndn/client.conf
new file mode 100644
index 0000000..b832cfc
--- /dev/null
+++ b/tests/unit/security/tmp-home/.ndn/client.conf
@@ -0,0 +1,2 @@
+pib=pib-sqlite3:/tmp/test/ndn-cxx/keychain
+tpm=tpm-file:/tmp/test/ndn-cxx/keychain
\ No newline at end of file
diff --git a/tests/unit/security/tpm/back-end-wrapper-file.hpp b/tests/unit/security/tpm/back-end-wrapper-file.hpp
new file mode 100644
index 0000000..dabd6d3
--- /dev/null
+++ b/tests/unit/security/tpm/back-end-wrapper-file.hpp
@@ -0,0 +1,72 @@
+/* -*- 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.
+ */
+
+#ifndef NDN_TESTS_SECURITY_TPM_BACK_END_WRAPPER_FILE_HPP
+#define NDN_TESTS_SECURITY_TPM_BACK_END_WRAPPER_FILE_HPP
+
+#include "security/tpm/back-end-file.hpp"
+#include <boost/filesystem.hpp>
+
+namespace ndn {
+namespace security {
+namespace tpm {
+namespace tests {
+
+/**
+ * @brief A wrapper of tpm::BackEndFile for unit test template.
+ */
+class BackEndWrapperFile
+{
+public:
+ BackEndWrapperFile()
+ : m_tmpPath(boost::filesystem::path(UNIT_TEST_CONFIG_PATH) / "TpmFileTest")
+ , m_impl(make_unique<BackEndFile>(m_tmpPath.string()))
+ {
+ }
+
+ ~BackEndWrapperFile()
+ {
+ boost::filesystem::remove_all(m_tmpPath);
+ }
+
+ BackEnd&
+ getTpm()
+ {
+ return *m_impl;
+ }
+
+ std::string
+ getScheme() const
+ {
+ return "tpm-file";
+ }
+
+private:
+ const boost::filesystem::path m_tmpPath;
+ const unique_ptr<BackEnd> m_impl;
+};
+
+} // namespace tests
+} // namespace tpm
+} // namespace security
+} // namespace ndn
+
+#endif // NDN_TESTS_SECURITY_TPM_BACK_END_WRAPPER_FILE_HPP
diff --git a/tests/unit/security/tpm/back-end-wrapper-mem.hpp b/tests/unit/security/tpm/back-end-wrapper-mem.hpp
new file mode 100644
index 0000000..ddf629b
--- /dev/null
+++ b/tests/unit/security/tpm/back-end-wrapper-mem.hpp
@@ -0,0 +1,64 @@
+/* -*- 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.
+ */
+
+#ifndef NDN_TESTS_SECURITY_TPM_BACK_END_WRAPPER_MEM_HPP
+#define NDN_TESTS_SECURITY_TPM_BACK_END_WRAPPER_MEM_HPP
+
+#include "security/tpm/back-end-mem.hpp"
+
+namespace ndn {
+namespace security {
+namespace tpm {
+namespace tests {
+
+/**
+ * @brief A wrapper of tpm::BackEndMem for unit test template.
+ */
+class BackEndWrapperMem
+{
+public:
+ BackEndWrapperMem()
+ : m_impl(make_unique<BackEndMem>())
+ {
+ }
+
+ BackEnd&
+ getTpm()
+ {
+ return *m_impl;
+ }
+
+ std::string
+ getScheme() const
+ {
+ return "tpm-memory";
+ }
+
+private:
+ const unique_ptr<BackEnd> m_impl;
+};
+
+} // namespace tests
+} // namespace tpm
+} // namespace security
+} // namespace ndn
+
+#endif // NDN_TESTS_SECURITY_TPM_BACK_END_WRAPPER_MEM_HPP
diff --git a/tests/unit/security/tpm/back-end-wrapper-osx.hpp b/tests/unit/security/tpm/back-end-wrapper-osx.hpp
new file mode 100644
index 0000000..6d7d35b
--- /dev/null
+++ b/tests/unit/security/tpm/back-end-wrapper-osx.hpp
@@ -0,0 +1,88 @@
+/* -*- 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.
+ */
+
+#ifndef NDN_TESTS_SECURITY_TPM_BACK_END_WRAPPER_OSX_HPP
+#define NDN_TESTS_SECURITY_TPM_BACK_END_WRAPPER_OSX_HPP
+
+#include "security/tpm/back-end-osx.hpp"
+#include "security/tpm/key-handle-osx.hpp"
+
+#include <cstdlib>
+
+namespace ndn {
+namespace security {
+namespace tpm {
+namespace tests {
+
+/**
+ * @brief A wrapper of tpm::BackEndOsx for unit test template.
+ */
+class BackEndWrapperOsx
+{
+public:
+ BackEndWrapperOsx()
+ {
+ std::string oldHOME;
+ if (std::getenv("OLD_HOME"))
+ oldHOME = std::getenv("OLD_HOME");
+
+ if (std::getenv("HOME"))
+ m_HOME = std::getenv("HOME");
+
+ if (!oldHOME.empty())
+ setenv("HOME", oldHOME.data(), 1);
+ else
+ unsetenv("HOME");
+
+ m_impl = make_unique<BackEndOsx>();
+ }
+
+ ~BackEndWrapperOsx()
+ {
+ if (!m_HOME.empty())
+ setenv("HOME", m_HOME.data(), 1);
+ else
+ unsetenv("HOME");
+ }
+
+ BackEnd&
+ getTpm()
+ {
+ return *m_impl;
+ }
+
+ std::string
+ getScheme() const
+ {
+ return "tpm-osxkeychain";
+ }
+
+private:
+ std::string m_HOME;
+ unique_ptr<BackEnd> m_impl;
+};
+
+} // namespace tests
+} // namespace tpm
+} // namespace security
+} // namespace ndn
+
+#endif // NDN_TESTS_SECURITY_TPM_BACK_END_WRAPPER_OSX_HPP
diff --git a/tests/unit/security/tpm/back-end.t.cpp b/tests/unit/security/tpm/back-end.t.cpp
new file mode 100644
index 0000000..5512ebd
--- /dev/null
+++ b/tests/unit/security/tpm/back-end.t.cpp
@@ -0,0 +1,279 @@
+/* -*- 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 "security/tpm/back-end.hpp"
+
+#include "encoding/buffer-stream.hpp"
+#include "security/pib/key.hpp"
+#include "security/tpm/key-handle.hpp"
+#include "security/tpm/tpm.hpp"
+#include "security/transform/bool-sink.hpp"
+#include "security/transform/buffer-source.hpp"
+#include "security/transform/private-key.hpp"
+#include "security/transform/public-key.hpp"
+#include "security/transform/verifier-filter.hpp"
+
+#include "back-end-wrapper-file.hpp"
+#include "back-end-wrapper-mem.hpp"
+#ifdef NDN_CXX_HAVE_OSX_FRAMEWORKS
+#include "back-end-wrapper-osx.hpp"
+#endif // NDN_CXX_HAVE_OSX_FRAMEWORKS
+
+#include "boost-test.hpp"
+
+#include <boost/mpl/vector.hpp>
+#include <set>
+
+namespace ndn {
+namespace security {
+namespace tpm {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(Tpm)
+BOOST_AUTO_TEST_SUITE(TestBackEnd)
+
+using tpm::Tpm;
+
+using TestBackEnds = boost::mpl::vector<
+#ifdef NDN_CXX_HAVE_OSX_FRAMEWORKS
+ BackEndWrapperOsx,
+#endif // NDN_CXX_HAVE_OSX_FRAMEWORKS
+ BackEndWrapperMem,
+ BackEndWrapperFile>;
+
+BOOST_AUTO_TEST_CASE_TEMPLATE(KeyManagement, T, TestBackEnds)
+{
+ T wrapper;
+ BackEnd& tpm = wrapper.getTpm();
+
+ Name identity("/Test/KeyName");
+ name::Component keyId("1");
+ Name keyName = v2::constructKeyName(identity, keyId);
+
+ // key should not exist
+ BOOST_CHECK_EQUAL(tpm.hasKey(keyName), false);
+ BOOST_CHECK(tpm.getKeyHandle(keyName) == nullptr);
+
+ // create key, should exist
+ BOOST_CHECK(tpm.createKey(identity, RsaKeyParams(keyId)) != nullptr);
+ BOOST_CHECK(tpm.hasKey(keyName));
+ BOOST_CHECK(tpm.getKeyHandle(keyName) != nullptr);
+
+ // create a key with the same name, should throw error
+ BOOST_CHECK_THROW(tpm.createKey(identity, RsaKeyParams(keyId)), Tpm::Error);
+
+ // delete key, should not exist
+ tpm.deleteKey(keyName);
+ BOOST_CHECK_EQUAL(tpm.hasKey(keyName), false);
+ BOOST_CHECK(tpm.getKeyHandle(keyName) == nullptr);
+}
+
+BOOST_AUTO_TEST_CASE_TEMPLATE(RsaSigning, T, TestBackEnds)
+{
+ T wrapper;
+ BackEnd& tpm = wrapper.getTpm();
+
+ // create an RSA key
+ Name identity("/Test/KeyName");
+ unique_ptr<KeyHandle> key = tpm.createKey(identity, RsaKeyParams());
+ Name keyName = key->getKeyName();
+
+ const uint8_t content[] = {0x01, 0x02, 0x03, 0x04};
+ Block sigBlock(tlv::SignatureValue, key->sign(DigestAlgorithm::SHA256, content, sizeof(content)));
+
+ transform::PublicKey pubKey;
+ ConstBufferPtr pubKeyBits = key->derivePublicKey();
+ pubKey.loadPkcs8(pubKeyBits->data(), pubKeyBits->size());
+
+ bool result;
+ {
+ using namespace transform;
+ bufferSource(content, sizeof(content)) >>
+ verifierFilter(DigestAlgorithm::SHA256, pubKey, sigBlock.value(), sigBlock.value_size()) >>
+ boolSink(result);
+ }
+ BOOST_CHECK_EQUAL(result, true);
+
+ tpm.deleteKey(keyName);
+ BOOST_CHECK_EQUAL(tpm.hasKey(keyName), false);
+}
+
+BOOST_AUTO_TEST_CASE_TEMPLATE(RsaDecryption, T, TestBackEnds)
+{
+ T wrapper;
+ BackEnd& tpm = wrapper.getTpm();
+
+ // create an RSA key
+ Name identity("/Test/KeyName");
+ unique_ptr<KeyHandle> key = tpm.createKey(identity, RsaKeyParams());
+ Name keyName = key->getKeyName();
+
+ const uint8_t content[] = {0x01, 0x02, 0x03, 0x04};
+
+ transform::PublicKey pubKey;
+ ConstBufferPtr pubKeyBits = key->derivePublicKey();
+ pubKey.loadPkcs8(pubKeyBits->data(), pubKeyBits->size());
+
+ ConstBufferPtr cipherText = pubKey.encrypt(content, sizeof(content));
+ ConstBufferPtr plainText = key->decrypt(cipherText->data(), cipherText->size());
+
+ BOOST_CHECK_EQUAL_COLLECTIONS(content, content + sizeof(content),
+ plainText->begin(), plainText->end());
+
+ tpm.deleteKey(keyName);
+ BOOST_CHECK_EQUAL(tpm.hasKey(keyName), false);
+}
+
+BOOST_AUTO_TEST_CASE_TEMPLATE(EcdsaSigning, T, TestBackEnds)
+{
+ T wrapper;
+ BackEnd& tpm = wrapper.getTpm();
+
+ // create an EC key
+ Name identity("/Test/Ec/KeyName");
+ unique_ptr<KeyHandle> key = tpm.createKey(identity, EcKeyParams());
+ Name ecKeyName = key->getKeyName();
+
+ const uint8_t content[] = {0x01, 0x02, 0x03, 0x04};
+ Block sigBlock(tlv::SignatureValue, key->sign(DigestAlgorithm::SHA256, content, sizeof(content)));
+
+ transform::PublicKey pubKey;
+ ConstBufferPtr pubKeyBits = key->derivePublicKey();
+ pubKey.loadPkcs8(pubKeyBits->data(), pubKeyBits->size());
+
+ bool result;
+ {
+ using namespace transform;
+ bufferSource(content, sizeof(content)) >> verifierFilter(DigestAlgorithm::SHA256, pubKey,
+ sigBlock.value(), sigBlock.value_size())
+ >> boolSink(result);
+ }
+ BOOST_CHECK_EQUAL(result, true);
+
+ tpm.deleteKey(ecKeyName);
+ BOOST_CHECK_EQUAL(tpm.hasKey(ecKeyName), false);
+}
+
+BOOST_AUTO_TEST_CASE_TEMPLATE(ImportExport, T, TestBackEnds)
+{
+ const std::string privKeyPkcs1 =
+ "MIIEpAIBAAKCAQEAw0WM1/WhAxyLtEqsiAJgWDZWuzkYpeYVdeeZcqRZzzfRgBQT\n"
+ "sNozS5t4HnwTZhwwXbH7k3QN0kRTV826Xobws3iigohnM9yTK+KKiayPhIAm/+5H\n"
+ "GT6SgFJhYhqo1/upWdueojil6RP4/AgavHhopxlAVbk6G9VdVnlQcQ5Zv0OcGi73\n"
+ "c+EnYD/YgURYGSngUi/Ynsh779p2U69/te9gZwIL5PuE9BiO6I39cL9z7EK1SfZh\n"
+ "OWvDe/qH7YhD/BHwcWit8FjRww1glwRVTJsA9rH58ynaAix0tcR/nBMRLUX+e3rU\n"
+ "RHg6UbSjJbdb9qmKM1fTGHKUzL/5pMG6uBU0ywIDAQABAoIBADQkckOIl4IZMUTn\n"
+ "W8LFv6xOdkJwMKC8G6bsPRFbyY+HvC2TLt7epSvfS+f4AcYWaOPcDu2E49vt2sNr\n"
+ "cASly8hgwiRRAB3dHH9vcsboiTo8bi2RFvMqvjv9w3tK2yMxVDtmZamzrrnaV3YV\n"
+ "Q+5nyKo2F/PMDjQ4eUAKDOzjhBuKHsZBTFnA1MFNI+UKj5X4Yp64DFmKlxTX/U2b\n"
+ "wzVywo5hzx2Uhw51jmoLls4YUvMJXD0wW5ZtYRuPogXvXb/of9ef/20/wU11WFKg\n"
+ "Xb4gfR8zUXaXS1sXcnVm3+24vIs9dApUwykuoyjOqxWqcHRec2QT2FxVGkFEraze\n"
+ "CPa4rMECgYEA5Y8CywomIcTgerFGFCeMHJr8nQGqY2V/owFb3k9maczPnC9p4a9R\n"
+ "c5szLxA9FMYFxurQZMBWSEG2JS1HR2mnjigx8UKjYML/A+rvvjZOMe4M6Sy2ggh4\n"
+ "SkLZKpWTzjTe07ByM/j5v/SjNZhWAG7sw4/LmPGRQkwJv+KZhGojuOkCgYEA2cOF\n"
+ "T6cJRv6kvzTz9S0COZOVm+euJh/BXp7oAsAmbNfOpckPMzqHXy8/wpdKl6AAcB57\n"
+ "OuztlNfV1D7qvbz7JuRlYwQ0cEfBgbZPcz1p18HHDXhwn57ZPb8G33Yh9Omg0HNA\n"
+ "Imb4LsVuSqxA6NwSj7cpRekgTedrhLFPJ+Ydb5MCgYEAsM3Q7OjILcIg0t6uht9e\n"
+ "vrlwTsz1mtCV2co2I6crzdj9HeI2vqf1KAElDt6G7PUHhglcr/yjd8uEqmWRPKNX\n"
+ "ddnnfVZB10jYeP/93pac6z/Zmc3iU4yKeUe7U10ZFf0KkiiYDQd59CpLef/2XScS\n"
+ "HB0oRofnxRQjfjLc4muNT+ECgYEAlcDk06MOOTly+F8lCc1bA1dgAmgwFd2usDBd\n"
+ "Y07a3e0HGnGLN3Kfl7C5i0tZq64HvxLnMd2vgLVxQlXGPpdQrC1TH+XLXg+qnlZO\n"
+ "ivSH7i0/gx75bHvj75eH1XK65V8pDVDEoSPottllAIs21CxLw3N1ObOZWJm2EfmR\n"
+ "cuHICmsCgYAtFJ1idqMoHxES3mlRpf2JxyQudP3SCm2WpGmqVzhRYInqeatY5sUd\n"
+ "lPLHm/p77RT7EyxQHTlwn8FJPuM/4ZH1rQd/vB+Y8qAtYJCexDMsbvLW+Js+VOvk\n"
+ "jweEC0nrcL31j9mF0vz5E6tfRu4hhJ6L4yfWs0gSejskeVB/w8QY4g==\n";
+ const std::string password("password");
+ const std::string wrongPassword("wrong");
+
+ T wrapper;
+ BackEnd& tpm = wrapper.getTpm();
+
+ Name keyName("/Test/KeyName/KEY/1");
+ tpm.deleteKey(keyName);
+ BOOST_REQUIRE_EQUAL(tpm.hasKey(keyName), false);
+
+ transform::PrivateKey sKey;
+ sKey.loadPkcs1Base64(reinterpret_cast<const uint8_t*>(privKeyPkcs1.data()), privKeyPkcs1.size());
+ OBufferStream os;
+ sKey.savePkcs8(os, password.data(), password.size());
+ auto pkcs8 = os.buf();
+
+ // import with wrong password
+ BOOST_CHECK_THROW(tpm.importKey(keyName, pkcs8->data(), pkcs8->size(), wrongPassword.data(), wrongPassword.size()),
+ BackEnd::Error);
+ BOOST_CHECK_EQUAL(tpm.hasKey(keyName), false);
+
+ // import with correct password
+ tpm.importKey(keyName, pkcs8->data(), pkcs8->size(), password.data(), password.size());
+ BOOST_CHECK_EQUAL(tpm.hasKey(keyName), true);
+
+ // import already present key
+ BOOST_CHECK_THROW(tpm.importKey(keyName, pkcs8->data(), pkcs8->size(), password.data(), password.size()),
+ BackEnd::Error);
+
+ // test derivePublicKey with the imported key
+ auto keyHdl = tpm.getKeyHandle(keyName);
+ auto pubKey = keyHdl->derivePublicKey();
+ BOOST_CHECK(pubKey != nullptr);
+
+ // export
+ auto exportedKey = tpm.exportKey(keyName, password.data(), password.size());
+ BOOST_CHECK_EQUAL(tpm.hasKey(keyName), true);
+
+ transform::PrivateKey sKey2;
+ sKey2.loadPkcs8(exportedKey->data(), exportedKey->size(), password.data(), password.size());
+ OBufferStream os2;
+ sKey.savePkcs1Base64(os2);
+ auto pkcs1 = os2.buf();
+
+ // verify that the exported key is identical to the key that was imported
+ BOOST_CHECK_EQUAL_COLLECTIONS(privKeyPkcs1.begin(), privKeyPkcs1.end(),
+ pkcs1->begin(), pkcs1->end());
+
+ // export nonexistent key
+ tpm.deleteKey(keyName);
+ BOOST_CHECK_EQUAL(tpm.hasKey(keyName), false);
+ BOOST_CHECK_THROW(tpm.exportKey(keyName, password.data(), password.size()), BackEnd::Error);
+}
+
+BOOST_AUTO_TEST_CASE(RandomKeyId)
+{
+ BackEndWrapperMem wrapper;
+ BackEnd& tpm = wrapper.getTpm();
+ Name identity("/Test/KeyName");
+
+ std::set<Name> keyNames;
+ for (int i = 0; i < 100; i++) {
+ auto key = tpm.createKey(identity, RsaKeyParams());
+ Name keyName = key->getKeyName();
+ BOOST_CHECK(keyNames.insert(keyName).second);
+ }
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestBackEnd
+BOOST_AUTO_TEST_SUITE_END() // Tpm
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace tpm
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/transform.t.cpp b/tests/unit/security/transform.t.cpp
new file mode 100644
index 0000000..1d55464
--- /dev/null
+++ b/tests/unit/security/transform.t.cpp
@@ -0,0 +1,86 @@
+/* -*- 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 "security/transform.hpp"
+
+#include "boost-test.hpp"
+
+namespace ndn {
+namespace security {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(TestTransform)
+
+BOOST_AUTO_TEST_CASE(SymbolVisibility)
+{
+ transform::BufferSource* bufferSource = nullptr;
+ BOOST_CHECK(bufferSource == nullptr);
+
+ transform::StreamSource* streamSource = nullptr;
+ BOOST_CHECK(streamSource == nullptr);
+
+ transform::StepSource* stepSource = nullptr;
+ BOOST_CHECK(stepSource == nullptr);
+
+ transform::BoolSink* boolSink = nullptr;
+ BOOST_CHECK(boolSink == nullptr);
+
+ transform::StreamSink* streamSink = nullptr;
+ BOOST_CHECK(streamSink == nullptr);
+
+ transform::HexEncode* hexEncode = nullptr;
+ BOOST_CHECK(hexEncode == nullptr);
+
+ transform::StripSpace* stripSpace = nullptr;
+ BOOST_CHECK(stripSpace == nullptr);
+
+ transform::HexDecode* hexDecode = nullptr;
+ BOOST_CHECK(hexDecode == nullptr);
+
+ transform::Base64Encode* base64Encode = nullptr;
+ BOOST_CHECK(base64Encode == nullptr);
+
+ transform::Base64Decode* base64Decode = nullptr;
+ BOOST_CHECK(base64Decode == nullptr);
+
+ transform::DigestFilter* digestFilter = nullptr;
+ BOOST_CHECK(digestFilter == nullptr);
+
+ transform::HmacFilter* hmacFilter = nullptr;
+ BOOST_CHECK(hmacFilter == nullptr);
+
+ transform::BlockCipher* blockCipher = nullptr;
+ BOOST_CHECK(blockCipher == nullptr);
+
+ transform::SignerFilter* signerFilter = nullptr;
+ BOOST_CHECK(signerFilter == nullptr);
+
+ transform::VerifierFilter* verifierFilter = nullptr;
+ BOOST_CHECK(verifierFilter == nullptr);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestTransform
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/transform/base64-decode.t.cpp b/tests/unit/security/transform/base64-decode.t.cpp
new file mode 100644
index 0000000..ecaa17c
--- /dev/null
+++ b/tests/unit/security/transform/base64-decode.t.cpp
@@ -0,0 +1,181 @@
+/* -*- 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 "security/transform/base64-decode.hpp"
+#include "security/transform/buffer-source.hpp"
+#include "security/transform/step-source.hpp"
+#include "security/transform/stream-sink.hpp"
+#include "encoding/buffer-stream.hpp"
+
+#include "boost-test.hpp"
+
+namespace ndn {
+namespace security {
+namespace transform {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(Transform)
+BOOST_AUTO_TEST_SUITE(TestBase64Decode)
+
+BOOST_AUTO_TEST_CASE(Basic)
+{
+ std::string in =
+ "AAECAwQFBgcICQoLDA0ODwABAgMEBQYHCAkKCwwNDg8AAQIDBAUGBwgJCgsMDQ4P\n"
+ "AAECAwQFBgcICQoLDA0ODwABAgMEBQYHCAkKCwwNDg8AAQIDBAUGBwgJCgsMDQ4P\n"
+ "AAECAwQFBgcICQoLDA0ODwABAgMEBQYHCAkKCwwNDg8=\n";
+
+ uint8_t out[] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f
+ };
+
+ OBufferStream os;
+ bufferSource(in) >> base64Decode() >> streamSink(os);
+
+ ConstBufferPtr buf1 = os.buf();
+ BOOST_CHECK_EQUAL_COLLECTIONS(out, out + sizeof(out), buf1->begin(), buf1->end());
+}
+
+BOOST_AUTO_TEST_CASE(NoNewLine)
+{
+ std::string in =
+ "AAECAwQFBgcICQoLDA0ODwABAgMEBQYHCAkKCwwNDg8AAQIDBAUGBwgJCgsMDQ4P"
+ "AAECAwQFBgcICQoLDA0ODwABAgMEBQYHCAkKCwwNDg8AAQIDBAUGBwgJCgsMDQ4P"
+ "AAECAwQFBgcICQoLDA0ODwABAgMEBQYHCAkKCwwNDg8=";
+
+ uint8_t out[] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f
+ };
+
+ OBufferStream os;
+ bufferSource(in) >> base64Decode(false) >> streamSink(os);
+
+ ConstBufferPtr buf1 = os.buf();
+ BOOST_CHECK_EQUAL_COLLECTIONS(out, out + sizeof(out), buf1->begin(), buf1->end());
+}
+
+BOOST_AUTO_TEST_CASE(StepByStep)
+{
+ std::string in =
+ "AAECAwQFBgcICQoLDA0ODwABAgMEBQYHCAkKCwwNDg8AAQIDBAUGBwgJCgsMDQ4P\n"
+ "AAECAwQFBgcICQoLDA0ODwABAgMEBQYHCAkKCwwNDg8AAQIDBAUGBwgJCgsMDQ4P\n"
+ "AAECAwQFBgcICQoLDA0ODwABAgMEBQYHCAkKCwwNDg8AAQIDBAUGBwgJCgsMDQ4P\n"
+ "AAECAwQFBgcICQoLDA0ODwABAgMEBQYHCAkKCwwNDg8AAQIDBAUGBwgJCgsMDQ4P\n"
+ "AAECAwQFBgcICQoLDA0ODwABAgMEBQYHCAkKCwwNDg8AAQIDBAUGBwgJCgsMDQ4P\n"
+ "AAECAwQFBgcICQoLDA0ODwABAgMEBQYHCAkKCwwNDg8AAQIDBAUGBwgJCgsMDQ4P\n"
+ "AAECAwQFBgcICQoLDA0ODwABAgMEBQYHCAkKCwwNDg8AAQIDBAUGBwgJCgsMDQ4P\n"
+ "AAECAwQFBgcICQoLDA0ODwABAgMEBQYHCAkKCwwNDg8AAQIDBAUGBwgJCgsMDQ4P\n";
+
+ uint8_t out[] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f
+ };
+
+ const uint8_t* input = reinterpret_cast<const uint8_t*>(in.data());
+ OBufferStream os;
+ StepSource source;
+ source >> base64Decode() >> streamSink(os);
+ source.write(input, 65); // complete line with "\n"
+ source.write(input + 65, 64); // complete line without "\n"
+ source.write(input + 129, 1); // single "\n"
+ source.write(input + 130, 35); // front of a line
+ source.write(input + 165, 30); // end of a line with "\n"
+ source.write(input + 195, 25); // front of a line
+ source.write(input + 220, 20); // middle of a line
+ source.write(input + 240, 19); // end of a line without "\n"
+ source.write(input + 259, 101); // "\n" plus one and half line
+ source.write(input + 360, 65); // end of a line plus front of another line
+ source.write(input + 425, 95); // remaining
+ source.end();
+
+ ConstBufferPtr buf1 = os.buf();
+ BOOST_CHECK_EQUAL_COLLECTIONS(out, out + sizeof(out), buf1->begin(), buf1->end());
+}
+
+BOOST_AUTO_TEST_CASE(EmptyInput)
+{
+ OBufferStream os;
+ StepSource source;
+ source >> base64Decode() >> streamSink(os);
+ source.end();
+ BOOST_CHECK_EQUAL(os.buf()->size(), 0);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestBase64Decode
+BOOST_AUTO_TEST_SUITE_END() // Transform
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace transform
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/transform/base64-encode.t.cpp b/tests/unit/security/transform/base64-encode.t.cpp
new file mode 100644
index 0000000..d52ac0b
--- /dev/null
+++ b/tests/unit/security/transform/base64-encode.t.cpp
@@ -0,0 +1,178 @@
+/* -*- 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 "security/transform/base64-encode.hpp"
+#include "security/transform/buffer-source.hpp"
+#include "security/transform/step-source.hpp"
+#include "security/transform/stream-sink.hpp"
+#include "encoding/buffer-stream.hpp"
+
+#include "boost-test.hpp"
+
+namespace ndn {
+namespace security {
+namespace transform {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(Transform)
+BOOST_AUTO_TEST_SUITE(TestBase64Encode)
+
+BOOST_AUTO_TEST_CASE(Basic)
+{
+ uint8_t in[] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f
+ };
+
+ std::string out =
+ "AAECAwQFBgcICQoLDA0ODwABAgMEBQYHCAkKCwwNDg8AAQIDBAUGBwgJCgsMDQ4P\n"
+ "AAECAwQFBgcICQoLDA0ODwABAgMEBQYHCAkKCwwNDg8AAQIDBAUGBwgJCgsMDQ4P\n"
+ "AAECAwQFBgcICQoLDA0ODwABAgMEBQYHCAkKCwwNDg8=\n";
+
+ OBufferStream os;
+ bufferSource(in, sizeof(in)) >> base64Encode() >> streamSink(os);
+
+ ConstBufferPtr buf1 = os.buf();
+ BOOST_CHECK_EQUAL_COLLECTIONS(out.begin(), out.end(), buf1->begin(), buf1->end());
+}
+
+BOOST_AUTO_TEST_CASE(NoNewLine)
+{
+ uint8_t in[] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f
+ };
+
+ std::string out =
+ "AAECAwQFBgcICQoLDA0ODwABAgMEBQYHCAkKCwwNDg8AAQIDBAUGBwgJCgsMDQ4P"
+ "AAECAwQFBgcICQoLDA0ODwABAgMEBQYHCAkKCwwNDg8AAQIDBAUGBwgJCgsMDQ4P"
+ "AAECAwQFBgcICQoLDA0ODwABAgMEBQYHCAkKCwwNDg8=";
+
+ OBufferStream os;
+ BufferSource(in, sizeof(in)) >> base64Encode(false) >> streamSink(os);
+
+ ConstBufferPtr buf1 = os.buf();
+ BOOST_CHECK_EQUAL_COLLECTIONS(out.begin(), out.end(), buf1->begin(), buf1->end());
+}
+
+BOOST_AUTO_TEST_CASE(StepByStep)
+{
+ uint8_t in[] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f
+ };
+
+ std::string out =
+ "AAECAwQFBgcICQoLDA0ODwABAgMEBQYHCAkKCwwNDg8AAQIDBAUGBwgJCgsMDQ4P\n"
+ "AAECAwQFBgcICQoLDA0ODwABAgMEBQYHCAkKCwwNDg8AAQIDBAUGBwgJCgsMDQ4P\n"
+ "AAECAwQFBgcICQoLDA0ODwABAgMEBQYHCAkKCwwNDg8AAQIDBAUGBwgJCgsMDQ4P\n"
+ "AAECAwQFBgcICQoLDA0ODwABAgMEBQYHCAkKCwwNDg8AAQIDBAUGBwgJCgsMDQ4P\n"
+ "AAECAwQFBgcICQoLDA0ODwABAgMEBQYHCAkKCwwNDg8AAQIDBAUGBwgJCgsMDQ4P\n"
+ "AAECAwQFBgcICQoLDA0ODwABAgMEBQYHCAkKCwwNDg8AAQIDBAUGBwgJCgsMDQ4P\n"
+ "AAECAwQFBgcICQoLDA0ODwABAgMEBQYHCAkKCwwNDg8AAQIDBAUGBwgJCgsMDQ4P\n"
+ "AAECAwQFBgcICQoLDA0ODwABAgMEBQYHCAkKCwwNDg8AAQIDBAUGBwgJCgsMDQ4P\n";
+
+ OBufferStream os;
+ StepSource source;
+ source >> base64Encode() >> streamSink(os);
+ source.write(in, 64); // complete chunk
+ source.write(in + 64, 32); // first half of a chunk
+ source.write(in + 96, 32); // second half of a chunk
+ source.write(in + 128, 24); // front of a chunk
+ source.write(in + 152, 20); // middle of a chunk
+ source.write(in + 172, 20); // end of a chunk
+ source.write(in + 192, 63); // odd number of bytes
+ source.write(in + 255, 85); // one and half chunk
+ source.write(in + 340, 44); // remaining part
+ source.end();
+
+ ConstBufferPtr buf1 = os.buf();
+ BOOST_CHECK_EQUAL_COLLECTIONS(out.begin(), out.end(), buf1->begin(), buf1->end());
+}
+
+BOOST_AUTO_TEST_CASE(EmptyInput)
+{
+ OBufferStream os;
+ StepSource source;
+ source >> base64Encode() >> streamSink(os);
+ source.end();
+ BOOST_CHECK_EQUAL(os.buf()->size(), 0);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestBase64Encode
+BOOST_AUTO_TEST_SUITE_END() // Transform
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace transform
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/transform/block-cipher.t.cpp b/tests/unit/security/transform/block-cipher.t.cpp
new file mode 100644
index 0000000..379093d
--- /dev/null
+++ b/tests/unit/security/transform/block-cipher.t.cpp
@@ -0,0 +1,112 @@
+/* -*- 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 "security/transform/block-cipher.hpp"
+
+#include "encoding/buffer-stream.hpp"
+#include "security/transform/buffer-source.hpp"
+#include "security/transform/stream-sink.hpp"
+
+#include "boost-test.hpp"
+
+namespace ndn {
+namespace security {
+namespace transform {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(Transform)
+BOOST_AUTO_TEST_SUITE(TestBlockCipher)
+
+BOOST_AUTO_TEST_CASE(AesCbc)
+{
+ const uint8_t key[] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f
+ };
+ const uint8_t iv[] = {
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07
+ };
+ const uint8_t plainText[] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f
+ };
+ //
+ // You can use the following shell one-liner to calculate the ciphertext:
+ // echo ${plaintext} | xxd -p -r | openssl enc -aes-128-cbc -K ${key} -iv ${iv} | xxd -i
+ //
+ const uint8_t cipherText[] = {
+ 0x07, 0x4d, 0x32, 0x68, 0xc3, 0x40, 0x64, 0x43,
+ 0x1e, 0x66, 0x4c, 0x25, 0x66, 0x42, 0x0f, 0x59,
+ 0x0a, 0x51, 0x19, 0x07, 0x67, 0x5c, 0x0e, 0xfa,
+ 0xa6, 0x8c, 0xbb, 0xaf, 0xfd, 0xea, 0x47, 0xd4,
+ 0xc7, 0x2c, 0x12, 0x34, 0x79, 0xde, 0xec, 0xc8,
+ 0x75, 0x33, 0x8f, 0x6b, 0xd6, 0x55, 0xf3, 0xfa
+ };
+
+ // encrypt
+ OBufferStream os;
+ bufferSource(plainText, sizeof(plainText)) >>
+ blockCipher(BlockCipherAlgorithm::AES_CBC, CipherOperator::ENCRYPT,
+ key, sizeof(key), iv, sizeof(iv)) >> streamSink(os);
+
+ auto buf = os.buf();
+ BOOST_CHECK_EQUAL_COLLECTIONS(cipherText, cipherText + sizeof(cipherText),
+ buf->begin(), buf->end());
+
+ // decrypt
+ OBufferStream os2;
+ bufferSource(cipherText, sizeof(cipherText)) >>
+ blockCipher(BlockCipherAlgorithm::AES_CBC, CipherOperator::DECRYPT,
+ key, sizeof(key), iv, sizeof(iv)) >> streamSink(os2);
+
+ auto buf2 = os2.buf();
+ BOOST_CHECK_EQUAL_COLLECTIONS(plainText, plainText + sizeof(plainText),
+ buf2->begin(), buf2->end());
+
+ // invalid key length
+ const uint8_t badKey[] = {0x00, 0x01, 0x02, 0x03};
+ BOOST_CHECK_THROW(BlockCipher(BlockCipherAlgorithm::AES_CBC, CipherOperator::ENCRYPT,
+ badKey, sizeof(badKey), iv, sizeof(iv)), Error);
+
+ // wrong iv length
+ const uint8_t badIv[] = {0x00, 0x01, 0x02, 0x03};
+ BOOST_CHECK_THROW(BlockCipher(BlockCipherAlgorithm::AES_CBC, CipherOperator::ENCRYPT,
+ key, sizeof(key), badIv, sizeof(badIv)), Error);
+}
+
+BOOST_AUTO_TEST_CASE(InvalidAlgorithm)
+{
+ BOOST_CHECK_THROW(BlockCipher(BlockCipherAlgorithm::NONE, CipherOperator::ENCRYPT,
+ nullptr, 0, nullptr, 0), Error);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestBlockCipher
+BOOST_AUTO_TEST_SUITE_END() // Transform
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace transform
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/transform/bool-sink.t.cpp b/tests/unit/security/transform/bool-sink.t.cpp
new file mode 100644
index 0000000..2ce633f
--- /dev/null
+++ b/tests/unit/security/transform/bool-sink.t.cpp
@@ -0,0 +1,63 @@
+/* -*- 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 "security/transform/bool-sink.hpp"
+
+#include "boost-test.hpp"
+
+namespace ndn {
+namespace security {
+namespace transform {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(Transform)
+BOOST_AUTO_TEST_SUITE(TestBoolSink)
+
+BOOST_AUTO_TEST_CASE(Basic)
+{
+ uint8_t in1[] = {0x00, 0x01};
+ bool value1 = true;
+ BoolSink sink1(value1);
+ BOOST_CHECK_EQUAL(sink1.write(in1, 1), 1);
+ BOOST_CHECK_EQUAL(sink1.write(in1 + 1, 1), 1);
+ sink1.end();
+ BOOST_CHECK_EQUAL(value1, false);
+ BOOST_CHECK_THROW(sink1.write(in1 + 1, 1), transform::Error);
+
+ uint8_t in2[] = {0x01, 0x00};
+ bool value2 = false;
+ BoolSink sink2(value2);
+ BOOST_CHECK_EQUAL(sink2.write(in2, 1), 1);
+ BOOST_CHECK_EQUAL(sink2.write(in2 + 1, 1), 1);
+ sink2.end();
+ BOOST_CHECK_EQUAL(value2, true);
+ BOOST_CHECK_THROW(sink2.write(in2 + 1, 1), transform::Error);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestBoolSink
+BOOST_AUTO_TEST_SUITE_END() // Transform
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace transform
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/transform/buffer-source.t.cpp b/tests/unit/security/transform/buffer-source.t.cpp
new file mode 100644
index 0000000..cb7e7f4
--- /dev/null
+++ b/tests/unit/security/transform/buffer-source.t.cpp
@@ -0,0 +1,89 @@
+/* -*- 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 "security/transform/buffer-source.hpp"
+#include "security/transform/stream-sink.hpp"
+
+#include "boost-test.hpp"
+
+namespace ndn {
+namespace security {
+namespace transform {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(Transform)
+BOOST_AUTO_TEST_SUITE(TestBufferSource)
+
+BOOST_AUTO_TEST_CASE(Basic)
+{
+ uint8_t in[16] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f
+ };
+
+ std::ostringstream os1;
+ bufferSource(in, sizeof(in)) >> streamSink(os1);
+ std::string out1 = os1.str();
+ BOOST_CHECK_EQUAL_COLLECTIONS(in, in + sizeof(in), out1.begin(), out1.end());
+
+ std::string in2 =
+ "0123456701234567012345670123456701234567012345670123456701234567"
+ "0123456701234567012345670123456701234567012345670123456701234567"
+ "0123456701234567012345670123456701234567012345670123456701234567"
+ "0123456701234567012345670123456701234567012345670123456701234567"
+ "0123456701234567012345670123456701234567012345670123456701234567"
+ "0123456701234567012345670123456701234567012345670123456701234567"
+ "0123456701234567012345670123456701234567012345670123456701234567"
+ "0123456701234567012345670123456701234567012345670123456701234567"
+ "0123456701234567012345670123456701234567012345670123456701234567"
+ "0123456701234567012345670123456701234567012345670123456701234567"
+ "0123456701234567012345670123456701234567012345670123456701234567"
+ "0123456701234567012345670123456701234567012345670123456701234567"
+ "0123456701234567012345670123456701234567012345670123456701234567"
+ "0123456701234567012345670123456701234567012345670123456701234567"
+ "0123456701234567012345670123456701234567012345670123456701234567"
+ "0123456701234567012345670123456701234567012345670123456701234567"
+ "0123456701234567012345670123456701234567012345670123456701234567"
+ "0123456701234567012345670123456701234567012345670123456701234567"
+ "0123456701234567012345670123456701234567012345670123456701234567"
+ "0123456701234567012345670123456701234567012345670123456701234567";
+
+ std::ostringstream os2;
+ bufferSource(in2) >> streamSink(os2);
+ std::string out2 = os2.str();
+ BOOST_CHECK_EQUAL_COLLECTIONS(in2.begin(), in2.end(), out2.begin(), out2.end());
+
+ Buffer in3(in, sizeof(in));
+ std::ostringstream os3;
+ bufferSource(in3) >> streamSink(os3);
+ std::string out3 = os3.str();
+ BOOST_CHECK_EQUAL_COLLECTIONS(in3.begin(), in3.end(), out3.begin(), out3.end());
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestBufferSource
+BOOST_AUTO_TEST_SUITE_END() // Transform
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace transform
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/transform/digest-filter.t.cpp b/tests/unit/security/transform/digest-filter.t.cpp
new file mode 100644
index 0000000..5c0aaca
--- /dev/null
+++ b/tests/unit/security/transform/digest-filter.t.cpp
@@ -0,0 +1,222 @@
+/* -*- 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 "security/transform/digest-filter.hpp"
+
+#include "encoding/buffer-stream.hpp"
+#include "security/detail/openssl.hpp"
+#include "security/transform/buffer-source.hpp"
+#include "security/transform/step-source.hpp"
+#include "security/transform/stream-sink.hpp"
+
+#include "boost-test.hpp"
+
+namespace ndn {
+namespace security {
+namespace transform {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(Transform)
+BOOST_AUTO_TEST_SUITE(TestDigestFilter)
+
+static const uint8_t in[] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f
+};
+static const uint8_t out[] = {
+ 0x3e, 0x14, 0xfd, 0x66, 0x9a, 0x79, 0x80, 0x65,
+ 0xc4, 0x0d, 0x61, 0xf8, 0x6a, 0xc7, 0x98, 0x29,
+ 0xc0, 0x6b, 0x90, 0x8f, 0xbb, 0x19, 0xa0, 0x85,
+ 0xf7, 0xfa, 0x7b, 0x2d, 0xd6, 0x8c, 0xd5, 0xa3
+};
+
+BOOST_AUTO_TEST_CASE(BufferInput)
+{
+ OBufferStream os;
+ bufferSource(in, sizeof(in)) >> digestFilter(DigestAlgorithm::SHA256) >> streamSink(os);
+ BOOST_CHECK_EQUAL_COLLECTIONS(out, out + sizeof(out), os.buf()->begin(), os.buf()->end());
+}
+
+BOOST_AUTO_TEST_CASE(StepInput)
+{
+ StepSource source;
+ OBufferStream os;
+ source >> digestFilter(DigestAlgorithm::SHA256) >> streamSink(os);
+ source.write(in, 32);
+ source.write(in + 32, 1);
+ source.write(in + 33, 2);
+ source.write(in + 35, 3);
+ source.write(in + 38, 26);
+ source.write(in + 64, 64);
+ source.end();
+ BOOST_CHECK_EQUAL_COLLECTIONS(out, out + sizeof(out), os.buf()->begin(), os.buf()->end());
+}
+
+BOOST_AUTO_TEST_CASE(AlgorithmNone)
+{
+ BOOST_CHECK_THROW(DigestFilter{DigestAlgorithm::NONE}, Error);
+}
+
+BOOST_AUTO_TEST_CASE(AlgorithmSha224)
+{
+ const uint8_t out[] = {
+ 0xd1, 0x4a, 0x02, 0x8c, 0x2a, 0x3a, 0x2b, 0xc9, 0x47, 0x61, 0x02, 0xbb, 0x28, 0x82, 0x34, 0xc4,
+ 0x15, 0xa2, 0xb0, 0x1f, 0x82, 0x8e, 0xa6, 0x2a, 0xc5, 0xb3, 0xe4, 0x2f,
+ };
+ OBufferStream os;
+ bufferSource("") >> digestFilter(DigestAlgorithm::SHA224) >> streamSink(os);
+ BOOST_CHECK_EQUAL_COLLECTIONS(out, out + sizeof(out), os.buf()->begin(), os.buf()->end());
+}
+
+BOOST_AUTO_TEST_CASE(AlgorithmSha256)
+{
+ const uint8_t out[] = {
+ 0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24,
+ 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55,
+ };
+ OBufferStream os;
+ bufferSource("") >> digestFilter(DigestAlgorithm::SHA256) >> streamSink(os);
+ BOOST_CHECK_EQUAL_COLLECTIONS(out, out + sizeof(out), os.buf()->begin(), os.buf()->end());
+}
+
+BOOST_AUTO_TEST_CASE(AlgorithmSha384)
+{
+ const uint8_t out[] = {
+ 0x38, 0xb0, 0x60, 0xa7, 0x51, 0xac, 0x96, 0x38, 0x4c, 0xd9, 0x32, 0x7e, 0xb1, 0xb1, 0xe3, 0x6a,
+ 0x21, 0xfd, 0xb7, 0x11, 0x14, 0xbe, 0x07, 0x43, 0x4c, 0x0c, 0xc7, 0xbf, 0x63, 0xf6, 0xe1, 0xda,
+ 0x27, 0x4e, 0xde, 0xbf, 0xe7, 0x6f, 0x65, 0xfb, 0xd5, 0x1a, 0xd2, 0xf1, 0x48, 0x98, 0xb9, 0x5b,
+ };
+ OBufferStream os;
+ bufferSource("") >> digestFilter(DigestAlgorithm::SHA384) >> streamSink(os);
+ BOOST_CHECK_EQUAL_COLLECTIONS(out, out + sizeof(out), os.buf()->begin(), os.buf()->end());
+}
+
+BOOST_AUTO_TEST_CASE(AlgorithmSha512)
+{
+ const uint8_t out[] = {
+ 0xcf, 0x83, 0xe1, 0x35, 0x7e, 0xef, 0xb8, 0xbd, 0xf1, 0x54, 0x28, 0x50, 0xd6, 0x6d, 0x80, 0x07,
+ 0xd6, 0x20, 0xe4, 0x05, 0x0b, 0x57, 0x15, 0xdc, 0x83, 0xf4, 0xa9, 0x21, 0xd3, 0x6c, 0xe9, 0xce,
+ 0x47, 0xd0, 0xd1, 0x3c, 0x5d, 0x85, 0xf2, 0xb0, 0xff, 0x83, 0x18, 0xd2, 0x87, 0x7e, 0xec, 0x2f,
+ 0x63, 0xb9, 0x31, 0xbd, 0x47, 0x41, 0x7a, 0x81, 0xa5, 0x38, 0x32, 0x7a, 0xf9, 0x27, 0xda, 0x3e,
+ };
+ OBufferStream os;
+ bufferSource("") >> digestFilter(DigestAlgorithm::SHA512) >> streamSink(os);
+ BOOST_CHECK_EQUAL_COLLECTIONS(out, out + sizeof(out), os.buf()->begin(), os.buf()->end());
+}
+
+#if OPENSSL_VERSION_NUMBER >= 0x1010000fL && !defined(OPENSSL_NO_BLAKE2)
+BOOST_AUTO_TEST_CASE(AlgorithmBlake2b_512)
+{
+ const uint8_t out[] = {
+ 0x78, 0x6a, 0x02, 0xf7, 0x42, 0x01, 0x59, 0x03, 0xc6, 0xc6, 0xfd, 0x85, 0x25, 0x52, 0xd2, 0x72,
+ 0x91, 0x2f, 0x47, 0x40, 0xe1, 0x58, 0x47, 0x61, 0x8a, 0x86, 0xe2, 0x17, 0xf7, 0x1f, 0x54, 0x19,
+ 0xd2, 0x5e, 0x10, 0x31, 0xaf, 0xee, 0x58, 0x53, 0x13, 0x89, 0x64, 0x44, 0x93, 0x4e, 0xb0, 0x4b,
+ 0x90, 0x3a, 0x68, 0x5b, 0x14, 0x48, 0xb7, 0x55, 0xd5, 0x6f, 0x70, 0x1a, 0xfe, 0x9b, 0xe2, 0xce,
+ };
+ OBufferStream os;
+ bufferSource("") >> digestFilter(DigestAlgorithm::BLAKE2B_512) >> streamSink(os);
+ BOOST_CHECK_EQUAL_COLLECTIONS(out, out + sizeof(out), os.buf()->begin(), os.buf()->end());
+}
+
+BOOST_AUTO_TEST_CASE(AlgorithmBlake2s_256)
+{
+ const uint8_t out[] = {
+ 0x69, 0x21, 0x7a, 0x30, 0x79, 0x90, 0x80, 0x94, 0xe1, 0x11, 0x21, 0xd0, 0x42, 0x35, 0x4a, 0x7c,
+ 0x1f, 0x55, 0xb6, 0x48, 0x2c, 0xa1, 0xa5, 0x1e, 0x1b, 0x25, 0x0d, 0xfd, 0x1e, 0xd0, 0xee, 0xf9,
+ };
+ OBufferStream os;
+ bufferSource("") >> digestFilter(DigestAlgorithm::BLAKE2S_256) >> streamSink(os);
+ BOOST_CHECK_EQUAL_COLLECTIONS(out, out + sizeof(out), os.buf()->begin(), os.buf()->end());
+}
+#endif // OPENSSL_VERSION_NUMBER >= 0x1010000fL && !defined(OPENSSL_NO_BLAKE2)
+
+#if OPENSSL_VERSION_NUMBER >= 0x10101001L
+BOOST_AUTO_TEST_CASE(AlgorithmSha3_224)
+{
+ const uint8_t out[] = {
+ 0x6b, 0x4e, 0x03, 0x42, 0x36, 0x67, 0xdb, 0xb7, 0x3b, 0x6e, 0x15, 0x45, 0x4f, 0x0e, 0xb1, 0xab,
+ 0xd4, 0x59, 0x7f, 0x9a, 0x1b, 0x07, 0x8e, 0x3f, 0x5b, 0x5a, 0x6b, 0xc7,
+ };
+ OBufferStream os;
+ bufferSource("") >> digestFilter(DigestAlgorithm::SHA3_224) >> streamSink(os);
+ BOOST_CHECK_EQUAL_COLLECTIONS(out, out + sizeof(out), os.buf()->begin(), os.buf()->end());
+}
+
+BOOST_AUTO_TEST_CASE(AlgorithmSha3_256)
+{
+ const uint8_t out[] = {
+ 0xa7, 0xff, 0xc6, 0xf8, 0xbf, 0x1e, 0xd7, 0x66, 0x51, 0xc1, 0x47, 0x56, 0xa0, 0x61, 0xd6, 0x62,
+ 0xf5, 0x80, 0xff, 0x4d, 0xe4, 0x3b, 0x49, 0xfa, 0x82, 0xd8, 0x0a, 0x4b, 0x80, 0xf8, 0x43, 0x4a,
+ };
+ OBufferStream os;
+ bufferSource("") >> digestFilter(DigestAlgorithm::SHA3_256) >> streamSink(os);
+ BOOST_CHECK_EQUAL_COLLECTIONS(out, out + sizeof(out), os.buf()->begin(), os.buf()->end());
+}
+
+BOOST_AUTO_TEST_CASE(AlgorithmSha3_384)
+{
+ const uint8_t out[] = {
+ 0x0c, 0x63, 0xa7, 0x5b, 0x84, 0x5e, 0x4f, 0x7d, 0x01, 0x10, 0x7d, 0x85, 0x2e, 0x4c, 0x24, 0x85,
+ 0xc5, 0x1a, 0x50, 0xaa, 0xaa, 0x94, 0xfc, 0x61, 0x99, 0x5e, 0x71, 0xbb, 0xee, 0x98, 0x3a, 0x2a,
+ 0xc3, 0x71, 0x38, 0x31, 0x26, 0x4a, 0xdb, 0x47, 0xfb, 0x6b, 0xd1, 0xe0, 0x58, 0xd5, 0xf0, 0x04,
+ };
+ OBufferStream os;
+ bufferSource("") >> digestFilter(DigestAlgorithm::SHA3_384) >> streamSink(os);
+ BOOST_CHECK_EQUAL_COLLECTIONS(out, out + sizeof(out), os.buf()->begin(), os.buf()->end());
+}
+
+BOOST_AUTO_TEST_CASE(AlgorithmSha3_512)
+{
+ const uint8_t out[] = {
+ 0xa6, 0x9f, 0x73, 0xcc, 0xa2, 0x3a, 0x9a, 0xc5, 0xc8, 0xb5, 0x67, 0xdc, 0x18, 0x5a, 0x75, 0x6e,
+ 0x97, 0xc9, 0x82, 0x16, 0x4f, 0xe2, 0x58, 0x59, 0xe0, 0xd1, 0xdc, 0xc1, 0x47, 0x5c, 0x80, 0xa6,
+ 0x15, 0xb2, 0x12, 0x3a, 0xf1, 0xf5, 0xf9, 0x4c, 0x11, 0xe3, 0xe9, 0x40, 0x2c, 0x3a, 0xc5, 0x58,
+ 0xf5, 0x00, 0x19, 0x9d, 0x95, 0xb6, 0xd3, 0xe3, 0x01, 0x75, 0x85, 0x86, 0x28, 0x1d, 0xcd, 0x26,
+ };
+ OBufferStream os;
+ bufferSource("") >> digestFilter(DigestAlgorithm::SHA3_512) >> streamSink(os);
+ BOOST_CHECK_EQUAL_COLLECTIONS(out, out + sizeof(out), os.buf()->begin(), os.buf()->end());
+}
+#endif // OPENSSL_VERSION_NUMBER >= 0x10101001L
+
+BOOST_AUTO_TEST_SUITE_END() // TestDigestFilter
+BOOST_AUTO_TEST_SUITE_END() // Transform
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace transform
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/transform/hex-decode.t.cpp b/tests/unit/security/transform/hex-decode.t.cpp
new file mode 100644
index 0000000..3c05ab4
--- /dev/null
+++ b/tests/unit/security/transform/hex-decode.t.cpp
@@ -0,0 +1,294 @@
+/* -*- 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 "security/transform/hex-decode.hpp"
+#include "security/transform/buffer-source.hpp"
+#include "security/transform/step-source.hpp"
+#include "security/transform/stream-sink.hpp"
+#include "encoding/buffer-stream.hpp"
+
+#include "boost-test.hpp"
+
+namespace ndn {
+namespace security {
+namespace transform {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(Transform)
+BOOST_AUTO_TEST_SUITE(TestHexDecode)
+
+BOOST_AUTO_TEST_CASE(Basic)
+{
+ std::string in =
+ "000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f"
+ "000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f"
+ "000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f"
+ "000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f";
+
+ uint8_t out[] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f
+ };
+
+ OBufferStream os;
+ bufferSource(in) >> hexDecode() >> streamSink(os);
+
+ ConstBufferPtr buf1 = os.buf();
+ BOOST_CHECK_EQUAL_COLLECTIONS(out, out + sizeof(out), buf1->begin(), buf1->end());
+}
+
+BOOST_AUTO_TEST_CASE(UpperCase)
+{
+ std::string in =
+ "000102030405060708090A0B0C0D0E0F000102030405060708090A0B0C0D0E0F"
+ "000102030405060708090A0B0C0D0E0F000102030405060708090A0B0C0D0E0F"
+ "000102030405060708090A0B0C0D0E0F000102030405060708090A0B0C0D0E0F"
+ "000102030405060708090A0B0C0D0E0F000102030405060708090A0B0C0D0E0F";
+
+ uint8_t out[] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f
+ };
+
+ OBufferStream os;
+ bufferSource(in) >> hexDecode() >> streamSink(os);
+
+ ConstBufferPtr buf1 = os.buf();
+ BOOST_CHECK_EQUAL_COLLECTIONS(out, out + sizeof(out), buf1->begin(), buf1->end());
+}
+
+BOOST_AUTO_TEST_CASE(MixCase)
+{
+ std::string in =
+ "000102030405060708090a0b0c0d0e0f"
+ "101112131415161718191a1b1c1d1e1f"
+ "202122232425262728292a2b2c2d2e2f"
+ "303132333435363738393a3b3c3d3e3f"
+ "404142434445464748494a4b4c4d4e4f"
+ "505152535455565758595a5b5c5d5e5f"
+ "606162636465666768696a6b6c6d6e6f"
+ "707172737475767778797a7b7c7d7e7f"
+ "808182838485868788898a8b8c8d8e8f"
+ "909192939495969798999a9b9c9d9e9f"
+ "a0a1a2a3a4a5a6a7a8a9aaabacadaeaf"
+ "b0b1b2b3b4b5b6b7b8b9babbbcbdbebf"
+ "c0c1c2c3c4c5c6c7c8c9cacbcccdcecf"
+ "d0d1d2d3d4d5d6d7d8d9dadbdcdddedf"
+ "e0e1e2e3e4e5e6e7e8e9eaebecedeeef"
+ "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff"
+ "000102030405060708090A0B0C0D0E0F"
+ "101112131415161718191A1B1C1D1E1F"
+ "202122232425262728292A2B2C2D2E2F"
+ "303132333435363738393A3B3C3D3E3F"
+ "404142434445464748494A4B4C4D4E4F"
+ "505152535455565758595A5B5C5D5E5F"
+ "606162636465666768696A6B6C6D6E6F"
+ "707172737475767778797A7B7C7D7E7F"
+ "808182838485868788898A8B8C8D8E8F"
+ "909192939495969798999A9B9C9D9E9F"
+ "A0A1A2A3A4A5A6A7A8A9AAABACADAEAF"
+ "B0B1B2B3B4B5B6B7B8B9BABBBCBDBEBF"
+ "C0C1C2C3C4C5C6C7C8C9CACBCCCDCECF"
+ "D0D1D2D3D4D5D6D7D8D9DADBDCDDDEDF"
+ "E0E1E2E3E4E5E6E7E8E9EAEBECEDEEEF"
+ "F0F1F2F3F4F5F6F7F8F9FAFBFCFDFEFF"
+ "aaabacadaeafaAaBaCaD"
+ "babbbcbdbebfbAbBbCbD"
+ "cacbcccdcecfcAcBcCcD"
+ "dadbdcdddedfdAdBdCdD"
+ "eaebecedeeefeAeBeCeD"
+ "fafbfcfdfefffAfBfCfD"
+ "AaAbAcAdAeAfAAABACAD"
+ "BaBbBcBdBeBfBABBBCBD"
+ "CaCbCcCdCeCfCACBCCCD"
+ "DaDbDcDdDeDfDADBDCDD";
+
+ uint8_t out[] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F,
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F,
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F,
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F,
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F,
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F,
+ 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
+ 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F,
+ 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF,
+ 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF,
+ 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF,
+ 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF,
+ 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF,
+ 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F,
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F,
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F,
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F,
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F,
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F,
+ 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
+ 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F,
+ 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF,
+ 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF,
+ 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF,
+ 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF,
+ 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF,
+ 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF,
+ 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, 0xAA, 0xAB, 0xAC, 0xAD,
+ 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF, 0xBA, 0xBB, 0xBC, 0xBD,
+ 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, 0xCA, 0xCB, 0xCC, 0xCD,
+ 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF, 0xDA, 0xDB, 0xDC, 0xDD,
+ 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, 0xEA, 0xEB, 0xEC, 0xED,
+ 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF, 0xFA, 0xFB, 0xFC, 0xFD,
+ 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, 0xAA, 0xAB, 0xAC, 0xAD,
+ 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF, 0xBA, 0xBB, 0xBC, 0xBD,
+ 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, 0xCA, 0xCB, 0xCC, 0xCD,
+ 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF, 0xDA, 0xDB, 0xDC, 0xDD
+ };
+
+ OBufferStream os;
+ bufferSource(in) >> hexDecode() >> streamSink(os);
+
+ ConstBufferPtr buf1 = os.buf();
+ BOOST_CHECK_EQUAL_COLLECTIONS(out, out + sizeof(out), buf1->begin(), buf1->end());
+}
+
+
+BOOST_AUTO_TEST_CASE(StepByStep)
+{
+ std::string in =
+ "000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f"
+ "000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f"
+ "000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f"
+ "000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f"
+ "000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f"
+ "000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f"
+ "000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f"
+ "000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f"
+ "000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f"
+ "000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f"
+ "000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f"
+ "000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f";
+
+ uint8_t out[] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f
+ };
+
+ const uint8_t* input = reinterpret_cast<const uint8_t*>(in.data());
+ OBufferStream os;
+ StepSource source;
+ source >> hexDecode() >> streamSink(os);
+ source.write(input, 128); // complete chunk
+ source.write(input + 128, 64); // first half of a chunk
+ source.write(input + 192, 64); // second half of a chunk
+ source.write(input + 256, 127); // odd number of byets
+ source.write(input + 383, 192); // one and half chunk
+ source.write(input + 575, 193); // remaining part
+ source.end();
+
+ ConstBufferPtr buf1 = os.buf();
+ BOOST_CHECK_EQUAL_COLLECTIONS(out, out + sizeof(out), buf1->begin(), buf1->end());
+}
+
+BOOST_AUTO_TEST_CASE(OddByte)
+{
+ std::string in1 = "0001020304050";
+
+ OBufferStream os1;
+ BOOST_REQUIRE_THROW(bufferSource(in1) >> hexDecode() >> streamSink(os1), transform::Error);
+
+ std::string in2 = "0001020304xy";
+
+ OBufferStream os2;
+ BOOST_REQUIRE_THROW(bufferSource(in2) >> hexDecode() >> streamSink(os2), transform::Error);
+}
+
+BOOST_AUTO_TEST_CASE(EmptyInput)
+{
+ OBufferStream os;
+ StepSource source;
+ source >> hexDecode() >> streamSink(os);
+ source.end();
+ BOOST_CHECK_EQUAL(os.buf()->size(), 0);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestHexDecode
+BOOST_AUTO_TEST_SUITE_END() // Transform
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace transform
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/transform/hex-encode.t.cpp b/tests/unit/security/transform/hex-encode.t.cpp
new file mode 100644
index 0000000..26f6321
--- /dev/null
+++ b/tests/unit/security/transform/hex-encode.t.cpp
@@ -0,0 +1,184 @@
+/* -*- 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 "security/transform/hex-encode.hpp"
+#include "security/transform/buffer-source.hpp"
+#include "security/transform/step-source.hpp"
+#include "security/transform/stream-sink.hpp"
+#include "encoding/buffer-stream.hpp"
+
+#include "boost-test.hpp"
+
+namespace ndn {
+namespace security {
+namespace transform {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(Transform)
+BOOST_AUTO_TEST_SUITE(TestHexEncode)
+
+BOOST_AUTO_TEST_CASE(Basic)
+{
+ uint8_t in[128] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f
+ };
+
+ std::string out =
+ "000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f"
+ "000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f"
+ "000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f"
+ "000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f";
+
+ OBufferStream os;
+ bufferSource(in, sizeof(in)) >> hexEncode() >> streamSink(os);
+
+ ConstBufferPtr buf1 = os.buf();
+ BOOST_CHECK_EQUAL_COLLECTIONS(out.begin(), out.end(), buf1->begin(), buf1->end());
+}
+
+BOOST_AUTO_TEST_CASE(UpperCase)
+{
+ uint8_t in[128] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f
+ };
+
+ std::string out =
+ "000102030405060708090A0B0C0D0E0F000102030405060708090A0B0C0D0E0F"
+ "000102030405060708090A0B0C0D0E0F000102030405060708090A0B0C0D0E0F"
+ "000102030405060708090A0B0C0D0E0F000102030405060708090A0B0C0D0E0F"
+ "000102030405060708090A0B0C0D0E0F000102030405060708090A0B0C0D0E0F";
+
+ OBufferStream os;
+ bufferSource(in, sizeof(in)) >> hexEncode(true) >> streamSink(os);
+
+ ConstBufferPtr buf1 = os.buf();
+ BOOST_CHECK_EQUAL_COLLECTIONS(out.begin(), out.end(), buf1->begin(), buf1->end());
+}
+
+BOOST_AUTO_TEST_CASE(StepByStep)
+{
+ uint8_t in[] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f
+ };
+
+ std::string out =
+ "000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f"
+ "000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f"
+ "000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f"
+ "000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f"
+ "000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f"
+ "000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f"
+ "000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f"
+ "000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f"
+ "000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f"
+ "000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f"
+ "000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f"
+ "000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f";
+
+ OBufferStream os;
+ StepSource source;
+ source >> hexEncode() >> streamSink(os);
+ source.write(in, 64); // complete chunk
+ source.write(in + 64, 32); // first half of a chunk
+ source.write(in + 96, 32); // second half of a chunk
+ source.write(in + 128, 20); // front of a chunk
+ source.write(in + 148, 20); // middle of a chunk
+ source.write(in + 168, 24); // end of a chunk
+ source.write(in + 192, 63); // odd number of bytes
+ source.write(in + 255, 85); // one and half chunk
+ source.write(in + 340, 44); // remaining part
+ source.end();
+
+ ConstBufferPtr buf1 = os.buf();
+ BOOST_CHECK_EQUAL_COLLECTIONS(out.begin(), out.end(), buf1->begin(), buf1->end());
+}
+
+BOOST_AUTO_TEST_CASE(EmptyInput)
+{
+ OBufferStream os;
+ StepSource source;
+ source >> hexEncode() >> streamSink(os);
+ source.end();
+ BOOST_CHECK_EQUAL(os.buf()->size(), 0);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestHexEncode
+BOOST_AUTO_TEST_SUITE_END() // Transform
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace transform
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/transform/hmac-filter.t.cpp b/tests/unit/security/transform/hmac-filter.t.cpp
new file mode 100644
index 0000000..1ed5e25
--- /dev/null
+++ b/tests/unit/security/transform/hmac-filter.t.cpp
@@ -0,0 +1,111 @@
+/* -*- 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 "security/transform/hmac-filter.hpp"
+
+#include "encoding/buffer-stream.hpp"
+#include "security/transform/buffer-source.hpp"
+#include "security/transform/step-source.hpp"
+#include "security/transform/stream-sink.hpp"
+
+#include "boost-test.hpp"
+
+namespace ndn {
+namespace security {
+namespace transform {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(Transform)
+BOOST_AUTO_TEST_SUITE(TestHmacFilter)
+
+static const uint8_t key[] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f
+};
+static const uint8_t data[] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f
+};
+static const uint8_t digest[] = {
+ 0x9f, 0x3a, 0xa2, 0x88, 0x26, 0xb3, 0x74, 0x85,
+ 0xca, 0x05, 0x01, 0x4d, 0x71, 0x42, 0xb3, 0xea,
+ 0x3f, 0xfb, 0xda, 0x5a, 0x35, 0xbf, 0xd2, 0x0f,
+ 0x2f, 0x9c, 0x8f, 0xcc, 0x6d, 0x30, 0x48, 0x54
+};
+
+BOOST_AUTO_TEST_CASE(Basic)
+{
+ OBufferStream os;
+ bufferSource(data, sizeof(data)) >> hmacFilter(DigestAlgorithm::SHA256, key, sizeof(key)) >> streamSink(os);
+
+ ConstBufferPtr buf = os.buf();
+ BOOST_CHECK_EQUAL_COLLECTIONS(digest, digest + sizeof(digest), buf->begin(), buf->end());
+}
+
+BOOST_AUTO_TEST_CASE(StepByStep)
+{
+ OBufferStream os;
+ StepSource source;
+ source >> hmacFilter(DigestAlgorithm::SHA256, key, sizeof(key)) >> streamSink(os);
+ source.write(data, 1);
+ source.write(data + 1, 2);
+ source.write(data + 3, 3);
+ source.write(data + 6, 4);
+ source.write(data + 10, 5);
+ source.write(data + 15, 1);
+ source.end();
+
+ ConstBufferPtr buf = os.buf();
+ BOOST_CHECK_EQUAL_COLLECTIONS(digest, digest + sizeof(digest), buf->begin(), buf->end());
+}
+
+BOOST_AUTO_TEST_CASE(EmptyInput)
+{
+ const uint8_t digest[] = {
+ 0x07, 0xEF, 0xF8, 0xB3, 0x26, 0xB7, 0x79, 0x8C,
+ 0x9C, 0xCF, 0xCB, 0xDB, 0xE5, 0x79, 0x48, 0x9A,
+ 0xC7, 0x85, 0xA7, 0x99, 0x5A, 0x04, 0x61, 0x8B,
+ 0x1A, 0x28, 0x13, 0xC2, 0x67, 0x44, 0x77, 0x7D
+ };
+
+ OBufferStream os;
+ StepSource source;
+ source >> hmacFilter(DigestAlgorithm::SHA256, key, sizeof(key)) >> streamSink(os);
+ source.end();
+
+ ConstBufferPtr buf = os.buf();
+ BOOST_CHECK_EQUAL_COLLECTIONS(digest, digest + sizeof(digest), buf->begin(), buf->end());
+}
+
+BOOST_AUTO_TEST_CASE(InvalidAlgorithm)
+{
+ BOOST_CHECK_THROW(HmacFilter(DigestAlgorithm::NONE, key, sizeof(key)), Error);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestHmacFilter
+BOOST_AUTO_TEST_SUITE_END() // Transform
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace transform
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/transform/private-key.t.cpp b/tests/unit/security/transform/private-key.t.cpp
new file mode 100644
index 0000000..ee956f9
--- /dev/null
+++ b/tests/unit/security/transform/private-key.t.cpp
@@ -0,0 +1,401 @@
+/* -*- 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 "security/transform/private-key.hpp"
+
+#include "encoding/buffer-stream.hpp"
+#include "security/key-params.hpp"
+#include "security/transform.hpp"
+
+#include "boost-test.hpp"
+#include <boost/mpl/vector.hpp>
+
+#include <sstream>
+
+namespace ndn {
+namespace security {
+namespace transform {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(Transform)
+BOOST_AUTO_TEST_SUITE(TestPrivateKey)
+
+struct RsaKeyTestData
+{
+ const std::string privateKeyPkcs1 =
+ "MIIEpAIBAAKCAQEAw0WM1/WhAxyLtEqsiAJgWDZWuzkYpeYVdeeZcqRZzzfRgBQT\n"
+ "sNozS5t4HnwTZhwwXbH7k3QN0kRTV826Xobws3iigohnM9yTK+KKiayPhIAm/+5H\n"
+ "GT6SgFJhYhqo1/upWdueojil6RP4/AgavHhopxlAVbk6G9VdVnlQcQ5Zv0OcGi73\n"
+ "c+EnYD/YgURYGSngUi/Ynsh779p2U69/te9gZwIL5PuE9BiO6I39cL9z7EK1SfZh\n"
+ "OWvDe/qH7YhD/BHwcWit8FjRww1glwRVTJsA9rH58ynaAix0tcR/nBMRLUX+e3rU\n"
+ "RHg6UbSjJbdb9qmKM1fTGHKUzL/5pMG6uBU0ywIDAQABAoIBADQkckOIl4IZMUTn\n"
+ "W8LFv6xOdkJwMKC8G6bsPRFbyY+HvC2TLt7epSvfS+f4AcYWaOPcDu2E49vt2sNr\n"
+ "cASly8hgwiRRAB3dHH9vcsboiTo8bi2RFvMqvjv9w3tK2yMxVDtmZamzrrnaV3YV\n"
+ "Q+5nyKo2F/PMDjQ4eUAKDOzjhBuKHsZBTFnA1MFNI+UKj5X4Yp64DFmKlxTX/U2b\n"
+ "wzVywo5hzx2Uhw51jmoLls4YUvMJXD0wW5ZtYRuPogXvXb/of9ef/20/wU11WFKg\n"
+ "Xb4gfR8zUXaXS1sXcnVm3+24vIs9dApUwykuoyjOqxWqcHRec2QT2FxVGkFEraze\n"
+ "CPa4rMECgYEA5Y8CywomIcTgerFGFCeMHJr8nQGqY2V/owFb3k9maczPnC9p4a9R\n"
+ "c5szLxA9FMYFxurQZMBWSEG2JS1HR2mnjigx8UKjYML/A+rvvjZOMe4M6Sy2ggh4\n"
+ "SkLZKpWTzjTe07ByM/j5v/SjNZhWAG7sw4/LmPGRQkwJv+KZhGojuOkCgYEA2cOF\n"
+ "T6cJRv6kvzTz9S0COZOVm+euJh/BXp7oAsAmbNfOpckPMzqHXy8/wpdKl6AAcB57\n"
+ "OuztlNfV1D7qvbz7JuRlYwQ0cEfBgbZPcz1p18HHDXhwn57ZPb8G33Yh9Omg0HNA\n"
+ "Imb4LsVuSqxA6NwSj7cpRekgTedrhLFPJ+Ydb5MCgYEAsM3Q7OjILcIg0t6uht9e\n"
+ "vrlwTsz1mtCV2co2I6crzdj9HeI2vqf1KAElDt6G7PUHhglcr/yjd8uEqmWRPKNX\n"
+ "ddnnfVZB10jYeP/93pac6z/Zmc3iU4yKeUe7U10ZFf0KkiiYDQd59CpLef/2XScS\n"
+ "HB0oRofnxRQjfjLc4muNT+ECgYEAlcDk06MOOTly+F8lCc1bA1dgAmgwFd2usDBd\n"
+ "Y07a3e0HGnGLN3Kfl7C5i0tZq64HvxLnMd2vgLVxQlXGPpdQrC1TH+XLXg+qnlZO\n"
+ "ivSH7i0/gx75bHvj75eH1XK65V8pDVDEoSPottllAIs21CxLw3N1ObOZWJm2EfmR\n"
+ "cuHICmsCgYAtFJ1idqMoHxES3mlRpf2JxyQudP3SCm2WpGmqVzhRYInqeatY5sUd\n"
+ "lPLHm/p77RT7EyxQHTlwn8FJPuM/4ZH1rQd/vB+Y8qAtYJCexDMsbvLW+Js+VOvk\n"
+ "jweEC0nrcL31j9mF0vz5E6tfRu4hhJ6L4yfWs0gSejskeVB/w8QY4g==\n";
+ const std::string privateKeyPkcs8 =
+ "MIIFCzA9BgkqhkiG9w0BBQ0wMDAbBgkqhkiG9w0BBQwwDgQIOKYJXvB6p8kCAggA\n"
+ "MBEGBSsOAwIHBAiQgMK8kQXTyASCBMjeNiKYYw5/yHgs9BfSGrpqvV0LkkgMQNUW\n"
+ "R4ZY8fuNjZynd+PxDuw2pyrv1Yv3jc+tupwUehZEzYOnGd53wQAuLO+Z0TBgRFN7\n"
+ "Lhk+AxlT7hu0xaB3ZpJ/uvWpgEJHsq/aB/GYgyzXcQo2AiqzERVpMCWJVmE1L977\n"
+ "CHwJmLm5mxclVLYp1UK5lkIBFu/M4nPavmNmYNUU1LOrXRo56TlJ2kUp8gQyQI1P\n"
+ "VPxi4chmlsr/OnQ2d1eZN+euFm0CS+yP+LFgI9ZqdyH1w+J43SXdHDzauVcZp7oa\n"
+ "Kw24OrhHfolLAnQIECXEJYeT7tZmhC4O9V6B18PFVyxWnEU4eFNpFE8kYSmm8Um2\n"
+ "buvDKI71q43hm23moYT9uIM1f4M8UkoOliJGrlf4xgEcmDuokEX01PdOq1gc4nvG\n"
+ "0DCwDI9cOsyn8cxhk9UVtFgzuG/seuznxIv1F5H0hzYOyloStXxRisJES0kgByBt\n"
+ "FFTfyoFKRrmCjRIygwVKUSkSDR0DlQS5ZLvQyIswnSQFwxAHqfvoSL4dB9UAIAQ+\n"
+ "ALVF1maaHgptbL6Ifqf0GFCv0hdNCVNDNCdy8R+S6nEYE+YdYSIdT1L88KD5PjU3\n"
+ "YY/CMnxhTncMaT4acPO1UUYuSGRZ/JL6E0ihoqIU+bqUgLSHNzhPySPfN9uqN61Y\n"
+ "HFBtxeEPWKU0f/JPkRBMmZdMI1/OVmA3QHSRBydI+CQN8no2gZRFoVbHTkG8IMpE\n"
+ "1fiDJpwFkpzIv/JPiTSE7DeBH5NJk1bgu7TcuZfa4unyAqss0UuLnXzS06TppkUj\n"
+ "QGft0g8VPW56eli6B4xrSzzuvAdbrxsVfxdmtHPyYxLb3/UG1g4x/H/yULhx7x9P\n"
+ "iI6cw6JUE+8bwJV2ZIlHXXHO+wUp/gCFJ6MHo9wkR1QvnHP2ClJAzBm9OvYnUx2Y\n"
+ "SX0HxEowW8BkhxOF184LEmxeua0yyZUqCdrYmErp7x9EY/LhD1zBwH8OGRa0qzmR\n"
+ "VKxAPKihkb9OgxcUKbvKePx3k2cQ7fbCUspGPm4Kn1zwMgRAZ4fz/o8Lnwc8MSY3\n"
+ "lPWnmLTFu420SRH2g9N0o/r195hiZ5cc+KfF4pwZWKbEbKFk/UfXA9vmOi7BBtDJ\n"
+ "RWshOINhzMU6Ij3KuaEpHni1HoHjw0SQ97ow2x/aB8k2QC28tbsa49lD2KKJku6b\n"
+ "2Or89adwFKqMgS2IXfXMXs/iG5EFLYN6r8e40Dn5f1vJfRLJl03XByIfT2n92pw3\n"
+ "fP7muOIKLUsEKjOrmn94NwMlfeW13oQHEH2KjPOWFS/tyJHDdVU+of4COH5yg59a\n"
+ "TZqFkOTGeliE1O+6sfF9fRuVxFUF3D8Hpr0JIjdc6+3RgIlGsXc8BwiSjDSI2XW+\n"
+ "vo75/2zPU9t8OeXEIJk2CQGyqLwUJ6dyi/yDRrvZAgjrUvbpcxydnBAHrLbLUGXJ\n"
+ "aEHH2tjEtnTqVyTchr1yHoupcFOCkA0dAA66XqwcssQxJiMGrWTpCbgd9mrTXQaZ\n"
+ "U7afFN1jpO78tgBQUUpImXdHLLsqdN5tefqjileZGZ9x3/C6TNAfDwYJdsicNNn5\n"
+ "y+JVsbltfLWlJxb9teb3dtQiFlJ7ofprLJnJVqI/Js8lozY+KaxV2vtbZkcD4dM=\n";
+ const std::string publicKeyPkcs8 =
+ "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAw0WM1/WhAxyLtEqsiAJg\n"
+ "WDZWuzkYpeYVdeeZcqRZzzfRgBQTsNozS5t4HnwTZhwwXbH7k3QN0kRTV826Xobw\n"
+ "s3iigohnM9yTK+KKiayPhIAm/+5HGT6SgFJhYhqo1/upWdueojil6RP4/AgavHho\n"
+ "pxlAVbk6G9VdVnlQcQ5Zv0OcGi73c+EnYD/YgURYGSngUi/Ynsh779p2U69/te9g\n"
+ "ZwIL5PuE9BiO6I39cL9z7EK1SfZhOWvDe/qH7YhD/BHwcWit8FjRww1glwRVTJsA\n"
+ "9rH58ynaAix0tcR/nBMRLUX+e3rURHg6UbSjJbdb9qmKM1fTGHKUzL/5pMG6uBU0\n"
+ "ywIDAQAB\n";
+};
+
+struct EcKeyTestData
+{
+ const std::string privateKeyPkcs1 =
+ "MIIBaAIBAQQgRxwcbzK9RV6AHYFsDcykI86o3M/a1KlJn0z8PcLMBZOggfowgfcC\n"
+ "AQEwLAYHKoZIzj0BAQIhAP////8AAAABAAAAAAAAAAAAAAAA////////////////\n"
+ "MFsEIP////8AAAABAAAAAAAAAAAAAAAA///////////////8BCBaxjXYqjqT57Pr\n"
+ "vVV2mIa8ZR0GsMxTsPY7zjw+J9JgSwMVAMSdNgiG5wSTamZ44ROdJreBn36QBEEE\n"
+ "axfR8uEsQkf4vOblY6RA8ncDfYEt6zOg9KE5RdiYwpZP40Li/hp/m47n60p8D54W\n"
+ "K84zV2sxXs7LtkBoN79R9QIhAP////8AAAAA//////////+85vqtpxeehPO5ysL8\n"
+ "YyVRAgEBoUQDQgAEaG4WJuDAt0QkEM4t29KDUdzkQlMPGrqWzkWhgt9OGnwc6O7A\n"
+ "ZLPSrDyhwyrKS7XLRXml5DisQ93RvByll32y8A==\n";
+ const std::string privateKeyPkcs8 =
+ "MIIBwzA9BgkqhkiG9w0BBQ0wMDAbBgkqhkiG9w0BBQwwDgQIVHkBzLGtDvICAggA\n"
+ "MBEGBSsOAwIHBAhk6g9eI3toNwSCAYDd+LWPDBTrKV7vUyxTvDbpUd0eXfh73DKA\n"
+ "MHkdHuVmhpmpBbsF9XvaFuL8J/1xi1Yl2XGw8j3WyrprD2YEhl/+zKjNbdTDJmNO\n"
+ "SlomuwWb5AVCJ9reT94zIXKCnexUcyBFS7ep+P4dwuef0VjzprjfmnAZHrP+u594\n"
+ "ELHpKwi0ZpQLtcJjjud13bn43vbXb+aU7jmPV5lU2XP8TxaQJiYIibNEh1Y3TZGr\n"
+ "akJormYvhaYbiZkKLHQ9AvQMEjhoIW5WCB3q+tKZUKTzcQpjNnf9FOTeKN3jk3Kd\n"
+ "2OmibPZcbMJdgCD/nRVn1cBo7Hjn3IMjgtszQHtEUphOQiAkOJUnKmy9MTYqtcNN\n"
+ "6cuFItbu4QvbVwailgdUjOYwIJCmIxExlPV0ohS24pFGsO03Yn7W8rBB9VWENYmG\n"
+ "HkZIbGsHv7O9Wy7fv+FJgZkjeti0807IsNXSJl8LUK0ZIhAR7OU8uONWMsbHdQnk\n"
+ "q1HB1ZKa52ugACl7g/DF9b7CoSAjFeE=\n";
+ const std::string publicKeyPkcs8 =
+ "MIIBSzCCAQMGByqGSM49AgEwgfcCAQEwLAYHKoZIzj0BAQIhAP////8AAAABAAAA\n"
+ "AAAAAAAAAAAA////////////////MFsEIP////8AAAABAAAAAAAAAAAAAAAA////\n"
+ "///////////8BCBaxjXYqjqT57PrvVV2mIa8ZR0GsMxTsPY7zjw+J9JgSwMVAMSd\n"
+ "NgiG5wSTamZ44ROdJreBn36QBEEEaxfR8uEsQkf4vOblY6RA8ncDfYEt6zOg9KE5\n"
+ "RdiYwpZP40Li/hp/m47n60p8D54WK84zV2sxXs7LtkBoN79R9QIhAP////8AAAAA\n"
+ "//////////+85vqtpxeehPO5ysL8YyVRAgEBA0IABGhuFibgwLdEJBDOLdvSg1Hc\n"
+ "5EJTDxq6ls5FoYLfThp8HOjuwGSz0qw8ocMqyku1y0V5peQ4rEPd0bwcpZd9svA=\n";
+};
+
+using KeyTestDataSets = boost::mpl::vector<RsaKeyTestData, EcKeyTestData>;
+
+static void
+checkPkcs8Encoding(ConstBufferPtr encoding, const std::string& password, ConstBufferPtr pkcs1)
+{
+ PrivateKey sKey;
+ sKey.loadPkcs8(encoding->data(), encoding->size(), password.c_str(), password.size());
+ OBufferStream os;
+ sKey.savePkcs1(os);
+ BOOST_CHECK_EQUAL_COLLECTIONS(pkcs1->begin(), pkcs1->end(),
+ os.buf()->begin(), os.buf()->end());
+}
+
+static void
+checkPkcs8Base64Encoding(ConstBufferPtr encoding, const std::string& password, ConstBufferPtr pkcs1)
+{
+ OBufferStream os;
+ bufferSource(*encoding) >> base64Decode() >> streamSink(os);
+ checkPkcs8Encoding(os.buf(), password, pkcs1);
+}
+
+BOOST_AUTO_TEST_CASE_TEMPLATE(SaveLoad, T, KeyTestDataSets)
+{
+ T dataSet;
+
+ const uint8_t* sKeyPkcs1Base64 = reinterpret_cast<const uint8_t*>(dataSet.privateKeyPkcs1.c_str());
+ size_t sKeyPkcs1Base64Len = dataSet.privateKeyPkcs1.size();
+ OBufferStream os;
+ bufferSource(sKeyPkcs1Base64, sKeyPkcs1Base64Len) >> base64Decode() >> streamSink(os);
+ ConstBufferPtr sKeyPkcs1Buf = os.buf();
+ const uint8_t* sKeyPkcs1 = sKeyPkcs1Buf->data();
+ size_t sKeyPkcs1Len = sKeyPkcs1Buf->size();
+
+ // load key in base64-encoded pkcs1 format
+ PrivateKey sKey;
+ BOOST_CHECK_NO_THROW(sKey.loadPkcs1Base64(sKeyPkcs1Base64, sKeyPkcs1Base64Len));
+
+ std::stringstream ss2(dataSet.privateKeyPkcs1);
+ PrivateKey sKey2;
+ BOOST_CHECK_NO_THROW(sKey2.loadPkcs1Base64(ss2));
+
+ // load key in pkcs1 format
+ PrivateKey sKey3;
+ BOOST_CHECK_NO_THROW(sKey3.loadPkcs1(sKeyPkcs1, sKeyPkcs1Len));
+
+ std::stringstream ss4;
+ ss4.write(reinterpret_cast<const char*>(sKeyPkcs1), sKeyPkcs1Len);
+ PrivateKey sKey4;
+ BOOST_CHECK_NO_THROW(sKey4.loadPkcs1(ss4));
+
+ // save key in base64-encoded pkcs1 format
+ OBufferStream os2;
+ BOOST_REQUIRE_NO_THROW(sKey.savePkcs1Base64(os2));
+ BOOST_CHECK_EQUAL_COLLECTIONS(sKeyPkcs1Base64, sKeyPkcs1Base64 + sKeyPkcs1Base64Len,
+ os2.buf()->begin(), os2.buf()->end());
+
+ // save key in pkcs1 format
+ OBufferStream os3;
+ BOOST_REQUIRE_NO_THROW(sKey.savePkcs1(os3));
+ BOOST_CHECK_EQUAL_COLLECTIONS(sKeyPkcs1, sKeyPkcs1 + sKeyPkcs1Len,
+ os3.buf()->begin(), os3.buf()->end());
+
+ const uint8_t* sKeyPkcs8Base64 = reinterpret_cast<const uint8_t*>(dataSet.privateKeyPkcs8.c_str());
+ size_t sKeyPkcs8Base64Len = dataSet.privateKeyPkcs8.size();
+ OBufferStream os4;
+ bufferSource(sKeyPkcs8Base64, sKeyPkcs8Base64Len) >> base64Decode() >> streamSink(os4);
+ const uint8_t* sKeyPkcs8 = os4.buf()->data();
+ size_t sKeyPkcs8Len = os4.buf()->size();
+
+ std::string password("password");
+ std::string wrongpw("wrongpw");
+ auto pwCallback = [&password] (char* buf, size_t size, int) -> int {
+ BOOST_REQUIRE_LE(password.size(), size);
+ std::copy(password.begin(), password.end(), buf);
+ return static_cast<int>(password.size());
+ };
+
+ // load key in base64-encoded pkcs8 format
+ PrivateKey sKey5;
+ BOOST_CHECK_NO_THROW(sKey5.loadPkcs8Base64(sKeyPkcs8Base64, sKeyPkcs8Base64Len,
+ password.c_str(), password.size()));
+
+ PrivateKey sKey6;
+ BOOST_CHECK_NO_THROW(sKey6.loadPkcs8Base64(sKeyPkcs8Base64, sKeyPkcs8Base64Len, pwCallback));
+
+ std::stringstream ss7(dataSet.privateKeyPkcs8);
+ PrivateKey sKey7;
+ BOOST_CHECK_NO_THROW(sKey7.loadPkcs8Base64(ss7, password.c_str(), password.size()));
+
+ std::stringstream ss8(dataSet.privateKeyPkcs8);
+ PrivateKey sKey8;
+ BOOST_CHECK_NO_THROW(sKey8.loadPkcs8Base64(ss8, pwCallback));
+
+ // load key in pkcs8 format
+ PrivateKey sKey9;
+ BOOST_CHECK_NO_THROW(sKey9.loadPkcs8(sKeyPkcs8, sKeyPkcs8Len, password.c_str(), password.size()));
+
+ PrivateKey sKey10;
+ BOOST_CHECK_NO_THROW(sKey10.loadPkcs8(sKeyPkcs8, sKeyPkcs8Len, pwCallback));
+
+ std::stringstream ss11;
+ ss11.write(reinterpret_cast<const char*>(sKeyPkcs8), sKeyPkcs8Len);
+ PrivateKey sKey11;
+ BOOST_CHECK_NO_THROW(sKey11.loadPkcs8(ss11, password.c_str(), password.size()));
+
+ std::stringstream ss12;
+ ss12.write(reinterpret_cast<const char*>(sKeyPkcs8), sKeyPkcs8Len);
+ PrivateKey sKey12;
+ BOOST_CHECK_NO_THROW(sKey12.loadPkcs8(ss12, pwCallback));
+
+ // load key using wrong password, Error is expected
+ PrivateKey sKey13;
+ BOOST_CHECK_THROW(sKey13.loadPkcs8Base64(sKeyPkcs8Base64, sKeyPkcs8Base64Len, wrongpw.c_str(), wrongpw.size()),
+ PrivateKey::Error);
+
+ // save key in base64-encoded pkcs8 format
+ OBufferStream os14;
+ BOOST_REQUIRE_NO_THROW(sKey.savePkcs8Base64(os14, password.c_str(), password.size()));
+ checkPkcs8Base64Encoding(os14.buf(), password, sKeyPkcs1Buf);
+
+ OBufferStream os15;
+ BOOST_REQUIRE_NO_THROW(sKey.savePkcs8Base64(os15, pwCallback));
+ checkPkcs8Base64Encoding(os15.buf(), password, sKeyPkcs1Buf);
+
+ // save key in pkcs8 format
+ OBufferStream os16;
+ BOOST_REQUIRE_NO_THROW(sKey.savePkcs8(os16, password.c_str(), password.size()));
+ checkPkcs8Encoding(os16.buf(), password, sKeyPkcs1Buf);
+
+ OBufferStream os17;
+ BOOST_REQUIRE_NO_THROW(sKey.savePkcs8(os17, pwCallback));
+ checkPkcs8Encoding(os17.buf(), password, sKeyPkcs1Buf);
+}
+
+BOOST_AUTO_TEST_CASE_TEMPLATE(DerivePublicKey, T, KeyTestDataSets)
+{
+ T dataSet;
+
+ const uint8_t* sKeyPkcs1Base64 = reinterpret_cast<const uint8_t*>(dataSet.privateKeyPkcs1.c_str());
+ size_t sKeyPkcs1Base64Len = dataSet.privateKeyPkcs1.size();
+ PrivateKey sKey;
+ sKey.loadPkcs1Base64(sKeyPkcs1Base64, sKeyPkcs1Base64Len);
+
+ // derive public key and compare
+ ConstBufferPtr pKeyBits = sKey.derivePublicKey();
+ OBufferStream os;
+ bufferSource(dataSet.publicKeyPkcs8) >> base64Decode() >> streamSink(os);
+ BOOST_CHECK_EQUAL_COLLECTIONS(pKeyBits->begin(), pKeyBits->end(),
+ os.buf()->begin(), os.buf()->end());
+}
+
+BOOST_AUTO_TEST_CASE(RsaDecryption)
+{
+ RsaKeyTestData dataSet;
+
+ PrivateKey sKey;
+ sKey.loadPkcs1Base64(reinterpret_cast<const uint8_t*>(dataSet.privateKeyPkcs1.c_str()),
+ dataSet.privateKeyPkcs1.size());
+ BOOST_CHECK_EQUAL(sKey.getKeyType(), KeyType::RSA);
+
+ const uint8_t plainText[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07};
+
+ const std::string cipherTextBase64 =
+ "i2XNpZ2JbLa4JmBTdDrGmsd4/0C+p+BSCpW3MuPBNe5uChQ0eRO1dvjTnEqwSECY\n"
+ "38en9JZwcyb0It/TSFNXHlq+Z1ZpffnjIJxQR9HcgwvwQJh6WRH0vu38tvGkGuNv\n"
+ "60Rdn85hqSy1CikmXCeWXL9yCqeqcP21R94G/T3FuA+c1FtFko8KOzCwvrTXMO6n\n"
+ "5PNsqlLXabSGr+jz4EwOsSCgPkiDf9U6tXoSPRA2/YvqFQdaiUXIVlomESvaqqZ8\n"
+ "FxPs2BON0lobM8gT+xdzbRKofp+rNjNK+5uWyeOnXJwzCszh17cdJl2BH1dZwaVD\n"
+ "PmTiSdeDQXZ94U5boDQ4Aw==\n";
+ OBufferStream os;
+ bufferSource(cipherTextBase64) >> base64Decode() >> streamSink(os);
+
+ auto decrypted = sKey.decrypt(os.buf()->data(), os.buf()->size());
+ BOOST_CHECK_EQUAL_COLLECTIONS(plainText, plainText + sizeof(plainText),
+ decrypted->begin(), decrypted->end());
+}
+
+BOOST_AUTO_TEST_CASE(RsaEncryptDecrypt)
+{
+ RsaKeyTestData dataSet;
+
+ PublicKey pKey;
+ pKey.loadPkcs8Base64(reinterpret_cast<const uint8_t*>(dataSet.publicKeyPkcs8.c_str()),
+ dataSet.publicKeyPkcs8.size());
+ BOOST_CHECK_EQUAL(pKey.getKeyType(), KeyType::RSA);
+
+ PrivateKey sKey;
+ sKey.loadPkcs1Base64(reinterpret_cast<const uint8_t*>(dataSet.privateKeyPkcs1.c_str()),
+ dataSet.privateKeyPkcs1.size());
+ BOOST_CHECK_EQUAL(sKey.getKeyType(), KeyType::RSA);
+
+ const uint8_t plainText[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07};
+
+ auto cipherText = pKey.encrypt(plainText, sizeof(plainText));
+ auto decrypted = sKey.decrypt(cipherText->data(), cipherText->size());
+ BOOST_CHECK_EQUAL_COLLECTIONS(plainText, plainText + sizeof(plainText),
+ decrypted->begin(), decrypted->end());
+}
+
+BOOST_AUTO_TEST_CASE(UnsupportedEcDecryption)
+{
+ EcKeyTestData dataSet;
+
+ PrivateKey sKey;
+ sKey.loadPkcs1Base64(reinterpret_cast<const uint8_t*>(dataSet.privateKeyPkcs1.c_str()),
+ dataSet.privateKeyPkcs1.size());
+ BOOST_CHECK_EQUAL(sKey.getKeyType(), KeyType::EC);
+
+ OBufferStream os;
+ bufferSource("Y2lhbyFob2xhIWhlbGxvIQ==") >> base64Decode() >> streamSink(os);
+
+ BOOST_CHECK_THROW(sKey.decrypt(os.buf()->data(), os.buf()->size()), PrivateKey::Error);
+}
+
+using KeyParams = boost::mpl::vector<RsaKeyParams, EcKeyParams>;
+
+BOOST_AUTO_TEST_CASE_TEMPLATE(GenerateKey, T, KeyParams)
+{
+ unique_ptr<PrivateKey> sKey = generatePrivateKey(T());
+ PublicKey pKey;
+ ConstBufferPtr pKeyBits = sKey->derivePublicKey();
+ pKey.loadPkcs8(pKeyBits->data(), pKeyBits->size());
+
+ const uint8_t data[] = {0x01, 0x02, 0x03, 0x04};
+ OBufferStream os;
+ BOOST_REQUIRE_NO_THROW(bufferSource(data, sizeof(data)) >>
+ signerFilter(DigestAlgorithm::SHA256, *sKey) >>
+ streamSink(os));
+
+ ConstBufferPtr sig = os.buf();
+ bool result = false;
+ BOOST_REQUIRE_NO_THROW(bufferSource(data, sizeof(data)) >>
+ verifierFilter(DigestAlgorithm::SHA256, pKey, sig->data(), sig->size()) >>
+ boolSink(result));
+ BOOST_CHECK(result);
+
+ unique_ptr<PrivateKey> sKey2 = generatePrivateKey(T());
+
+ OBufferStream os1;
+ sKey->savePkcs1(os1);
+ ConstBufferPtr key1Pkcs1 = os1.buf();
+
+ OBufferStream os2;
+ sKey2->savePkcs1(os2);
+ ConstBufferPtr key2Pkcs1 = os2.buf();
+
+ BOOST_CHECK(*key1Pkcs1 != *key2Pkcs1);
+}
+
+BOOST_AUTO_TEST_CASE(UnsupportedKeyType)
+{
+ BOOST_CHECK_THROW(generatePrivateKey(AesKeyParams()), std::invalid_argument);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestPrivateKey
+BOOST_AUTO_TEST_SUITE_END() // Transform
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace transform
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/transform/public-key.t.cpp b/tests/unit/security/transform/public-key.t.cpp
new file mode 100644
index 0000000..f9d3304
--- /dev/null
+++ b/tests/unit/security/transform/public-key.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 "security/transform/public-key.hpp"
+
+#include "encoding/buffer-stream.hpp"
+#include "security/transform.hpp"
+
+#include "boost-test.hpp"
+#include <boost/mpl/vector.hpp>
+
+#include <sstream>
+
+namespace ndn {
+namespace security {
+namespace transform {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(Transform)
+BOOST_AUTO_TEST_SUITE(TestPublicKey)
+
+struct RsaKeyTestData
+{
+ const std::string publicKeyPkcs8 =
+ "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAw0WM1/WhAxyLtEqsiAJg\n"
+ "WDZWuzkYpeYVdeeZcqRZzzfRgBQTsNozS5t4HnwTZhwwXbH7k3QN0kRTV826Xobw\n"
+ "s3iigohnM9yTK+KKiayPhIAm/+5HGT6SgFJhYhqo1/upWdueojil6RP4/AgavHho\n"
+ "pxlAVbk6G9VdVnlQcQ5Zv0OcGi73c+EnYD/YgURYGSngUi/Ynsh779p2U69/te9g\n"
+ "ZwIL5PuE9BiO6I39cL9z7EK1SfZhOWvDe/qH7YhD/BHwcWit8FjRww1glwRVTJsA\n"
+ "9rH58ynaAix0tcR/nBMRLUX+e3rURHg6UbSjJbdb9qmKM1fTGHKUzL/5pMG6uBU0\n"
+ "ywIDAQAB\n";
+};
+
+struct EcKeyTestData
+{
+ const std::string publicKeyPkcs8 =
+ "MIIBSzCCAQMGByqGSM49AgEwgfcCAQEwLAYHKoZIzj0BAQIhAP////8AAAABAAAA\n"
+ "AAAAAAAAAAAA////////////////MFsEIP////8AAAABAAAAAAAAAAAAAAAA////\n"
+ "///////////8BCBaxjXYqjqT57PrvVV2mIa8ZR0GsMxTsPY7zjw+J9JgSwMVAMSd\n"
+ "NgiG5wSTamZ44ROdJreBn36QBEEEaxfR8uEsQkf4vOblY6RA8ncDfYEt6zOg9KE5\n"
+ "RdiYwpZP40Li/hp/m47n60p8D54WK84zV2sxXs7LtkBoN79R9QIhAP////8AAAAA\n"
+ "//////////+85vqtpxeehPO5ysL8YyVRAgEBA0IABGhuFibgwLdEJBDOLdvSg1Hc\n"
+ "5EJTDxq6ls5FoYLfThp8HOjuwGSz0qw8ocMqyku1y0V5peQ4rEPd0bwcpZd9svA=\n";
+};
+
+using KeyTestDataSets = boost::mpl::vector<RsaKeyTestData, EcKeyTestData>;
+
+BOOST_AUTO_TEST_CASE_TEMPLATE(SaveLoad, T, KeyTestDataSets)
+{
+ T dataSet;
+
+ const uint8_t* pKeyPkcs8Base64 = reinterpret_cast<const uint8_t*>(dataSet.publicKeyPkcs8.c_str());
+ size_t pKeyPkcs8Base64Len = dataSet.publicKeyPkcs8.size();
+ OBufferStream os;
+ bufferSource(pKeyPkcs8Base64, pKeyPkcs8Base64Len) >> base64Decode() >> streamSink(os);
+ ConstBufferPtr pKeyPkcs8Buf = os.buf();
+ const uint8_t* pKeyPkcs8 = pKeyPkcs8Buf->data();
+ size_t pKeyPkcs8Len = pKeyPkcs8Buf->size();
+
+ PublicKey pKey1;
+ BOOST_CHECK_NO_THROW(pKey1.loadPkcs8Base64(pKeyPkcs8Base64, pKeyPkcs8Base64Len));
+
+ std::stringstream ss2(dataSet.publicKeyPkcs8);
+ PublicKey pKey2;
+ BOOST_CHECK_NO_THROW(pKey2.loadPkcs8Base64(ss2));
+
+ PublicKey pKey3;
+ BOOST_CHECK_NO_THROW(pKey3.loadPkcs8(pKeyPkcs8, pKeyPkcs8Len));
+
+ std::stringstream ss4;
+ ss4.write(reinterpret_cast<const char*>(pKeyPkcs8), pKeyPkcs8Len);
+ PublicKey pKey4;
+ BOOST_CHECK_NO_THROW(pKey4.loadPkcs8(ss4));
+
+ OBufferStream os5;
+ BOOST_REQUIRE_NO_THROW(pKey1.savePkcs8Base64(os5));
+ BOOST_CHECK_EQUAL_COLLECTIONS(pKeyPkcs8Base64, pKeyPkcs8Base64 + pKeyPkcs8Base64Len,
+ os5.buf()->begin(), os5.buf()->end());
+
+ OBufferStream os6;
+ BOOST_REQUIRE_NO_THROW(pKey1.savePkcs8(os6));
+ BOOST_CHECK_EQUAL_COLLECTIONS(pKeyPkcs8, pKeyPkcs8 + pKeyPkcs8Len,
+ os6.buf()->begin(), os6.buf()->end());
+}
+
+// NOTE: We cannot test RSA encryption by comparing the computed ciphertext to
+// a known-good one, because OAEP padding is randomized and would produce
+// different results every time. An encrypt/decrypt round-trip test is
+// performed in private-key.t.cpp
+
+BOOST_AUTO_TEST_CASE(UnsupportedEcEncryption)
+{
+ EcKeyTestData dataSet;
+
+ PublicKey pKey;
+ pKey.loadPkcs8Base64(reinterpret_cast<const uint8_t*>(dataSet.publicKeyPkcs8.c_str()),
+ dataSet.publicKeyPkcs8.size());
+ BOOST_CHECK_EQUAL(pKey.getKeyType(), KeyType::EC);
+
+ OBufferStream os;
+ bufferSource("Y2lhbyFob2xhIWhlbGxvIQ==") >> base64Decode() >> streamSink(os);
+
+ BOOST_CHECK_THROW(pKey.encrypt(os.buf()->data(), os.buf()->size()), PublicKey::Error);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestPublicKey
+BOOST_AUTO_TEST_SUITE_END() // Transform
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace transform
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/transform/signer-filter.t.cpp b/tests/unit/security/transform/signer-filter.t.cpp
new file mode 100644
index 0000000..51a98f0
--- /dev/null
+++ b/tests/unit/security/transform/signer-filter.t.cpp
@@ -0,0 +1,146 @@
+/* -*- 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 "security/transform/signer-filter.hpp"
+
+#include "encoding/buffer-stream.hpp"
+#include "security/transform/base64-decode.hpp"
+#include "security/transform/buffer-source.hpp"
+#include "security/transform/private-key.hpp"
+#include "security/transform/stream-sink.hpp"
+#include "security/verification-helpers.hpp"
+
+#include "boost-test.hpp"
+
+namespace ndn {
+namespace security {
+namespace transform {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(Transform)
+BOOST_AUTO_TEST_SUITE(TestSignerFilter)
+
+BOOST_AUTO_TEST_CASE(Rsa)
+{
+ const std::string publicKeyPkcs8 =
+ "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAw0WM1/WhAxyLtEqsiAJg\n"
+ "WDZWuzkYpeYVdeeZcqRZzzfRgBQTsNozS5t4HnwTZhwwXbH7k3QN0kRTV826Xobw\n"
+ "s3iigohnM9yTK+KKiayPhIAm/+5HGT6SgFJhYhqo1/upWdueojil6RP4/AgavHho\n"
+ "pxlAVbk6G9VdVnlQcQ5Zv0OcGi73c+EnYD/YgURYGSngUi/Ynsh779p2U69/te9g\n"
+ "ZwIL5PuE9BiO6I39cL9z7EK1SfZhOWvDe/qH7YhD/BHwcWit8FjRww1glwRVTJsA\n"
+ "9rH58ynaAix0tcR/nBMRLUX+e3rURHg6UbSjJbdb9qmKM1fTGHKUzL/5pMG6uBU0\n"
+ "ywIDAQAB\n";
+ const std::string privateKeyPkcs1 =
+ "MIIEpAIBAAKCAQEAw0WM1/WhAxyLtEqsiAJgWDZWuzkYpeYVdeeZcqRZzzfRgBQT\n"
+ "sNozS5t4HnwTZhwwXbH7k3QN0kRTV826Xobws3iigohnM9yTK+KKiayPhIAm/+5H\n"
+ "GT6SgFJhYhqo1/upWdueojil6RP4/AgavHhopxlAVbk6G9VdVnlQcQ5Zv0OcGi73\n"
+ "c+EnYD/YgURYGSngUi/Ynsh779p2U69/te9gZwIL5PuE9BiO6I39cL9z7EK1SfZh\n"
+ "OWvDe/qH7YhD/BHwcWit8FjRww1glwRVTJsA9rH58ynaAix0tcR/nBMRLUX+e3rU\n"
+ "RHg6UbSjJbdb9qmKM1fTGHKUzL/5pMG6uBU0ywIDAQABAoIBADQkckOIl4IZMUTn\n"
+ "W8LFv6xOdkJwMKC8G6bsPRFbyY+HvC2TLt7epSvfS+f4AcYWaOPcDu2E49vt2sNr\n"
+ "cASly8hgwiRRAB3dHH9vcsboiTo8bi2RFvMqvjv9w3tK2yMxVDtmZamzrrnaV3YV\n"
+ "Q+5nyKo2F/PMDjQ4eUAKDOzjhBuKHsZBTFnA1MFNI+UKj5X4Yp64DFmKlxTX/U2b\n"
+ "wzVywo5hzx2Uhw51jmoLls4YUvMJXD0wW5ZtYRuPogXvXb/of9ef/20/wU11WFKg\n"
+ "Xb4gfR8zUXaXS1sXcnVm3+24vIs9dApUwykuoyjOqxWqcHRec2QT2FxVGkFEraze\n"
+ "CPa4rMECgYEA5Y8CywomIcTgerFGFCeMHJr8nQGqY2V/owFb3k9maczPnC9p4a9R\n"
+ "c5szLxA9FMYFxurQZMBWSEG2JS1HR2mnjigx8UKjYML/A+rvvjZOMe4M6Sy2ggh4\n"
+ "SkLZKpWTzjTe07ByM/j5v/SjNZhWAG7sw4/LmPGRQkwJv+KZhGojuOkCgYEA2cOF\n"
+ "T6cJRv6kvzTz9S0COZOVm+euJh/BXp7oAsAmbNfOpckPMzqHXy8/wpdKl6AAcB57\n"
+ "OuztlNfV1D7qvbz7JuRlYwQ0cEfBgbZPcz1p18HHDXhwn57ZPb8G33Yh9Omg0HNA\n"
+ "Imb4LsVuSqxA6NwSj7cpRekgTedrhLFPJ+Ydb5MCgYEAsM3Q7OjILcIg0t6uht9e\n"
+ "vrlwTsz1mtCV2co2I6crzdj9HeI2vqf1KAElDt6G7PUHhglcr/yjd8uEqmWRPKNX\n"
+ "ddnnfVZB10jYeP/93pac6z/Zmc3iU4yKeUe7U10ZFf0KkiiYDQd59CpLef/2XScS\n"
+ "HB0oRofnxRQjfjLc4muNT+ECgYEAlcDk06MOOTly+F8lCc1bA1dgAmgwFd2usDBd\n"
+ "Y07a3e0HGnGLN3Kfl7C5i0tZq64HvxLnMd2vgLVxQlXGPpdQrC1TH+XLXg+qnlZO\n"
+ "ivSH7i0/gx75bHvj75eH1XK65V8pDVDEoSPottllAIs21CxLw3N1ObOZWJm2EfmR\n"
+ "cuHICmsCgYAtFJ1idqMoHxES3mlRpf2JxyQudP3SCm2WpGmqVzhRYInqeatY5sUd\n"
+ "lPLHm/p77RT7EyxQHTlwn8FJPuM/4ZH1rQd/vB+Y8qAtYJCexDMsbvLW+Js+VOvk\n"
+ "jweEC0nrcL31j9mF0vz5E6tfRu4hhJ6L4yfWs0gSejskeVB/w8QY4g==\n";
+ const uint8_t data[] = {0x01, 0x02, 0x03, 0x04};
+
+ OBufferStream os1;
+ bufferSource(publicKeyPkcs8) >> base64Decode() >> streamSink(os1);
+ auto pubKey = os1.buf();
+
+ PrivateKey sKey;
+ sKey.loadPkcs1Base64(reinterpret_cast<const uint8_t*>(privateKeyPkcs1.data()), privateKeyPkcs1.size());
+
+ BOOST_CHECK_THROW(SignerFilter(DigestAlgorithm::NONE, sKey), Error);
+
+ OBufferStream os2;
+ bufferSource(data, sizeof(data)) >> signerFilter(DigestAlgorithm::SHA256, sKey) >> streamSink(os2);
+ auto sig = os2.buf();
+
+ BOOST_CHECK(verifySignature(data, sizeof(data), sig->data(), sig->size(), pubKey->data(), pubKey->size()));
+}
+
+BOOST_AUTO_TEST_CASE(Ecdsa)
+{
+ const std::string privateKeyPkcs1 =
+ "MIIBaAIBAQQgRxwcbzK9RV6AHYFsDcykI86o3M/a1KlJn0z8PcLMBZOggfowgfcC\n"
+ "AQEwLAYHKoZIzj0BAQIhAP////8AAAABAAAAAAAAAAAAAAAA////////////////\n"
+ "MFsEIP////8AAAABAAAAAAAAAAAAAAAA///////////////8BCBaxjXYqjqT57Pr\n"
+ "vVV2mIa8ZR0GsMxTsPY7zjw+J9JgSwMVAMSdNgiG5wSTamZ44ROdJreBn36QBEEE\n"
+ "axfR8uEsQkf4vOblY6RA8ncDfYEt6zOg9KE5RdiYwpZP40Li/hp/m47n60p8D54W\n"
+ "K84zV2sxXs7LtkBoN79R9QIhAP////8AAAAA//////////+85vqtpxeehPO5ysL8\n"
+ "YyVRAgEBoUQDQgAEaG4WJuDAt0QkEM4t29KDUdzkQlMPGrqWzkWhgt9OGnwc6O7A\n"
+ "ZLPSrDyhwyrKS7XLRXml5DisQ93RvByll32y8A==\n";
+ const std::string publicKeyPkcs8 =
+ "MIIBSzCCAQMGByqGSM49AgEwgfcCAQEwLAYHKoZIzj0BAQIhAP////8AAAABAAAA\n"
+ "AAAAAAAAAAAA////////////////MFsEIP////8AAAABAAAAAAAAAAAAAAAA////\n"
+ "///////////8BCBaxjXYqjqT57PrvVV2mIa8ZR0GsMxTsPY7zjw+J9JgSwMVAMSd\n"
+ "NgiG5wSTamZ44ROdJreBn36QBEEEaxfR8uEsQkf4vOblY6RA8ncDfYEt6zOg9KE5\n"
+ "RdiYwpZP40Li/hp/m47n60p8D54WK84zV2sxXs7LtkBoN79R9QIhAP////8AAAAA\n"
+ "//////////+85vqtpxeehPO5ysL8YyVRAgEBA0IABGhuFibgwLdEJBDOLdvSg1Hc\n"
+ "5EJTDxq6ls5FoYLfThp8HOjuwGSz0qw8ocMqyku1y0V5peQ4rEPd0bwcpZd9svA=\n";
+ const uint8_t data[] = {0x01, 0x02, 0x03, 0x04};
+
+ OBufferStream os1;
+ bufferSource(publicKeyPkcs8) >> base64Decode() >> streamSink(os1);
+ auto pubKey = os1.buf();
+
+ PrivateKey sKey;
+ sKey.loadPkcs1Base64(reinterpret_cast<const uint8_t*>(privateKeyPkcs1.data()), privateKeyPkcs1.size());
+
+ BOOST_CHECK_THROW(SignerFilter(DigestAlgorithm::NONE, sKey), Error);
+
+ OBufferStream os2;
+ bufferSource(data, sizeof(data)) >> signerFilter(DigestAlgorithm::SHA256, sKey) >> streamSink(os2);
+ auto sig = os2.buf();
+
+ BOOST_CHECK(verifySignature(data, sizeof(data), sig->data(), sig->size(), pubKey->data(), pubKey->size()));
+}
+
+BOOST_AUTO_TEST_CASE(InvalidKey)
+{
+ PrivateKey sKey;
+ BOOST_CHECK_THROW(SignerFilter(DigestAlgorithm::SHA256, sKey), Error);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestSignerFilter
+BOOST_AUTO_TEST_SUITE_END() // Transform
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace transform
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/transform/step-source.t.cpp b/tests/unit/security/transform/step-source.t.cpp
new file mode 100644
index 0000000..43c6655
--- /dev/null
+++ b/tests/unit/security/transform/step-source.t.cpp
@@ -0,0 +1,91 @@
+/* -*- 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 "security/transform/step-source.hpp"
+#include "security/transform/stream-sink.hpp"
+
+#include "boost-test.hpp"
+
+#include <sstream>
+
+namespace ndn {
+namespace security {
+namespace transform {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(Transform)
+BOOST_AUTO_TEST_SUITE(TestStepSource)
+
+BOOST_AUTO_TEST_CASE(Basic)
+{
+ const std::string input =
+ "0123456701234567012345670123456701234567012345670123456701234567"
+ "0123456701234567012345670123456701234567012345670123456701234567"
+ "0123456701234567012345670123456701234567012345670123456701234567"
+ "0123456701234567012345670123456701234567012345670123456701234567"
+ "0123456701234567012345670123456701234567012345670123456701234567"
+ "0123456701234567012345670123456701234567012345670123456701234567"
+ "0123456701234567012345670123456701234567012345670123456701234567"
+ "0123456701234567012345670123456701234567012345670123456701234567"
+ "0123456701234567012345670123456701234567012345670123456701234567"
+ "0123456701234567012345670123456701234567012345670123456701234567"
+ "0123456701234567012345670123456701234567012345670123456701234567"
+ "0123456701234567012345670123456701234567012345670123456701234567"
+ "0123456701234567012345670123456701234567012345670123456701234567"
+ "0123456701234567012345670123456701234567012345670123456701234567"
+ "0123456701234567012345670123456701234567012345670123456701234567"
+ "0123456701234567012345670123456701234567012345670123456701234567"
+ "0123456701234567012345670123456701234567012345670123456701234567"
+ "0123456701234567012345670123456701234567012345670123456701234567"
+ "0123456701234567012345670123456701234567012345670123456701234567"
+ "0123456701234567012345670123456701234567012345670123456701234567";
+ const uint8_t* buf = reinterpret_cast<const uint8_t*>(input.data());
+
+ std::ostringstream os;
+ StepSource ss;
+ ss >> streamSink(os);
+ BOOST_CHECK_EQUAL(ss.write(buf, 320), 320);
+ BOOST_CHECK_EQUAL(ss.write(buf + 320, 320), 320);
+ BOOST_CHECK_EQUAL(ss.write(buf + 640, 320), 320);
+ BOOST_CHECK_EQUAL(ss.write(buf + 960, 320), 320);
+ ss.end();
+ BOOST_CHECK_THROW(ss.write(buf + 960, 320), transform::Error);
+ BOOST_CHECK_EQUAL(os.str(), input);
+}
+
+BOOST_AUTO_TEST_CASE(EmptyInput)
+{
+ std::ostringstream os;
+ StepSource ss;
+ ss >> streamSink(os);
+ ss.end();
+ BOOST_CHECK_EQUAL(os.str(), "");
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestStepSource
+BOOST_AUTO_TEST_SUITE_END() // Transform
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace transform
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/transform/stream-sink.t.cpp b/tests/unit/security/transform/stream-sink.t.cpp
new file mode 100644
index 0000000..753ea35
--- /dev/null
+++ b/tests/unit/security/transform/stream-sink.t.cpp
@@ -0,0 +1,60 @@
+/* -*- 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 "security/transform/stream-sink.hpp"
+
+#include "boost-test.hpp"
+
+namespace ndn {
+namespace security {
+namespace transform {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(Transform)
+BOOST_AUTO_TEST_SUITE(TestStreamSink)
+
+BOOST_AUTO_TEST_CASE(Basic)
+{
+ uint8_t in[] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x20, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f
+ };
+ std::ostringstream os;
+ StreamSink sink(os);
+ BOOST_CHECK_EQUAL(sink.write(in, 4), 4);
+ BOOST_CHECK_EQUAL(sink.write(in + 4, 4), 4);
+ BOOST_CHECK_EQUAL(sink.write(in + 8, 4), 4);
+ BOOST_CHECK_EQUAL(sink.write(in + 12, 4), 4);
+ sink.end();
+ std::string out = os.str();
+ BOOST_CHECK_EQUAL_COLLECTIONS(in, in + sizeof(in), out.begin(), out.end());
+ BOOST_CHECK_THROW(sink.write(in + 8, 8), transform::Error);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestStreamSink
+BOOST_AUTO_TEST_SUITE_END() // Transform
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace transform
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/transform/stream-source.t.cpp b/tests/unit/security/transform/stream-source.t.cpp
new file mode 100644
index 0000000..ae385b1
--- /dev/null
+++ b/tests/unit/security/transform/stream-source.t.cpp
@@ -0,0 +1,99 @@
+/* -*- 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 "security/transform/stream-source.hpp"
+#include "security/transform/stream-sink.hpp"
+
+#include "boost-test.hpp"
+#include <boost/mpl/integral_c.hpp>
+#include <boost/mpl/vector.hpp>
+
+namespace ndn {
+namespace security {
+namespace transform {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(Transform)
+BOOST_AUTO_TEST_SUITE(TestStreamSource)
+
+BOOST_AUTO_TEST_CASE(Basic)
+{
+ std::string input =
+ "0123456701234567012345670123456701234567012345670123456701234567"
+ "01234567012345670123456701234567 1234567012345670123456701234567"
+ "0123456701234567012345670123456701234567012345670123456701234567"
+ "0123456701234567012345670123456701234567012345670123456701234567"
+ "0123456701234567012345670123456701234567012345670123456701234567"
+ "0123456701234567012345670123456701234567012345670123456701234567"
+ "0123456701234567012345670123456701234567012345670123456701234567"
+ "0123456701234567012345670123456701234567012345670123456701234567"
+ "0123456701234567012345670123456701234567012345670123456701234567"
+ "0123456701234567012345670123456701234567012345670123456701234567"
+ "0123456701234567012345670123456701234567012345670123456701234567"
+ "0123456701234567012345670123456701234567012345670123456701234567"
+ "0123456701234567012345670123456701234567012345670123456701234567"
+ "0123456701234567012345670123456701234567012345670123456701234567"
+ "0123456701234567012345670123456701234567012345670123456701234567"
+ "0123456701234567012345670123456701234567012345670123456701234567"
+ "0123456701234567012345670123456701234567012345670123456701234567"
+ "0123456701234567012345670123456701234567012345670123456701234567"
+ "0123456701234567012345670123456701234567012345670123456701234567"
+ "0123456701234567012345670123456701234567012345670123456701234567";
+
+ std::stringstream is(input);
+ std::stringstream os;
+ streamSource(is) >> streamSink(os);
+ std::string output = os.str();
+
+ BOOST_CHECK_EQUAL(input, output);
+}
+
+BOOST_AUTO_TEST_CASE(EmptyStream)
+{
+ std::stringstream is;
+ std::stringstream os;
+ streamSource(is) >> streamSink(os);
+
+ BOOST_CHECK_EQUAL(os.str(), "");
+}
+
+typedef boost::mpl::vector<
+ boost::mpl::integral_c<std::ios_base::iostate, std::ios_base::badbit>,
+ boost::mpl::integral_c<std::ios_base::iostate, std::ios_base::failbit>
+> BadBits;
+
+BOOST_AUTO_TEST_CASE_TEMPLATE(BadStream, BadBit, BadBits)
+{
+ std::stringstream is;
+ is.setstate(BadBit::value);
+ std::stringstream os;
+ BOOST_CHECK_THROW(streamSource(is) >> streamSink(os), transform::Error);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestStreamSource
+BOOST_AUTO_TEST_SUITE_END() // Transform
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace transform
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/transform/strip-space.t.cpp b/tests/unit/security/transform/strip-space.t.cpp
new file mode 100644
index 0000000..db8fa84
--- /dev/null
+++ b/tests/unit/security/transform/strip-space.t.cpp
@@ -0,0 +1,77 @@
+/* -*- 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 "security/transform/strip-space.hpp"
+#include "security/transform/step-source.hpp"
+#include "security/transform/stream-sink.hpp"
+#include "encoding/buffer-stream.hpp"
+
+#include "boost-test.hpp"
+
+namespace ndn {
+namespace security {
+namespace transform {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(Transform)
+BOOST_AUTO_TEST_SUITE(TestStripSpace)
+
+BOOST_AUTO_TEST_CASE(Basic)
+{
+ const char* input = R"STR(
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor
+ incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud
+ exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute
+ irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
+ pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia
+ deserunt mollit anim id est laborum.
+ )STR";
+ size_t inputLen = strlen(input);
+
+ OBufferStream os;
+ StepSource source;
+ source >> stripSpace() >> streamSink(os);
+
+ for (size_t offset = 0; offset < inputLen; offset += 40) {
+ source.write(reinterpret_cast<const uint8_t*>(input + offset),
+ std::min<size_t>(40, inputLen - offset));
+ }
+ source.end();
+
+ std::string expected(
+ "Loremipsumdolorsitamet,consecteturadipiscingelit,seddoeiusmodtemporincididuntutl"
+ "aboreetdoloremagnaaliqua.Utenimadminimveniam,quisnostrudexercitationullamcolabor"
+ "isnisiutaliquipexeacommodoconsequat.Duisauteiruredolorinreprehenderitinvoluptate"
+ "velitessecillumdoloreeufugiatnullapariatur.Excepteursintoccaecatcupidatatnonproi"
+ "dent,suntinculpaquiofficiadeseruntmollitanimidestlaborum.");
+ ConstBufferPtr buf = os.buf();
+ BOOST_CHECK_EQUAL(std::string(buf->get<char>(), buf->size()), expected);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestStripSpace
+BOOST_AUTO_TEST_SUITE_END() // Transform
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace transform
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/transform/verifier-filter.t.cpp b/tests/unit/security/transform/verifier-filter.t.cpp
new file mode 100644
index 0000000..1b74690
--- /dev/null
+++ b/tests/unit/security/transform/verifier-filter.t.cpp
@@ -0,0 +1,165 @@
+/* -*- 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 "security/transform/verifier-filter.hpp"
+
+#include "encoding/buffer-stream.hpp"
+#include "security/transform/base64-decode.hpp"
+#include "security/transform/bool-sink.hpp"
+#include "security/transform/buffer-source.hpp"
+#include "security/transform/private-key.hpp"
+#include "security/transform/public-key.hpp"
+#include "security/transform/signer-filter.hpp"
+#include "security/transform/stream-sink.hpp"
+
+#include "boost-test.hpp"
+
+namespace ndn {
+namespace security {
+namespace transform {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(Transform)
+BOOST_AUTO_TEST_SUITE(TestVerifierFilter)
+
+BOOST_AUTO_TEST_CASE(Rsa)
+{
+ const std::string publicKeyPkcs8 =
+ "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAw0WM1/WhAxyLtEqsiAJg\n"
+ "WDZWuzkYpeYVdeeZcqRZzzfRgBQTsNozS5t4HnwTZhwwXbH7k3QN0kRTV826Xobw\n"
+ "s3iigohnM9yTK+KKiayPhIAm/+5HGT6SgFJhYhqo1/upWdueojil6RP4/AgavHho\n"
+ "pxlAVbk6G9VdVnlQcQ5Zv0OcGi73c+EnYD/YgURYGSngUi/Ynsh779p2U69/te9g\n"
+ "ZwIL5PuE9BiO6I39cL9z7EK1SfZhOWvDe/qH7YhD/BHwcWit8FjRww1glwRVTJsA\n"
+ "9rH58ynaAix0tcR/nBMRLUX+e3rURHg6UbSjJbdb9qmKM1fTGHKUzL/5pMG6uBU0\n"
+ "ywIDAQAB\n";
+ const std::string privateKeyPkcs1 =
+ "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDDRYzX9aEDHIu0\n"
+ "SqyIAmBYNla7ORil5hV155lypFnPN9GAFBOw2jNLm3gefBNmHDBdsfuTdA3SRFNX\n"
+ "zbpehvCzeKKCiGcz3JMr4oqJrI+EgCb/7kcZPpKAUmFiGqjX+6lZ256iOKXpE/j8\n"
+ "CBq8eGinGUBVuTob1V1WeVBxDlm/Q5waLvdz4SdgP9iBRFgZKeBSL9ieyHvv2nZT\n"
+ "r3+172BnAgvk+4T0GI7ojf1wv3PsQrVJ9mE5a8N7+oftiEP8EfBxaK3wWNHDDWCX\n"
+ "BFVMmwD2sfnzKdoCLHS1xH+cExEtRf57etREeDpRtKMlt1v2qYozV9MYcpTMv/mk\n"
+ "wbq4FTTLAgMBAAECggEANCRyQ4iXghkxROdbwsW/rE52QnAwoLwbpuw9EVvJj4e8\n"
+ "LZMu3t6lK99L5/gBxhZo49wO7YTj2+3aw2twBKXLyGDCJFEAHd0cf29yxuiJOjxu\n"
+ "LZEW8yq+O/3De0rbIzFUO2ZlqbOuudpXdhVD7mfIqjYX88wONDh5QAoM7OOEG4oe\n"
+ "xkFMWcDUwU0j5QqPlfhinrgMWYqXFNf9TZvDNXLCjmHPHZSHDnWOaguWzhhS8wlc\n"
+ "PTBblm1hG4+iBe9dv+h/15//bT/BTXVYUqBdviB9HzNRdpdLWxdydWbf7bi8iz10\n"
+ "ClTDKS6jKM6rFapwdF5zZBPYXFUaQUStrN4I9riswQKBgQDljwLLCiYhxOB6sUYU\n"
+ "J4wcmvydAapjZX+jAVveT2ZpzM+cL2nhr1FzmzMvED0UxgXG6tBkwFZIQbYlLUdH\n"
+ "aaeOKDHxQqNgwv8D6u++Nk4x7gzpLLaCCHhKQtkqlZPONN7TsHIz+Pm/9KM1mFYA\n"
+ "buzDj8uY8ZFCTAm/4pmEaiO46QKBgQDZw4VPpwlG/qS/NPP1LQI5k5Wb564mH8Fe\n"
+ "nugCwCZs186lyQ8zOodfLz/Cl0qXoABwHns67O2U19XUPuq9vPsm5GVjBDRwR8GB\n"
+ "tk9zPWnXwccNeHCfntk9vwbfdiH06aDQc0AiZvguxW5KrEDo3BKPtylF6SBN52uE\n"
+ "sU8n5h1vkwKBgQCwzdDs6MgtwiDS3q6G316+uXBOzPWa0JXZyjYjpyvN2P0d4ja+\n"
+ "p/UoASUO3obs9QeGCVyv/KN3y4SqZZE8o1d12ed9VkHXSNh4//3elpzrP9mZzeJT\n"
+ "jIp5R7tTXRkV/QqSKJgNB3n0Kkt5//ZdJxIcHShGh+fFFCN+Mtzia41P4QKBgQCV\n"
+ "wOTTow45OXL4XyUJzVsDV2ACaDAV3a6wMF1jTtrd7QcacYs3cp+XsLmLS1mrrge/\n"
+ "Eucx3a+AtXFCVcY+l1CsLVMf5cteD6qeVk6K9IfuLT+DHvlse+Pvl4fVcrrlXykN\n"
+ "UMShI+i22WUAizbULEvDc3U5s5lYmbYR+ZFy4cgKawKBgC0UnWJ2oygfERLeaVGl\n"
+ "/YnHJC50/dIKbZakaapXOFFgiep5q1jmxR2U8seb+nvtFPsTLFAdOXCfwUk+4z/h\n"
+ "kfWtB3+8H5jyoC1gkJ7EMyxu8tb4mz5U6+SPB4QLSetwvfWP2YXS/PkTq19G7iGE\n"
+ "novjJ9azSBJ6OyR5UH/DxBji\n";
+ const uint8_t data[] = {0x01, 0x02, 0x03, 0x04};
+
+ OBufferStream os1;
+ bufferSource(publicKeyPkcs8) >> base64Decode() >> streamSink(os1);
+ auto pubKey = os1.buf();
+
+ PublicKey pKey;
+ pKey.loadPkcs8(pubKey->data(), pubKey->size());
+
+ PrivateKey sKey;
+ sKey.loadPkcs1Base64(reinterpret_cast<const uint8_t*>(privateKeyPkcs1.data()), privateKeyPkcs1.size());
+
+ OBufferStream os2;
+ bufferSource(data, sizeof(data)) >> signerFilter(DigestAlgorithm::SHA256, sKey) >> streamSink(os2);
+ auto sig = os2.buf();
+
+ BOOST_CHECK_THROW(VerifierFilter(DigestAlgorithm::NONE, pKey, sig->data(), sig->size()), Error);
+
+ bool result = false;
+ bufferSource(data, sizeof(data)) >>
+ verifierFilter(DigestAlgorithm::SHA256, pKey, sig->data(), sig->size()) >>
+ boolSink(result);
+
+ BOOST_CHECK_EQUAL(result, true);
+}
+
+BOOST_AUTO_TEST_CASE(Ecdsa)
+{
+ const std::string privateKeyPkcs1 =
+ "MIIBeQIBADCCAQMGByqGSM49AgEwgfcCAQEwLAYHKoZIzj0BAQIhAP////8AAAAB\n"
+ "AAAAAAAAAAAAAAAA////////////////MFsEIP////8AAAABAAAAAAAAAAAAAAAA\n"
+ "///////////////8BCBaxjXYqjqT57PrvVV2mIa8ZR0GsMxTsPY7zjw+J9JgSwMV\n"
+ "AMSdNgiG5wSTamZ44ROdJreBn36QBEEEaxfR8uEsQkf4vOblY6RA8ncDfYEt6zOg\n"
+ "9KE5RdiYwpZP40Li/hp/m47n60p8D54WK84zV2sxXs7LtkBoN79R9QIhAP////8A\n"
+ "AAAA//////////+85vqtpxeehPO5ysL8YyVRAgEBBG0wawIBAQQgRxwcbzK9RV6A\n"
+ "HYFsDcykI86o3M/a1KlJn0z8PcLMBZOhRANCAARobhYm4MC3RCQQzi3b0oNR3ORC\n"
+ "Uw8aupbORaGC304afBzo7sBks9KsPKHDKspLtctFeaXkOKxD3dG8HKWXfbLw\n";
+ const std::string publicKeyPkcs8 =
+ "MIIBSzCCAQMGByqGSM49AgEwgfcCAQEwLAYHKoZIzj0BAQIhAP////8AAAABAAAA\n"
+ "AAAAAAAAAAAA////////////////MFsEIP////8AAAABAAAAAAAAAAAAAAAA////\n"
+ "///////////8BCBaxjXYqjqT57PrvVV2mIa8ZR0GsMxTsPY7zjw+J9JgSwMVAMSd\n"
+ "NgiG5wSTamZ44ROdJreBn36QBEEEaxfR8uEsQkf4vOblY6RA8ncDfYEt6zOg9KE5\n"
+ "RdiYwpZP40Li/hp/m47n60p8D54WK84zV2sxXs7LtkBoN79R9QIhAP////8AAAAA\n"
+ "//////////+85vqtpxeehPO5ysL8YyVRAgEBA0IABGhuFibgwLdEJBDOLdvSg1Hc\n"
+ "5EJTDxq6ls5FoYLfThp8HOjuwGSz0qw8ocMqyku1y0V5peQ4rEPd0bwcpZd9svA=\n";
+ const uint8_t data[] = {0x01, 0x02, 0x03, 0x04};
+
+ OBufferStream os1;
+ bufferSource(publicKeyPkcs8) >> base64Decode() >> streamSink(os1);
+ auto pubKey = os1.buf();
+
+ PublicKey pKey;
+ pKey.loadPkcs8(pubKey->data(), pubKey->size());
+
+ PrivateKey sKey;
+ sKey.loadPkcs1Base64(reinterpret_cast<const uint8_t*>(privateKeyPkcs1.data()), privateKeyPkcs1.size());
+
+ OBufferStream os2;
+ bufferSource(data, sizeof(data)) >> signerFilter(DigestAlgorithm::SHA256, sKey) >> streamSink(os2);
+ auto sig = os2.buf();
+
+ BOOST_CHECK_THROW(VerifierFilter(DigestAlgorithm::NONE, pKey, sig->data(), sig->size()), Error);
+
+ bool result = false;
+ bufferSource(data, sizeof(data)) >>
+ verifierFilter(DigestAlgorithm::SHA256, pKey, sig->data(), sig->size()) >>
+ boolSink(result);
+
+ BOOST_CHECK_EQUAL(result, true);
+}
+
+BOOST_AUTO_TEST_CASE(InvalidKey)
+{
+ PublicKey pKey;
+ BOOST_CHECK_THROW(VerifierFilter(DigestAlgorithm::SHA256, pKey, nullptr, 0), Error);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestVerifierFilter
+BOOST_AUTO_TEST_SUITE_END() // Transform
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace transform
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/v2/additional-description.t.cpp b/tests/unit/security/v2/additional-description.t.cpp
new file mode 100644
index 0000000..7ceee8c
--- /dev/null
+++ b/tests/unit/security/v2/additional-description.t.cpp
@@ -0,0 +1,103 @@
+/* -*- 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 "security/v2/additional-description.hpp"
+
+#include "boost-test.hpp"
+
+namespace ndn {
+namespace security {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(TestAdditionalDescription)
+
+const uint8_t description[] = {
+ 0xfd, 0x01, 0x02, 0x28,
+ 0xfd, 0x02, 0x00, 0x10, // DescriptionEntry
+ 0xfd, 0x02, 0x01, 0x04, // DescriptionKey
+ 0x6b, 0x65, 0x79, 0x31, // "key1"
+ 0xfd, 0x02, 0x02, 0x04, // DescriptionValue
+ 0x76, 0x61, 0x6c, 0x31, // "val1"
+ 0xfd, 0x02, 0x00, 0x10, // DescriptionEntry
+ 0xfd, 0x02, 0x01, 0x04, // DescriptionKey
+ 0x6b, 0x65, 0x79, 0x32, // "key2"
+ 0xfd, 0x02, 0x02, 0x04, // DescriptionValue
+ 0x76, 0x61, 0x6c, 0x32, // "val2"
+};
+
+const std::string text = "((key1:val1), (key2:val2))";
+
+BOOST_AUTO_TEST_CASE(Basic)
+{
+ AdditionalDescription aDescription;
+
+ aDescription.set("key2", "val2");
+ aDescription.set("key1", "val1");
+
+ BOOST_REQUIRE_NO_THROW(aDescription.get("key1"));
+ BOOST_REQUIRE_NO_THROW(aDescription.get("key2"));
+ BOOST_REQUIRE_THROW(aDescription.get("key3"), AdditionalDescription::Error);
+
+ BOOST_CHECK_EQUAL(aDescription.has("key1"), true);
+ BOOST_CHECK_EQUAL(aDescription.has("key2"), true);
+ BOOST_CHECK_EQUAL(aDescription.has("key3"), false);
+
+ auto val1 = aDescription.get("key1");
+ auto val2 = aDescription.get("key2");
+
+ BOOST_CHECK_EQUAL(val1, "val1");
+ BOOST_CHECK_EQUAL(val2, "val2");
+
+ auto it = aDescription.begin();
+ BOOST_CHECK_EQUAL(it->second, "val1");
+ it++;
+ BOOST_CHECK_EQUAL(it->second, "val2");
+ it++;
+ BOOST_CHECK(it == aDescription.end());
+
+ BOOST_CHECK_EQUAL_COLLECTIONS(aDescription.wireEncode().wire(),
+ aDescription.wireEncode().wire() + aDescription.wireEncode().size(),
+ description,
+ description + sizeof(description));
+
+ BOOST_REQUIRE_NO_THROW(AdditionalDescription(Block(description, sizeof(description))));
+ AdditionalDescription aDescription2(Block(description, sizeof(description)));
+
+ BOOST_CHECK_EQUAL(aDescription2, aDescription);
+
+ AdditionalDescription aDescription3;
+ aDescription3.set("key3", "val3");
+ aDescription3.set("key2", "val2");
+
+ BOOST_CHECK_NE(aDescription2, aDescription3);
+
+ std::ostringstream os;
+ os << aDescription;
+ BOOST_CHECK_EQUAL(os.str(), text);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestAdditionalDescription
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/v2/certificate-bundle-fetcher.t.cpp b/tests/unit/security/v2/certificate-bundle-fetcher.t.cpp
new file mode 100644
index 0000000..d2e0565
--- /dev/null
+++ b/tests/unit/security/v2/certificate-bundle-fetcher.t.cpp
@@ -0,0 +1,191 @@
+/* -*- 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 "security/v2/certificate-bundle-fetcher.hpp"
+#include "security/v2/validation-policy-simple-hierarchy.hpp"
+#include "util/regex/regex-pattern-list-matcher.hpp"
+#include "lp/nack.hpp"
+
+#include "boost-test.hpp"
+#include "validator-fixture.hpp"
+
+namespace ndn {
+namespace security {
+namespace v2 {
+namespace tests {
+
+using namespace ndn::tests;
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(V2)
+BOOST_AUTO_TEST_SUITE(TestCertificateBundleFetcher)
+
+class CertificateBundleFetcherWrapper : public CertificateBundleFetcher
+{
+public:
+ CertificateBundleFetcherWrapper(Face& face)
+ : CertificateBundleFetcher(make_unique<CertificateFetcherFromNetwork>(face), face)
+ {
+ }
+};
+
+class Bundle
+{
+};
+
+class Cert
+{
+};
+
+class Timeout
+{
+};
+
+class Nack
+{
+};
+
+template<class Response>
+class CertificateBundleFetcherFixture : public HierarchicalValidatorFixture<ValidationPolicySimpleHierarchy,
+ CertificateBundleFetcherWrapper>
+{
+public:
+ CertificateBundleFetcherFixture()
+ : data("/Security/V2/ValidatorFixture/Sub1/Sub3/Data")
+ {
+ subSubIdentity = addSubCertificate("/Security/V2/ValidatorFixture/Sub1/Sub3", subIdentity);
+ cache.insert(subSubIdentity.getDefaultKey().getDefaultCertificate());
+
+ m_keyChain.sign(data, signingByIdentity(subSubIdentity));
+ bundleRegexMatcher = make_shared<RegexPatternListMatcher>("<>*<_BUNDLE><>*", nullptr);
+ processInterest = [this] (const Interest& interest) {
+ // check if the interest is for Bundle or individual certificates
+ if (bundleRegexMatcher->match(interest.getName(), 0, interest.getName().size())) {
+ makeResponse(interest);
+ }
+ else {
+ auto cert = cache.find(interest);
+ if (cert == nullptr) {
+ return;
+ }
+ face.receive(*cert);
+ }
+ };
+ }
+
+ void
+ makeResponse(const Interest& interest);
+
+public:
+ Data data;
+ Identity subSubIdentity;
+ shared_ptr<RegexPatternListMatcher> bundleRegexMatcher;
+};
+
+template<>
+void
+CertificateBundleFetcherFixture<Bundle>::makeResponse(const Interest& interest)
+{
+ Block certList = Block(tlv::Content);
+ Name bundleName(interest.getName());
+
+ if (!bundleName.get(-1).isSegment() || bundleName.get(-1).toSegment() == 0) {
+ Block subSubCert = subSubIdentity.getDefaultKey().getDefaultCertificate().wireEncode();
+ certList.push_back(subSubCert);
+
+ if (!bundleName.get(-1).isSegment()) {
+ bundleName
+ .appendVersion()
+ .appendSegment(0);
+ }
+ }
+ else {
+ Block subCert = subIdentity.getDefaultKey().getDefaultCertificate().wireEncode();
+ Block anchor = identity.getDefaultKey().getDefaultCertificate().wireEncode();
+ certList.push_back(subCert);
+ certList.push_back(anchor);
+ }
+
+ shared_ptr<Data> certBundle = make_shared<Data>();
+ certBundle->setName(bundleName);
+ certBundle->setFreshnessPeriod(100_s);
+ certBundle->setContent(certList);
+ certBundle->setFinalBlock(name::Component::fromSegment(1));
+
+ m_keyChain.sign(*certBundle, signingWithSha256());
+
+ face.receive(*certBundle);
+}
+
+template<>
+void
+CertificateBundleFetcherFixture<Timeout>::makeResponse(const Interest& interest)
+{
+ this->advanceClocks(200_s);
+}
+
+template<>
+void
+CertificateBundleFetcherFixture<Nack>::makeResponse(const Interest& interest)
+{
+ lp::Nack nack(interest);
+ nack.setHeader(lp::NackHeader().setReason(lp::NackReason::NO_ROUTE));
+ face.receive(nack);
+}
+
+BOOST_FIXTURE_TEST_CASE(ValidateSuccessWithBundle, CertificateBundleFetcherFixture<Bundle>)
+{
+ VALIDATE_SUCCESS(this->data, "Should get accepted, as interest brings the bundle segments");
+ BOOST_CHECK_EQUAL(this->face.sentInterests.size(), 2); // produced bundle has 2 segments
+
+ for (const auto& sentInterest : this->face.sentInterests) {
+ BOOST_CHECK(this->bundleRegexMatcher->match(sentInterest.getName(), 0, sentInterest.getName().size()));
+ }
+}
+
+using SuccessWithoutBundle = boost::mpl::vector<Nack, Timeout>;
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE(ValidateSuccessWithoutBundle, T, SuccessWithoutBundle, CertificateBundleFetcherFixture<T>)
+{
+ VALIDATE_SUCCESS(this->data, "Should get accepted, as interest brings the certs");
+ BOOST_CHECK_EQUAL(this->face.sentInterests.size(), 4); // since interest for Bundle fails, each cert is retrieved
+
+ bool toggle = true;
+ for (const auto& sentInterest : this->face.sentInterests) {
+ if (toggle) {
+ // every alternate interest is going to be that of a bundle
+ BOOST_CHECK(this->bundleRegexMatcher->match(sentInterest.getName(), 0, sentInterest.getName().size()));
+ }
+ else {
+ BOOST_CHECK(!this->bundleRegexMatcher->match(sentInterest.getName(), 0, sentInterest.getName().size()));
+ }
+ toggle = !toggle;
+ }
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestCertificateBundleFetcher
+BOOST_AUTO_TEST_SUITE_END() // V2
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace v2
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/v2/certificate-cache.t.cpp b/tests/unit/security/v2/certificate-cache.t.cpp
new file mode 100644
index 0000000..4ae3505
--- /dev/null
+++ b/tests/unit/security/v2/certificate-cache.t.cpp
@@ -0,0 +1,108 @@
+/* -*- 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 "security/v2/certificate-cache.hpp"
+
+#include "../../identity-management-time-fixture.hpp"
+#include "boost-test.hpp"
+
+namespace ndn {
+namespace security {
+namespace v2 {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(V2)
+
+class CertificateCacheFixture : public ndn::tests::IdentityManagementTimeFixture
+{
+public:
+ CertificateCacheFixture()
+ : certCache(10_s)
+ {
+ identity = addIdentity("/TestCertificateCache/");
+ cert = identity.getDefaultKey().getDefaultCertificate();
+ }
+
+public:
+ CertificateCache certCache;
+ Identity identity;
+ Certificate cert;
+};
+
+BOOST_FIXTURE_TEST_SUITE(TestCertificateCache, CertificateCacheFixture)
+
+BOOST_AUTO_TEST_CASE(RemovalTime)
+{
+ // Cache lifetime is capped to 10 seconds during cache construction
+
+ BOOST_CHECK_NO_THROW(certCache.insert(cert));
+ BOOST_CHECK(certCache.find(cert.getName()) != nullptr);
+
+ advanceClocks(11_s, 1);
+ BOOST_CHECK(certCache.find(cert.getName()) == nullptr);
+
+ BOOST_CHECK_NO_THROW(certCache.insert(cert));
+ BOOST_CHECK(certCache.find(cert.getName()) != nullptr);
+
+ advanceClocks(5_s);
+ BOOST_CHECK(certCache.find(cert.getName()) != nullptr);
+
+ advanceClocks(15_s);
+ BOOST_CHECK(certCache.find(cert.getName()) == nullptr);
+}
+
+BOOST_AUTO_TEST_CASE(FindByInterest)
+{
+ BOOST_CHECK_NO_THROW(certCache.insert(cert));
+
+ // Find by interest
+ BOOST_CHECK(certCache.find(Interest(cert.getIdentity())) != nullptr);
+ BOOST_CHECK(certCache.find(Interest(cert.getKeyName())) != nullptr);
+ BOOST_CHECK(certCache.find(Interest(Name(cert.getName()).appendVersion())) == nullptr);
+
+ advanceClocks(12_s);
+ BOOST_CHECK(certCache.find(Interest(cert.getIdentity())) == nullptr);
+
+ Certificate cert3 = addCertificate(identity.getDefaultKey(), "3");
+ Certificate cert4 = addCertificate(identity.getDefaultKey(), "4");
+ Certificate cert5 = addCertificate(identity.getDefaultKey(), "5");
+
+ certCache.insert(cert3);
+ certCache.insert(cert4);
+ certCache.insert(cert5);
+
+ Interest interest4(cert3.getKeyName());
+ interest4.setExclude(Exclude().excludeOne(cert3.getName().at(Certificate::ISSUER_ID_OFFSET)));
+ BOOST_CHECK(certCache.find(interest4) != nullptr);
+ BOOST_CHECK_NE(certCache.find(interest4)->getName(), cert3.getName());
+
+ // TODO cover more cases with different interests
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestCertificateCache
+BOOST_AUTO_TEST_SUITE_END() // V2
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace v2
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/v2/certificate-fetcher-direct-fetch.t.cpp b/tests/unit/security/v2/certificate-fetcher-direct-fetch.t.cpp
new file mode 100644
index 0000000..68fe6b1
--- /dev/null
+++ b/tests/unit/security/v2/certificate-fetcher-direct-fetch.t.cpp
@@ -0,0 +1,205 @@
+/* -*- 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 "security/v2/certificate-fetcher-direct-fetch.hpp"
+#include "security/v2/validation-policy-simple-hierarchy.hpp"
+#include "lp/nack.hpp"
+#include "lp/tags.hpp"
+
+#include "boost-test.hpp"
+#include "validator-fixture.hpp"
+
+#include <boost/range/adaptor/strided.hpp>
+#include <boost/range/adaptor/sliced.hpp>
+
+namespace ndn {
+namespace security {
+namespace v2 {
+namespace tests {
+
+using namespace ndn::tests;
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(V2)
+BOOST_AUTO_TEST_SUITE(TestCertificateFetcherDirectFetch)
+
+class Cert
+{
+};
+
+class Timeout
+{
+};
+
+class Nack
+{
+};
+
+template<class Response>
+class CertificateFetcherDirectFetchFixture : public HierarchicalValidatorFixture<ValidationPolicySimpleHierarchy,
+ CertificateFetcherDirectFetch>
+{
+public:
+ CertificateFetcherDirectFetchFixture()
+ : data("/Security/V2/ValidatorFixture/Sub1/Sub3/Data")
+ , interest("/Security/V2/ValidatorFixture/Sub1/Sub3/Interest")
+ , interestNoTag("/Security/V2/ValidatorFixture/Sub1/Sub3/Interest2")
+ {
+ Identity subSubIdentity = addSubCertificate("/Security/V2/ValidatorFixture/Sub1/Sub3", subIdentity);
+ cache.insert(subSubIdentity.getDefaultKey().getDefaultCertificate());
+
+ m_keyChain.sign(data, signingByIdentity(subSubIdentity));
+ m_keyChain.sign(interest, signingByIdentity(subSubIdentity));
+ m_keyChain.sign(interestNoTag, signingByIdentity(subSubIdentity));
+
+ data.setTag(make_shared<lp::IncomingFaceIdTag>(123));
+ interest.setTag(make_shared<lp::IncomingFaceIdTag>(123));
+
+ processInterest = [this] (const Interest& interest) {
+ auto nextHopFaceIdTag = interest.template getTag<lp::NextHopFaceIdTag>();
+ if (nextHopFaceIdTag == nullptr) {
+ makeResponse(interest); // respond only to the "infrastructure" interest
+ }
+ };
+ }
+
+ void
+ makeResponse(const Interest& interest);
+
+public:
+ Data data;
+ Interest interest;
+ Interest interestNoTag;
+};
+
+template<>
+void
+CertificateFetcherDirectFetchFixture<Cert>::makeResponse(const Interest& interest)
+{
+ auto cert = cache.find(interest);
+ if (cert == nullptr) {
+ return;
+ }
+ face.receive(*cert);
+}
+
+template<>
+void
+CertificateFetcherDirectFetchFixture<Timeout>::makeResponse(const Interest& interest)
+{
+ // do nothing
+}
+
+template<>
+void
+CertificateFetcherDirectFetchFixture<Nack>::makeResponse(const Interest& interest)
+{
+ lp::Nack nack(interest);
+ nack.setHeader(lp::NackHeader().setReason(lp::NackReason::NO_ROUTE));
+ face.receive(nack);
+}
+
+using Failures = boost::mpl::vector<Timeout, Nack>;
+
+BOOST_FIXTURE_TEST_CASE(ValidateSuccessData, CertificateFetcherDirectFetchFixture<Cert>)
+{
+ VALIDATE_SUCCESS(this->data, "Should get accepted, normal and/or direct interests bring certs");
+ BOOST_CHECK_EQUAL(this->face.sentInterests.size(), 4);
+
+ // odd interests
+ for (const auto& sentInterest : this->face.sentInterests | boost::adaptors::strided(2)) {
+ BOOST_CHECK(sentInterest.template getTag<lp::NextHopFaceIdTag>() != nullptr);
+ }
+
+ // even interests
+ for (const auto& sentInterest : this->face.sentInterests |
+ boost::adaptors::sliced(1, this->face.sentInterests.size()) |
+ boost::adaptors::strided(2)) {
+ BOOST_CHECK(sentInterest.template getTag<lp::NextHopFaceIdTag>() == nullptr);
+ }
+}
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE(ValidateFailureData, T, Failures, CertificateFetcherDirectFetchFixture<T>)
+{
+ VALIDATE_FAILURE(this->data, "Should fail, as all interests either NACKed or timeout");
+ // Direct fetcher sends two interests each time - to network and face
+ // 3 retries on nack or timeout (2 * (1 + 3) = 4)
+ BOOST_CHECK_EQUAL(this->face.sentInterests.size(), 8);
+
+ // odd interests
+ for (const auto& sentInterest : this->face.sentInterests | boost::adaptors::strided(2)) {
+ BOOST_CHECK(sentInterest.template getTag<lp::NextHopFaceIdTag>() != nullptr);
+ }
+
+ // even interests
+ for (const auto& sentInterest : this->face.sentInterests |
+ boost::adaptors::sliced(1, this->face.sentInterests.size()) |
+ boost::adaptors::strided(2)) {
+ BOOST_CHECK(sentInterest.template getTag<lp::NextHopFaceIdTag>() == nullptr);
+ }
+}
+
+BOOST_FIXTURE_TEST_CASE(ValidateSuccessInterest, CertificateFetcherDirectFetchFixture<Cert>)
+{
+ VALIDATE_SUCCESS(this->interest, "Should get accepted, normal and/or direct interests bring certs");
+ BOOST_CHECK_EQUAL(this->face.sentInterests.size(), 4);
+
+ // odd interests
+ for (const auto& sentInterest : this->face.sentInterests | boost::adaptors::strided(2)) {
+ BOOST_CHECK(sentInterest.template getTag<lp::NextHopFaceIdTag>() != nullptr);
+ }
+
+ // even interests
+ for (const auto& sentInterest : this->face.sentInterests |
+ boost::adaptors::sliced(1, this->face.sentInterests.size()) |
+ boost::adaptors::strided(2)) {
+ BOOST_CHECK(sentInterest.template getTag<lp::NextHopFaceIdTag>() == nullptr);
+ }
+}
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE(ValidateFailureInterest, T, Failures, CertificateFetcherDirectFetchFixture<T>)
+{
+ VALIDATE_FAILURE(this->interest, "Should fail, as all interests either NACKed or timeout");
+ // Direct fetcher sends two interests each time - to network and face
+ // 3 retries on nack or timeout (2 * (1 + 3) = 4)
+ BOOST_CHECK_EQUAL(this->face.sentInterests.size(), 8);
+
+ // odd interests
+ for (const auto& sentInterest : this->face.sentInterests | boost::adaptors::strided(2)) {
+ BOOST_CHECK(sentInterest.template getTag<lp::NextHopFaceIdTag>() != nullptr);
+ }
+
+ // even interests
+ for (const auto& sentInterest : this->face.sentInterests |
+ boost::adaptors::sliced(1, this->face.sentInterests.size()) |
+ boost::adaptors::strided(2)) {
+ BOOST_CHECK(sentInterest.template getTag<lp::NextHopFaceIdTag>() == nullptr);
+ }
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestCertificateFetcherDirectFetch
+BOOST_AUTO_TEST_SUITE_END() // V2
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace v2
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/v2/certificate-fetcher-from-network.t.cpp b/tests/unit/security/v2/certificate-fetcher-from-network.t.cpp
new file mode 100644
index 0000000..5a6b395
--- /dev/null
+++ b/tests/unit/security/v2/certificate-fetcher-from-network.t.cpp
@@ -0,0 +1,140 @@
+/* -*- 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 "security/v2/certificate-fetcher-from-network.hpp"
+#include "security/v2/validation-policy-simple-hierarchy.hpp"
+#include "lp/nack.hpp"
+
+#include "boost-test.hpp"
+#include "validator-fixture.hpp"
+
+namespace ndn {
+namespace security {
+namespace v2 {
+namespace tests {
+
+using namespace ndn::tests;
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(V2)
+BOOST_AUTO_TEST_SUITE(TestCertificateFetcherFromNetwork)
+
+class Cert
+{
+};
+
+class Timeout
+{
+};
+
+class Nack
+{
+};
+
+template<class Response>
+class CertificateFetcherFromNetworkFixture : public HierarchicalValidatorFixture<ValidationPolicySimpleHierarchy,
+ CertificateFetcherFromNetwork>
+{
+public:
+ CertificateFetcherFromNetworkFixture()
+ : data("/Security/V2/ValidatorFixture/Sub1/Sub3/Data")
+ , interest("/Security/V2/ValidatorFixture/Sub1/Sub3/Interest")
+ {
+ Identity subSubIdentity = addSubCertificate("/Security/V2/ValidatorFixture/Sub1/Sub3", subIdentity);
+ cache.insert(subSubIdentity.getDefaultKey().getDefaultCertificate());
+
+ m_keyChain.sign(data, signingByIdentity(subSubIdentity));
+ m_keyChain.sign(interest, signingByIdentity(subSubIdentity));
+
+ processInterest = bind(&CertificateFetcherFromNetworkFixture<Response>::makeResponse, this, _1);
+ }
+
+ void
+ makeResponse(const Interest& interest);
+
+public:
+ Data data;
+ Interest interest;
+};
+
+template<>
+void
+CertificateFetcherFromNetworkFixture<Cert>::makeResponse(const Interest& interest)
+{
+ auto cert = cache.find(interest);
+ if (cert == nullptr) {
+ return;
+ }
+ face.receive(*cert);
+}
+
+template<>
+void
+CertificateFetcherFromNetworkFixture<Timeout>::makeResponse(const Interest& interest)
+{
+ // do nothing
+}
+
+template<>
+void
+CertificateFetcherFromNetworkFixture<Nack>::makeResponse(const Interest& interest)
+{
+ lp::Nack nack(interest);
+ nack.setHeader(lp::NackHeader().setReason(lp::NackReason::NO_ROUTE));
+ face.receive(nack);
+}
+
+using Failures = boost::mpl::vector<Timeout, Nack>;
+
+BOOST_FIXTURE_TEST_CASE(ValidateSuccess, CertificateFetcherFromNetworkFixture<Cert>)
+{
+ VALIDATE_SUCCESS(this->data, "Should get accepted, as normal interests bring cert");
+ BOOST_CHECK_EQUAL(this->face.sentInterests.size(), 2);
+ this->face.sentInterests.clear();
+
+ this->advanceClocks(1_h, 2); // expire validator caches
+
+ VALIDATE_SUCCESS(this->interest, "Should get accepted, as interests bring certs");
+ BOOST_CHECK_EQUAL(this->face.sentInterests.size(), 2);
+}
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE(ValidateFailure, T, Failures, CertificateFetcherFromNetworkFixture<T>)
+{
+ VALIDATE_FAILURE(this->data, "Should fail, as interests don't bring data");
+ // first interest + 3 retries
+ BOOST_CHECK_EQUAL(this->face.sentInterests.size(), 4);
+
+ this->face.sentInterests.clear();
+
+ this->advanceClocks(1_h, 2); // expire validator caches
+
+ VALIDATE_FAILURE(this->interest, "Should fail, as interests don't bring data");
+ BOOST_CHECK_EQUAL(this->face.sentInterests.size(), 4);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestCertificateFetcherFromNetwork
+BOOST_AUTO_TEST_SUITE_END() // V2
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace v2
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/v2/certificate-fetcher-offline.t.cpp b/tests/unit/security/v2/certificate-fetcher-offline.t.cpp
new file mode 100644
index 0000000..102a308
--- /dev/null
+++ b/tests/unit/security/v2/certificate-fetcher-offline.t.cpp
@@ -0,0 +1,75 @@
+/* -*- 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 "security/v2/certificate-fetcher-offline.hpp"
+#include "security/v2/validation-policy-simple-hierarchy.hpp"
+
+#include "boost-test.hpp"
+#include "validator-fixture.hpp"
+
+namespace ndn {
+namespace security {
+namespace v2 {
+namespace tests {
+
+using namespace ndn::tests;
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(V2)
+
+class CertificateFetcherOfflineWrapper : public CertificateFetcherOffline
+{
+public:
+ CertificateFetcherOfflineWrapper(Face&)
+ {
+ }
+};
+
+using CertificateFetcherOfflineFixture = HierarchicalValidatorFixture<ValidationPolicySimpleHierarchy,
+ CertificateFetcherOfflineWrapper>;
+
+BOOST_FIXTURE_TEST_SUITE(TestCertificateFetcherOffline, CertificateFetcherOfflineFixture)
+
+typedef boost::mpl::vector<Interest, Data> Packets;
+
+BOOST_AUTO_TEST_CASE_TEMPLATE(Validate, Packet, Packets)
+{
+ Packet unsignedPacket("/Security/V2/ValidatorFixture/Sub1/Packet");
+
+ Packet packet = unsignedPacket;
+ m_keyChain.sign(packet, signingByIdentity(subIdentity));
+ VALIDATE_FAILURE(packet, "Should fail, as no cert should be requested");
+ BOOST_CHECK_EQUAL(this->face.sentInterests.size(), 0);
+
+ packet = unsignedPacket;
+ m_keyChain.sign(packet, signingByIdentity(identity));
+ VALIDATE_SUCCESS(packet, "Should succeed, as signed by trust anchor");
+ BOOST_CHECK_EQUAL(this->face.sentInterests.size(), 0);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestCertificateFetcherOffline
+BOOST_AUTO_TEST_SUITE_END() // V2
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace v2
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/v2/certificate.t.cpp b/tests/unit/security/v2/certificate.t.cpp
new file mode 100644
index 0000000..e677aed
--- /dev/null
+++ b/tests/unit/security/v2/certificate.t.cpp
@@ -0,0 +1,291 @@
+/* -*- 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 Zhiyi Zhang <dreamerbarrychang@gmail.com>
+ */
+
+#include "security/v2/certificate.hpp"
+
+#include "boost-test.hpp"
+#include "../../unit-test-time-fixture.hpp"
+
+#include <boost/lexical_cast.hpp>
+
+namespace ndn {
+namespace security {
+namespace v2 {
+namespace tests {
+
+using namespace ndn::tests;
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(V2)
+BOOST_FIXTURE_TEST_SUITE(TestCertificate, UnitTestTimeFixture)
+
+const uint8_t PUBLIC_KEY[] = {
+ 0x30, 0x81, 0x9d, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+ 0x01, 0x05, 0x00, 0x03, 0x81, 0x8b, 0x00, 0x30, 0x81, 0x87, 0x02, 0x81, 0x81, 0x00, 0x9e,
+ 0x06, 0x3e, 0x47, 0x85, 0xb2, 0x34, 0x37, 0xaa, 0x85, 0x47, 0xac, 0x03, 0x24, 0x83, 0xb5,
+ 0x9c, 0xa8, 0x05, 0x3a, 0x24, 0x1e, 0xeb, 0x89, 0x01, 0xbb, 0xe9, 0x9b, 0xb2, 0xc3, 0x22,
+ 0xac, 0x68, 0xe3, 0xf0, 0x6c, 0x02, 0xce, 0x68, 0xa6, 0xc4, 0xd0, 0xa7, 0x06, 0x90, 0x9c,
+ 0xaa, 0x1b, 0x08, 0x1d, 0x8b, 0x43, 0x9a, 0x33, 0x67, 0x44, 0x6d, 0x21, 0xa3, 0x1b, 0x88,
+ 0x9a, 0x97, 0x5e, 0x59, 0xc4, 0x15, 0x0b, 0xd9, 0x2c, 0xbd, 0x51, 0x07, 0x61, 0x82, 0xad,
+ 0xc1, 0xb8, 0xd7, 0xbf, 0x9b, 0xcf, 0x7d, 0x24, 0xc2, 0x63, 0xf3, 0x97, 0x17, 0xeb, 0xfe,
+ 0x62, 0x25, 0xba, 0x5b, 0x4d, 0x8a, 0xc2, 0x7a, 0xbd, 0x43, 0x8a, 0x8f, 0xb8, 0xf2, 0xf1,
+ 0xc5, 0x6a, 0x30, 0xd3, 0x50, 0x8c, 0xc8, 0x9a, 0xdf, 0xef, 0xed, 0x35, 0xe7, 0x7a, 0x62,
+ 0xea, 0x76, 0x7c, 0xbb, 0x08, 0x26, 0xc7, 0x02, 0x01, 0x11
+};
+
+const uint8_t SIG_INFO[] = {
+ 0x16, 0x55, 0x1B, 0x01, 0x01, 0x1C, 0x26, 0x07, 0x24, 0x08, 0x03, 0x6E, 0x64, 0x6E, 0x08, 0x05,
+ 0x73, 0x69, 0x74, 0x65, 0x31, 0x08, 0x11, 0x6B, 0x73, 0x6B, 0x2D, 0x32, 0x35, 0x31, 0x36, 0x34,
+ 0x32, 0x35, 0x33, 0x37, 0x37, 0x30, 0x39, 0x34, 0x08, 0x03, 0x4B, 0x45, 0x59, 0xFD, 0x00, 0xFD,
+ 0x26, 0xFD, 0x00, 0xFE, 0x0F, 0x32, 0x30, 0x31, 0x35, 0x30, 0x38, 0x31, 0x34, 0x54, 0x32, 0x32,
+ 0x33, 0x37, 0x33, 0x39, 0xFD, 0x00, 0xFF, 0x0F, 0x32, 0x30, 0x31, 0x35, 0x30, 0x38, 0x31, 0x38,
+ 0x54, 0x32, 0x32, 0x33, 0x37, 0x33, 0x38
+};
+
+const uint8_t SIG_VALUE[] = {
+ 0x17, 0x80, // SignatureValue
+ 0x2f, 0xd6, 0xf1, 0x6e, 0x80, 0x6f, 0x10, 0xbe, 0xb1, 0x6f, 0x3e, 0x31, 0xec,
+ 0xe3, 0xb9, 0xea, 0x83, 0x30, 0x40, 0x03, 0xfc, 0xa0, 0x13, 0xd9, 0xb3, 0xc6,
+ 0x25, 0x16, 0x2d, 0xa6, 0x58, 0x41, 0x69, 0x62, 0x56, 0xd8, 0xb3, 0x6a, 0x38,
+ 0x76, 0x56, 0xea, 0x61, 0xb2, 0x32, 0x70, 0x1c, 0xb6, 0x4d, 0x10, 0x1d, 0xdc,
+ 0x92, 0x8e, 0x52, 0xa5, 0x8a, 0x1d, 0xd9, 0x96, 0x5e, 0xc0, 0x62, 0x0b, 0xcf,
+ 0x3a, 0x9d, 0x7f, 0xca, 0xbe, 0xa1, 0x41, 0x71, 0x85, 0x7a, 0x8b, 0x5d, 0xa9,
+ 0x64, 0xd6, 0x66, 0xb4, 0xe9, 0x8d, 0x0c, 0x28, 0x43, 0xee, 0xa6, 0x64, 0xe8,
+ 0x55, 0xf6, 0x1c, 0x19, 0x0b, 0xef, 0x99, 0x25, 0x1e, 0xdc, 0x78, 0xb3, 0xa7,
+ 0xaa, 0x0d, 0x14, 0x58, 0x30, 0xe5, 0x37, 0x6a, 0x6d, 0xdb, 0x56, 0xac, 0xa3,
+ 0xfc, 0x90, 0x7a, 0xb8, 0x66, 0x9c, 0x0e, 0xf6, 0xb7, 0x64, 0xd1
+};
+
+const uint8_t CERT[] = {
+ 0x06, 0xFD, 0x01, 0xBB, // Data
+ 0x07, 0x33, // Name /ndn/site1/KEY/ksk-1416425377094/0123/%FD%00%00%01I%C9%8B
+ 0x08, 0x03, 0x6E, 0x64, 0x6E,
+ 0x08, 0x05, 0x73, 0x69, 0x74, 0x65, 0x31,
+ 0x08, 0x03, 0x4B, 0x45, 0x59,
+ 0x08, 0x11,
+ 0x6B, 0x73, 0x6B, 0x2D, 0x31, 0x34, 0x31, 0x36, 0x34, 0x32, 0x35, 0x33, 0x37, 0x37, 0x30, 0x39,
+ 0x34,
+ 0x08, 0x04, 0x30, 0x31, 0x32, 0x33,
+ 0x08, 0x07, 0xFD, 0x00, 0x00, 0x01, 0x49, 0xC9, 0x8B,
+ 0x14, 0x09, // MetaInfo
+ 0x18, 0x01, 0x02, // ContentType = Key
+ 0x19, 0x04, 0x00, 0x36, 0xEE, 0x80, // FreshnessPeriod = 3600000 ms
+ 0x15, 0xA0, // Content
+ 0x30, 0x81, 0x9D, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01,
+ 0x05, 0x00, 0x03, 0x81, 0x8B, 0x00, 0x30, 0x81, 0x87, 0x02, 0x81, 0x81, 0x00, 0x9E, 0x06, 0x3E,
+ 0x47, 0x85, 0xB2, 0x34, 0x37, 0xAA, 0x85, 0x47, 0xAC, 0x03, 0x24, 0x83, 0xB5, 0x9C, 0xA8, 0x05,
+ 0x3A, 0x24, 0x1E, 0xEB, 0x89, 0x01, 0xBB, 0xE9, 0x9B, 0xB2, 0xC3, 0x22, 0xAC, 0x68, 0xE3, 0xF0,
+ 0x6C, 0x02, 0xCE, 0x68, 0xA6, 0xC4, 0xD0, 0xA7, 0x06, 0x90, 0x9C, 0xAA, 0x1B, 0x08, 0x1D, 0x8B,
+ 0x43, 0x9A, 0x33, 0x67, 0x44, 0x6D, 0x21, 0xA3, 0x1B, 0x88, 0x9A, 0x97, 0x5E, 0x59, 0xC4, 0x15,
+ 0x0B, 0xD9, 0x2C, 0xBD, 0x51, 0x07, 0x61, 0x82, 0xAD, 0xC1, 0xB8, 0xD7, 0xBF, 0x9B, 0xCF, 0x7D,
+ 0x24, 0xC2, 0x63, 0xF3, 0x97, 0x17, 0xEB, 0xFE, 0x62, 0x25, 0xBA, 0x5B, 0x4D, 0x8A, 0xC2, 0x7A,
+ 0xBD, 0x43, 0x8A, 0x8F, 0xB8, 0xF2, 0xF1, 0xC5, 0x6A, 0x30, 0xD3, 0x50, 0x8C, 0xC8, 0x9A, 0xDF,
+ 0xEF, 0xED, 0x35, 0xE7, 0x7A, 0x62, 0xEA, 0x76, 0x7C, 0xBB, 0x08, 0x26, 0xC7, 0x02, 0x01, 0x11,
+ 0x16, 0x55, // SignatureInfo
+ 0x1B, 0x01, 0x01, // SignatureType
+ 0x1C, 0x26, // KeyLocator: /ndn/site1/KEY/ksk-2516425377094
+ 0x07, 0x24,
+ 0x08, 0x03, 0x6E, 0x64, 0x6E,
+ 0x08, 0x05, 0x73, 0x69, 0x74, 0x65, 0x31,
+ 0x08, 0x03, 0x4B, 0x45, 0x59,
+ 0x08, 0x11,
+ 0x6B, 0x73, 0x6B, 0x2D, 0x32, 0x35, 0x31, 0x36, 0x34, 0x32, 0x35, 0x33, 0x37, 0x37, 0x30, 0x39,
+ 0x34,
+ 0xFD, 0x00, 0xFD, 0x26, // ValidityPeriod: (20150814T223739, 20150818T223738)
+ 0xFD, 0x00, 0xFE, 0x0F,
+ 0x32, 0x30, 0x31, 0x35, 0x30, 0x38, 0x31, 0x34, 0x54, 0x32, 0x32, 0x33, 0x37, 0x33, 0x39,
+ 0xFD, 0x00, 0xFF, 0x0F,
+ 0x32, 0x30, 0x31, 0x35, 0x30, 0x38, 0x31, 0x38, 0x54, 0x32, 0x32, 0x33, 0x37, 0x33, 0x38,
+ 0x17, 0x80, // SignatureValue
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+
+Signature
+generateFakeSignature()
+{
+ Block block1(SIG_INFO, sizeof(SIG_INFO));
+ SignatureInfo signatureInfo(block1);
+
+ Name keyLocatorName("/ndn/site1/KEY/ksk-2516425377094");
+ KeyLocator keyLocator(keyLocatorName);
+ signatureInfo.setKeyLocator(keyLocator);
+
+ ValidityPeriod period(time::fromIsoString("20141111T050000"), time::fromIsoString("20141111T060000"));
+ signatureInfo.setValidityPeriod(period);
+
+ Signature signature(signatureInfo);
+ Block block2(SIG_VALUE, sizeof(SIG_VALUE));
+ signature.setValue(block2);
+
+ return signature;
+}
+
+BOOST_AUTO_TEST_CASE(Construction)
+{
+ Block block(CERT, sizeof(CERT));
+ Certificate certificate(block);
+
+ BOOST_CHECK_EQUAL(certificate.getName(), "/ndn/site1/KEY/ksk-1416425377094/0123/%FD%00%00%01I%C9%8B");
+ BOOST_CHECK_EQUAL(certificate.getKeyName(), "/ndn/site1/KEY/ksk-1416425377094");
+ BOOST_CHECK_EQUAL(certificate.getIdentity(), "/ndn/site1");
+ BOOST_CHECK_EQUAL(certificate.getIssuerId(), name::Component("0123"));
+ BOOST_CHECK_EQUAL(certificate.getKeyId(), name::Component("ksk-1416425377094"));
+ BOOST_CHECK_EQUAL(certificate.getSignature().getKeyLocator().getName(), "/ndn/site1/KEY/ksk-2516425377094");
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(certificate.getValidityPeriod()), "(20150814T223739, 20150818T223738)");
+
+ BOOST_CHECK_THROW(certificate.getExtension(12345), ndn::SignatureInfo::Error);
+ BOOST_CHECK_NO_THROW(certificate.getPublicKey());
+
+ Data data(block);
+ Certificate certificate2(std::move(data));
+ BOOST_CHECK_EQUAL(certificate, certificate2);
+}
+
+BOOST_AUTO_TEST_CASE(Setters)
+{
+ Certificate certificate;
+ certificate.setName("/ndn/site1/KEY/ksk-1416425377094/0123/%FD%00%00%01I%C9%8B");
+ certificate.setFreshnessPeriod(1_h);
+ certificate.setContent(PUBLIC_KEY, sizeof(PUBLIC_KEY));
+ certificate.setSignature(generateFakeSignature());
+
+ BOOST_CHECK_EQUAL(certificate.getName(), "/ndn/site1/KEY/ksk-1416425377094/0123/%FD%00%00%01I%C9%8B");
+ BOOST_CHECK_EQUAL(certificate.getKeyName(), "/ndn/site1/KEY/ksk-1416425377094");
+ BOOST_CHECK_EQUAL(certificate.getIdentity(), "/ndn/site1");
+ BOOST_CHECK_EQUAL(certificate.getIssuerId(), name::Component("0123"));
+ BOOST_CHECK_EQUAL(certificate.getKeyId(), name::Component("ksk-1416425377094"));
+ BOOST_CHECK_EQUAL(certificate.getSignature().getKeyLocator().getName(), "/ndn/site1/KEY/ksk-2516425377094");
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(certificate.getValidityPeriod()), "(20141111T050000, 20141111T060000)");
+
+ BOOST_CHECK_THROW(certificate.getExtension(12345), ndn::SignatureInfo::Error);
+ BOOST_CHECK_NO_THROW(certificate.getPublicKey());
+}
+
+BOOST_AUTO_TEST_CASE(ValidityPeriodChecking)
+{
+ Certificate certificate;
+ certificate.setName("/ndn/site1/KEY/ksk-1416425377094/0123/%FD%00%00%01I%C9%8B");
+ certificate.setFreshnessPeriod(1_h);
+ certificate.setContent(PUBLIC_KEY, sizeof(PUBLIC_KEY));
+ certificate.setSignature(generateFakeSignature());
+
+ BOOST_CHECK_EQUAL(certificate.isValid(), true);
+ BOOST_CHECK_EQUAL(certificate.isValid(time::fromIsoString("20141111T045959")), false);
+ BOOST_CHECK_EQUAL(certificate.isValid(time::fromIsoString("20141111T060001")), false);
+}
+
+// This fixture prepares a well-formed certificate. A test case then modifies one of the
+// fields, and verifies the Certificate class correctly identifies the certificate as
+// malformed.
+class InvalidCertFixture
+{
+public:
+ InvalidCertFixture()
+ {
+ Certificate certBase(Block(CERT, sizeof(CERT)));
+ BOOST_CHECK_NO_THROW((Certificate(certBase)));
+
+ m_certBase = Data(certBase);
+ m_certBase.setSignature(generateFakeSignature());
+
+ BOOST_CHECK_NO_THROW((Certificate(m_certBase)));
+ }
+
+public:
+ Data m_certBase;
+};
+
+BOOST_FIXTURE_TEST_CASE(InvalidName, InvalidCertFixture)
+{
+ Data data(m_certBase);
+ data.setName("/ndn/site1/ksk-1416425377094/0123/%FD%00%00%01I%C9%8B");
+ data.setSignature(generateFakeSignature());
+
+ BOOST_CHECK_THROW((Certificate(data)), Certificate::Error);
+ BOOST_CHECK_THROW((Certificate(std::move(data))), Certificate::Error);
+}
+
+BOOST_FIXTURE_TEST_CASE(InvalidType, InvalidCertFixture)
+{
+ Data data(m_certBase);
+ data.setContentType(tlv::ContentType_Blob);
+ data.setSignature(generateFakeSignature());
+
+ BOOST_CHECK_THROW((Certificate(data)), Certificate::Error);
+ BOOST_CHECK_THROW((Certificate(std::move(data))), Certificate::Error);
+}
+
+BOOST_FIXTURE_TEST_CASE(EmptyContent, InvalidCertFixture)
+{
+ Data data(m_certBase);
+ data.setContent(nullptr, 0);
+ data.setSignature(generateFakeSignature());
+
+ BOOST_CHECK_THROW((Certificate(data)), Certificate::Error);
+ BOOST_CHECK_THROW((Certificate(std::move(data))), Certificate::Error);
+
+ Certificate cert(m_certBase);
+ cert.setContent(nullptr, 0);
+ cert.setSignature(generateFakeSignature());
+ BOOST_CHECK_THROW(cert.getPublicKey(), Certificate::Error);
+}
+
+BOOST_AUTO_TEST_CASE(PrintCertificateInfo)
+{
+ const std::string expectedCertificateInfo = std::string(R"INFO(
+Certificate name:
+ /ndn/site1/KEY/ksk-1416425377094/0123/%FD%00%00%01I%C9%8B
+Validity:
+ NotBefore: 20150814T223739
+ NotAfter: 20150818T223738
+Public key bits:
+ MIGdMA0GCSqGSIb3DQEBAQUAA4GLADCBhwKBgQCeBj5HhbI0N6qFR6wDJIO1nKgF
+ OiQe64kBu+mbssMirGjj8GwCzmimxNCnBpCcqhsIHYtDmjNnRG0hoxuImpdeWcQV
+ C9ksvVEHYYKtwbjXv5vPfSTCY/OXF+v+YiW6W02Kwnq9Q4qPuPLxxWow01CMyJrf
+ 7+0153pi6nZ8uwgmxwIBEQ==
+Signature Information:
+ Signature Type: SignatureSha256WithRsa
+ Key Locator: Name=/ndn/site1/KEY/ksk-2516425377094
+)INFO").substr(1);
+
+ Certificate certificate(Block(CERT, sizeof(CERT)));
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(certificate), expectedCertificateInfo);
+
+ // @todo Check output formats of other certificates
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestCertificate
+BOOST_AUTO_TEST_SUITE_END() // V2
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace v2
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/v2/key-chain.t.cpp b/tests/unit/security/v2/key-chain.t.cpp
new file mode 100644
index 0000000..649ef7b
--- /dev/null
+++ b/tests/unit/security/v2/key-chain.t.cpp
@@ -0,0 +1,445 @@
+/* -*- 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 "security/v2/key-chain.hpp"
+#include "security/signing-helpers.hpp"
+#include "security/verification-helpers.hpp"
+
+#include "boost-test.hpp"
+#include "identity-management-fixture.hpp"
+#include "../../test-home-env-saver.hpp"
+
+namespace ndn {
+namespace security {
+namespace v2 {
+namespace tests {
+
+using namespace ndn::tests;
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(V2)
+BOOST_FIXTURE_TEST_SUITE(TestKeyChain, TestHomeEnvSaver)
+
+template<class Path>
+class TestHomeAndPibFixture : public TestHomeFixture<Path>
+{
+public:
+ TestHomeAndPibFixture()
+ {
+ unsetenv("NDN_CLIENT_PIB");
+ unsetenv("NDN_CLIENT_TPM");
+ }
+
+ ~TestHomeAndPibFixture()
+ {
+ try {
+ const_cast<std::string&>(KeyChain::getDefaultPibLocator()).clear();
+ }
+ catch (const KeyChain::Error&) {
+ // ignore
+ }
+
+ try {
+ const_cast<std::string&>(KeyChain::getDefaultTpmLocator()).clear();
+ }
+ catch (const KeyChain::Error&) {
+ // ignore
+ }
+ }
+};
+
+struct PibPathConfigFileHome
+{
+ const std::string PATH = "build/config-file-home/";
+};
+
+BOOST_FIXTURE_TEST_CASE(ConstructorNormalConfig, TestHomeAndPibFixture<PibPathConfigFileHome>)
+{
+ createClientConf({"pib=pib-memory:", "tpm=tpm-memory:"});
+
+ BOOST_REQUIRE_NO_THROW(KeyChain());
+
+ KeyChain keyChain;
+ BOOST_CHECK_EQUAL(keyChain.getPib().getPibLocator(), "pib-memory:");
+ BOOST_CHECK_EQUAL(keyChain.getPib().getTpmLocator(), "tpm-memory:");
+ BOOST_CHECK_EQUAL(keyChain.getTpm().getTpmLocator(), "tpm-memory:");
+}
+
+struct PibPathConfigFileEmptyHome
+{
+ const std::string PATH = "build/config-file-empty-home/";
+};
+
+BOOST_FIXTURE_TEST_CASE(ConstructorEmptyConfig, TestHomeAndPibFixture<PibPathConfigFileEmptyHome>)
+{
+ createClientConf({"pib=pib-memory:"});
+
+#if defined(NDN_CXX_HAVE_OSX_FRAMEWORKS)
+ std::string oldHOME;
+ if (std::getenv("OLD_HOME"))
+ oldHOME = std::getenv("OLD_HOME");
+
+ std::string HOME;
+ if (std::getenv("HOME"))
+ HOME = std::getenv("HOME");
+
+ if (!oldHOME.empty())
+ setenv("HOME", oldHOME.c_str(), 1);
+ else
+ unsetenv("HOME");
+#endif
+
+ BOOST_REQUIRE_NO_THROW(KeyChain());
+ KeyChain keyChain;
+ BOOST_CHECK_EQUAL(keyChain.getPib().getPibLocator(), "pib-memory:");
+
+#if defined(NDN_CXX_HAVE_OSX_FRAMEWORKS)
+ BOOST_CHECK_EQUAL(keyChain.getPib().getTpmLocator(), "tpm-osxkeychain:");
+ BOOST_CHECK_EQUAL(keyChain.getTpm().getTpmLocator(), "tpm-osxkeychain:");
+#else
+ BOOST_CHECK_EQUAL(keyChain.getPib().getTpmLocator(), "tpm-file:");
+ BOOST_CHECK_EQUAL(keyChain.getTpm().getTpmLocator(), "tpm-file:");
+#endif
+
+#if defined(NDN_CXX_HAVE_OSX_FRAMEWORKS)
+ if (!HOME.empty())
+ setenv("HOME", HOME.c_str(), 1);
+ else
+ unsetenv("HOME");
+
+ if (!oldHOME.empty())
+ setenv("OLD_HOME", oldHOME.c_str(), 1);
+ else
+ unsetenv("OLD_HOME");
+#endif
+}
+
+struct PibPathConfigFileEmpty2Home
+{
+ const std::string PATH = "build/config-file-empty2-home/";
+};
+
+BOOST_FIXTURE_TEST_CASE(ConstructorEmpty2Config, TestHomeAndPibFixture<PibPathConfigFileEmpty2Home>)
+{
+ createClientConf({"tpm=tpm-memory:"});
+
+ BOOST_REQUIRE_NO_THROW(KeyChain());
+
+ KeyChain keyChain;
+ BOOST_CHECK_EQUAL(keyChain.getPib().getPibLocator(), "pib-sqlite3:");
+ BOOST_CHECK_EQUAL(keyChain.getPib().getTpmLocator(), "tpm-memory:");
+ BOOST_CHECK_EQUAL(keyChain.getTpm().getTpmLocator(), "tpm-memory:");
+}
+
+struct PibPathConfigFileMalformedHome
+{
+ const std::string PATH = "build/config-file-malformed-home/";
+};
+
+BOOST_FIXTURE_TEST_CASE(ConstructorMalConfig, TestHomeAndPibFixture<PibPathConfigFileMalformedHome>)
+{
+ createClientConf({"pib=lord", "tpm=ring"});
+
+ BOOST_REQUIRE_THROW(KeyChain(), KeyChain::Error); // Wrong configuration. Error expected.
+}
+
+struct PibPathConfigFileMalformed2Home
+{
+ const std::string PATH = "build/config-file-malformed2-home/";
+};
+
+BOOST_FIXTURE_TEST_CASE(ConstructorMal2Config, TestHomeAndPibFixture<PibPathConfigFileMalformed2Home>)
+{
+ createClientConf({"pib=pib-sqlite3:%PATH%", "tpm=just-wrong"});
+
+ BOOST_REQUIRE_THROW(KeyChain(), KeyChain::Error); // Wrong configuration. Error expected.
+}
+
+struct PibPathConfigFileNonCanonicalTpm
+{
+ const std::string PATH = "build/config-file-non-canonical-tpm/";
+};
+
+BOOST_FIXTURE_TEST_CASE(ConstructorNonCanonicalTpm, TestHomeAndPibFixture<PibPathConfigFileNonCanonicalTpm>) // Bug 4297
+{
+ createClientConf({"pib=pib-sqlite3:", "tpm=tpm-file"});
+
+ {
+ KeyChain keyChain;
+ keyChain.createIdentity("/test");
+ BOOST_CHECK_EQUAL(keyChain.getPib().getPibLocator(), "pib-sqlite3:");
+ BOOST_CHECK_EQUAL(keyChain.getTpm().getTpmLocator(), "tpm-file:");
+ }
+
+ {
+ KeyChain keyChain;
+ BOOST_CHECK_EQUAL(keyChain.getPib().getPibLocator(), "pib-sqlite3:");
+ BOOST_CHECK_EQUAL(keyChain.getTpm().getTpmLocator(), "tpm-file:");
+ BOOST_CHECK(keyChain.getPib().getIdentities().find("/test") != keyChain.getPib().getIdentities().end());
+ }
+}
+
+BOOST_AUTO_TEST_CASE(KeyChainWithCustomTpmAndPib)
+{
+ BOOST_REQUIRE_NO_THROW((KeyChain("pib-memory", "tpm-memory")));
+ BOOST_REQUIRE_NO_THROW((KeyChain("pib-memory:", "tpm-memory:")));
+ BOOST_REQUIRE_NO_THROW((KeyChain("pib-memory:/something", "tpm-memory:/something")));
+
+ KeyChain keyChain("pib-memory", "tpm-memory");
+ BOOST_CHECK_EQUAL(keyChain.getPib().getPibLocator(), "pib-memory:");
+ BOOST_CHECK_EQUAL(keyChain.getPib().getTpmLocator(), "tpm-memory:");
+ BOOST_CHECK_EQUAL(keyChain.getTpm().getTpmLocator(), "tpm-memory:");
+}
+
+BOOST_FIXTURE_TEST_CASE(Management, IdentityManagementFixture)
+{
+ Name identityName("/test/id");
+ Name identity2Name("/test/id2");
+
+ BOOST_CHECK_EQUAL(m_keyChain.getPib().getIdentities().size(), 0);
+ BOOST_REQUIRE_THROW(m_keyChain.getPib().getDefaultIdentity(), Pib::Error);
+
+ // Create identity
+ Identity id = m_keyChain.createIdentity(identityName);
+ BOOST_CHECK(id);
+ BOOST_CHECK(m_keyChain.getPib().getIdentities().find(identityName) != m_keyChain.getPib().getIdentities().end());
+ // The first added identity becomes the default identity
+ BOOST_REQUIRE_NO_THROW(m_keyChain.getPib().getDefaultIdentity());
+ // The default key of the added identity must exist
+ Key key;
+ BOOST_REQUIRE_NO_THROW(key = id.getDefaultKey());
+ // The default certificate of the default key must exist
+ BOOST_REQUIRE_NO_THROW(key.getDefaultCertificate());
+
+ // Delete key
+ Name key1Name = key.getName();
+ BOOST_CHECK_NO_THROW(id.getKey(key1Name));
+ BOOST_CHECK_EQUAL(id.getKeys().size(), 1);
+ m_keyChain.deleteKey(id, key);
+ // The key instance should not be valid any more
+ BOOST_CHECK(!key);
+ BOOST_CHECK_THROW(id.getKey(key1Name), Pib::Error);
+ BOOST_CHECK_EQUAL(id.getKeys().size(), 0);
+
+ // Create another key
+ m_keyChain.createKey(id);
+ // The added key becomes the default key.
+ BOOST_REQUIRE_NO_THROW(id.getDefaultKey());
+ Key key2 = id.getDefaultKey();
+ BOOST_REQUIRE(key2);
+ BOOST_CHECK_NE(key2.getName(), key1Name);
+ BOOST_CHECK_EQUAL(id.getKeys().size(), 1);
+ BOOST_REQUIRE_NO_THROW(key2.getDefaultCertificate());
+
+ // Create the third key
+ Key key3 = m_keyChain.createKey(id);
+ BOOST_CHECK_NE(key3.getName(), key2.getName());
+ // The added key will not be the default key, because the default key already exists
+ BOOST_CHECK_EQUAL(id.getDefaultKey().getName(), key2.getName());
+ BOOST_CHECK_EQUAL(id.getKeys().size(), 2);
+ BOOST_REQUIRE_NO_THROW(key3.getDefaultCertificate());
+
+ // Delete cert
+ BOOST_CHECK_EQUAL(key3.getCertificates().size(), 1);
+ Certificate key3Cert1 = *key3.getCertificates().begin();
+ Name key3CertName = key3Cert1.getName();
+ m_keyChain.deleteCertificate(key3, key3CertName);
+ BOOST_CHECK_EQUAL(key3.getCertificates().size(), 0);
+ BOOST_REQUIRE_THROW(key3.getDefaultCertificate(), Pib::Error);
+
+ // Add cert
+ m_keyChain.addCertificate(key3, key3Cert1);
+ BOOST_CHECK_EQUAL(key3.getCertificates().size(), 1);
+ BOOST_REQUIRE_NO_THROW(key3.getDefaultCertificate());
+ m_keyChain.addCertificate(key3, key3Cert1); // overwriting the cert should work
+ BOOST_CHECK_EQUAL(key3.getCertificates().size(), 1);
+ // Add another cert
+ Certificate key3Cert2 = key3Cert1;
+ Name key3Cert2Name = key3.getName();
+ key3Cert2Name.append("Self");
+ key3Cert2Name.appendVersion();
+ key3Cert2.setName(key3Cert2Name);
+ m_keyChain.addCertificate(key3, key3Cert2);
+ BOOST_CHECK_EQUAL(key3.getCertificates().size(), 2);
+
+ // Default certificate setting
+ BOOST_CHECK_EQUAL(key3.getDefaultCertificate().getName(), key3CertName);
+ m_keyChain.setDefaultCertificate(key3, key3Cert2);
+ BOOST_CHECK_EQUAL(key3.getDefaultCertificate().getName(), key3Cert2Name);
+
+ // Default key setting
+ BOOST_CHECK_EQUAL(id.getDefaultKey().getName(), key2.getName());
+ m_keyChain.setDefaultKey(id, key3);
+ BOOST_CHECK_EQUAL(id.getDefaultKey().getName(), key3.getName());
+
+ // Default identity setting
+ Identity id2 = m_keyChain.createIdentity(identity2Name);
+ BOOST_CHECK_EQUAL(m_keyChain.getPib().getDefaultIdentity().getName(), id.getName());
+ m_keyChain.setDefaultIdentity(id2);
+ BOOST_CHECK_EQUAL(m_keyChain.getPib().getDefaultIdentity().getName(), id2.getName());
+
+ // Delete identity
+ m_keyChain.deleteIdentity(id);
+ // The identity instance should not be valid any more
+ BOOST_CHECK(!id);
+ BOOST_REQUIRE_THROW(m_keyChain.getPib().getIdentity(identityName), Pib::Error);
+ BOOST_CHECK(m_keyChain.getPib().getIdentities().find(identityName) == m_keyChain.getPib().getIdentities().end());
+}
+
+BOOST_FIXTURE_TEST_CASE(GeneralSigningInterface, IdentityManagementFixture)
+{
+ Identity id = addIdentity("/id");
+ Key key = id.getDefaultKey();
+ Certificate cert = key.getDefaultCertificate();
+
+ std::list<SigningInfo> signingInfos = {
+ SigningInfo(),
+
+ SigningInfo(SigningInfo::SIGNER_TYPE_ID, id.getName()),
+ signingByIdentity(id.getName()),
+
+ SigningInfo(id),
+ signingByIdentity(id),
+
+ SigningInfo(SigningInfo::SIGNER_TYPE_KEY, key.getName()),
+ signingByKey(key.getName()),
+
+ SigningInfo(key),
+ signingByKey(key),
+
+ SigningInfo(SigningInfo::SIGNER_TYPE_CERT, cert.getName()),
+ signingByCertificate(cert.getName()),
+ signingByCertificate(cert),
+
+ SigningInfo(SigningInfo::SIGNER_TYPE_SHA256),
+ signingWithSha256()
+ };
+
+ for (const auto& signingInfo : signingInfos) {
+ BOOST_TEST_MESSAGE("SigningInfo: " << signingInfo);
+ Data data("/data");
+ Interest interest("/interest");
+
+ if (signingInfo.getSignerType() == SigningInfo::SIGNER_TYPE_NULL) {
+ m_keyChain.sign(data);
+ m_keyChain.sign(interest);
+ }
+ else {
+ m_keyChain.sign(data, signingInfo);
+ m_keyChain.sign(interest, signingInfo);
+ }
+
+ Signature interestSignature(interest.getName()[-2].blockFromValue(), interest.getName()[-1].blockFromValue());
+
+ if (signingInfo.getSignerType() == SigningInfo::SIGNER_TYPE_SHA256) {
+ BOOST_CHECK_EQUAL(data.getSignature().getType(), tlv::DigestSha256);
+ BOOST_CHECK_EQUAL(interestSignature.getType(), tlv::DigestSha256);
+
+ BOOST_CHECK(verifyDigest(data, DigestAlgorithm::SHA256));
+ BOOST_CHECK(verifyDigest(interest, DigestAlgorithm::SHA256));
+ }
+ else {
+ BOOST_CHECK_EQUAL(data.getSignature().getType(), tlv::SignatureSha256WithEcdsa);
+ BOOST_CHECK_EQUAL(interestSignature.getType(), tlv::SignatureSha256WithEcdsa);
+
+ BOOST_CHECK_EQUAL(data.getSignature().getKeyLocator().getName(), cert.getName().getPrefix(-2));
+ BOOST_CHECK_EQUAL(interestSignature.getKeyLocator().getName(), cert.getName().getPrefix(-2));
+
+ BOOST_CHECK(verifySignature(data, key));
+ BOOST_CHECK(verifySignature(interest, key));
+ }
+ }
+}
+
+BOOST_FIXTURE_TEST_CASE(PublicKeySigningDefaults, IdentityManagementFixture)
+{
+ Data data("/test/data");
+
+ // Identity will be created with generated key and self-signed cert with default parameters
+ BOOST_CHECK_THROW(m_keyChain.sign(data, signingByIdentity("/non-existing/identity")), KeyChain::InvalidSigningInfoError);
+
+ // Create identity with EC key and the corresponding self-signed certificate
+ Identity id = addIdentity("/ndn/test/ec", EcKeyParams());
+ BOOST_CHECK_NO_THROW(m_keyChain.sign(data, signingByIdentity(id.getName())));
+ BOOST_CHECK_EQUAL(data.getSignature().getType(),
+ KeyChain::getSignatureType(EcKeyParams().getKeyType(), DigestAlgorithm::SHA256));
+ BOOST_CHECK(id.getName().isPrefixOf(data.getSignature().getKeyLocator().getName()));
+
+ // Create identity with RSA key and the corresponding self-signed certificate
+ id = addIdentity("/ndn/test/rsa", RsaKeyParams());
+ BOOST_CHECK_NO_THROW(m_keyChain.sign(data, signingByIdentity(id.getName())));
+ BOOST_CHECK_EQUAL(data.getSignature().getType(),
+ KeyChain::getSignatureType(RsaKeyParams().getKeyType(), DigestAlgorithm::SHA256));
+ BOOST_CHECK(id.getName().isPrefixOf(data.getSignature().getKeyLocator().getName()));
+}
+
+BOOST_FIXTURE_TEST_CASE(ExportImport, IdentityManagementFixture)
+{
+ Identity id = addIdentity("/TestKeyChain/ExportIdentity/");
+ Certificate cert = id.getDefaultKey().getDefaultCertificate();
+
+ shared_ptr<SafeBag> exported = m_keyChain.exportSafeBag(cert, "1234", 4);
+ Block block = exported->wireEncode();
+
+ m_keyChain.deleteIdentity(id);
+ BOOST_CHECK_THROW(m_keyChain.exportSafeBag(cert, "1234", 4), KeyChain::Error);
+
+ BOOST_CHECK_EQUAL(m_keyChain.getTpm().hasKey(cert.getKeyName()), false);
+ BOOST_CHECK_EQUAL(m_keyChain.getPib().getIdentities().size(), 0);
+
+ SafeBag imported;
+ imported.wireDecode(block);
+ m_keyChain.importSafeBag(imported, "1234", 4);
+ BOOST_CHECK_THROW(m_keyChain.importSafeBag(imported, "1234", 4), KeyChain::Error);
+
+ BOOST_CHECK_EQUAL(m_keyChain.getTpm().hasKey(cert.getKeyName()), true);
+ BOOST_CHECK_EQUAL(m_keyChain.getPib().getIdentities().size(), 1);
+ BOOST_REQUIRE_NO_THROW(m_keyChain.getPib().getIdentity(cert.getIdentity()));
+ Identity newId = m_keyChain.getPib().getIdentity(cert.getIdentity());
+ BOOST_CHECK_EQUAL(newId.getKeys().size(), 1);
+ BOOST_REQUIRE_NO_THROW(newId.getKey(cert.getKeyName()));
+ Key newKey = newId.getKey(cert.getKeyName());
+ BOOST_CHECK_EQUAL(newKey.getCertificates().size(), 1);
+ BOOST_REQUIRE_NO_THROW(newKey.getCertificate(cert.getName()));
+
+ m_keyChain.deleteIdentity(newId);
+ BOOST_CHECK_EQUAL(m_keyChain.getPib().getIdentities().size(), 0);
+ BOOST_CHECK_EQUAL(m_keyChain.getTpm().hasKey(cert.getKeyName()), false);
+}
+
+BOOST_FIXTURE_TEST_CASE(SelfSignedCertValidity, IdentityManagementFixture)
+{
+ Certificate cert = addIdentity("/Security/V2/TestKeyChain/SelfSignedCertValidity")
+ .getDefaultKey()
+ .getDefaultCertificate();
+ BOOST_CHECK(cert.isValid());
+ BOOST_CHECK(cert.isValid(time::system_clock::now() + 10 * 365_days));
+ BOOST_CHECK_GT(cert.getValidityPeriod().getPeriod().second, time::system_clock::now() + 10 * 365_days);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestKeyChain
+BOOST_AUTO_TEST_SUITE_END() // V2
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace v2
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/v2/trust-anchor-container.t.cpp b/tests/unit/security/v2/trust-anchor-container.t.cpp
new file mode 100644
index 0000000..efd6ca3
--- /dev/null
+++ b/tests/unit/security/v2/trust-anchor-container.t.cpp
@@ -0,0 +1,198 @@
+/* -*- 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 "security/v2/trust-anchor-container.hpp"
+#include "util/io.hpp"
+
+#include "../../identity-management-time-fixture.hpp"
+#include "boost-test.hpp"
+
+#include <boost/filesystem.hpp>
+
+namespace ndn {
+namespace security {
+namespace v2 {
+namespace tests {
+
+using namespace ndn::tests;
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(V2)
+
+/**
+ * This fixture creates a directory and prepares two certificates.
+ * cert1 is written to a file under the directory, while cert2 is not.
+ */
+class AnchorContainerTestFixture : public IdentityManagementTimeFixture
+{
+public:
+ AnchorContainerTestFixture()
+ {
+ namespace fs = boost::filesystem;
+
+ fs::create_directory(fs::path(UNIT_TEST_CONFIG_PATH));
+
+ certDirPath = fs::path(UNIT_TEST_CONFIG_PATH) / "test-cert-dir";
+ fs::create_directory(certDirPath);
+
+ certPath1 = fs::path(UNIT_TEST_CONFIG_PATH) / "test-cert-dir" / "trust-anchor-1.cert";
+ certPath2 = fs::path(UNIT_TEST_CONFIG_PATH) / "test-cert-dir" / "trust-anchor-2.cert";
+
+ identity1 = addIdentity("/TestAnchorContainer/First");
+ cert1 = identity1.getDefaultKey().getDefaultCertificate();
+ saveCertToFile(cert1, certPath1.string());
+
+ identity2 = addIdentity("/TestAnchorContainer/Second");
+ cert2 = identity2.getDefaultKey().getDefaultCertificate();
+ saveCertToFile(cert2, certPath2.string());
+ }
+
+ ~AnchorContainerTestFixture()
+ {
+ boost::filesystem::remove_all(UNIT_TEST_CONFIG_PATH);
+ }
+
+public:
+ TrustAnchorContainer anchorContainer;
+
+ boost::filesystem::path certDirPath;
+ boost::filesystem::path certPath1;
+ boost::filesystem::path certPath2;
+
+ Identity identity1;
+ Identity identity2;
+
+ Certificate cert1;
+ Certificate cert2;
+};
+
+BOOST_FIXTURE_TEST_SUITE(TestTrustAnchorContainer, AnchorContainerTestFixture)
+
+// one static group and one dynamic group created from file
+BOOST_AUTO_TEST_CASE(Insert)
+{
+ // Static
+ anchorContainer.insert("group1", Certificate(cert1));
+ BOOST_CHECK(anchorContainer.find(cert1.getName()) != nullptr);
+ BOOST_CHECK(anchorContainer.find(identity1.getName()) != nullptr);
+ const Certificate* cert = anchorContainer.find(cert1.getName());
+ BOOST_CHECK_NO_THROW(anchorContainer.insert("group1", Certificate(cert1)));
+ BOOST_CHECK_EQUAL(cert, anchorContainer.find(cert1.getName())); // still the same instance of the certificate
+ // cannot add dynamic group when static already exists
+ BOOST_CHECK_THROW(anchorContainer.insert("group1", certPath1.string(), 1_s), TrustAnchorContainer::Error);
+ BOOST_CHECK_EQUAL(anchorContainer.getGroup("group1").size(), 1);
+ BOOST_CHECK_EQUAL(anchorContainer.size(), 1);
+
+ // From file
+ anchorContainer.insert("group2", certPath2.string(), 1_s);
+ BOOST_CHECK(anchorContainer.find(cert2.getName()) != nullptr);
+ BOOST_CHECK(anchorContainer.find(identity2.getName()) != nullptr);
+ BOOST_CHECK_THROW(anchorContainer.insert("group2", Certificate(cert2)), TrustAnchorContainer::Error);
+ BOOST_CHECK_THROW(anchorContainer.insert("group2", certPath2.string(), 1_s), TrustAnchorContainer::Error);
+ BOOST_CHECK_EQUAL(anchorContainer.getGroup("group2").size(), 1);
+ BOOST_CHECK_EQUAL(anchorContainer.size(), 2);
+
+ boost::filesystem::remove(certPath2);
+ advanceClocks(1_s, 11);
+
+ BOOST_CHECK(anchorContainer.find(identity2.getName()) == nullptr);
+ BOOST_CHECK(anchorContainer.find(cert2.getName()) == nullptr);
+ BOOST_CHECK_EQUAL(anchorContainer.getGroup("group2").size(), 0);
+ BOOST_CHECK_EQUAL(anchorContainer.size(), 1);
+
+ TrustAnchorGroup& group = anchorContainer.getGroup("group1");
+ auto staticGroup = dynamic_cast<StaticTrustAnchorGroup*>(&group);
+ BOOST_REQUIRE(staticGroup != nullptr);
+ BOOST_CHECK_EQUAL(staticGroup->size(), 1);
+ staticGroup->remove(cert1.getName());
+ BOOST_CHECK_EQUAL(staticGroup->size(), 0);
+ BOOST_CHECK_EQUAL(anchorContainer.size(), 0);
+
+ BOOST_CHECK_THROW(anchorContainer.getGroup("non-existing-group"), TrustAnchorContainer::Error);
+}
+
+BOOST_AUTO_TEST_CASE(DynamicAnchorFromDir)
+{
+ boost::filesystem::remove(certPath2);
+
+ anchorContainer.insert("group", certDirPath.string(), 1_s, true /* isDir */);
+
+ BOOST_CHECK(anchorContainer.find(identity1.getName()) != nullptr);
+ BOOST_CHECK(anchorContainer.find(identity2.getName()) == nullptr);
+ BOOST_CHECK_EQUAL(anchorContainer.getGroup("group").size(), 1);
+
+ saveCertToFile(cert2, certPath2.string());
+
+ advanceClocks(100_ms, 11);
+
+ BOOST_CHECK(anchorContainer.find(identity1.getName()) != nullptr);
+ BOOST_CHECK(anchorContainer.find(identity2.getName()) != nullptr);
+ BOOST_CHECK_EQUAL(anchorContainer.getGroup("group").size(), 2);
+
+ boost::filesystem::remove_all(certDirPath);
+
+ advanceClocks(100_ms, 11);
+
+ BOOST_CHECK(anchorContainer.find(identity1.getName()) == nullptr);
+ BOOST_CHECK(anchorContainer.find(identity2.getName()) == nullptr);
+ BOOST_CHECK_EQUAL(anchorContainer.getGroup("group").size(), 0);
+}
+
+BOOST_FIXTURE_TEST_CASE(FindByInterest, AnchorContainerTestFixture)
+{
+ anchorContainer.insert("group1", certPath1.string(), 1_s);
+ Interest interest(identity1.getName());
+ BOOST_CHECK(anchorContainer.find(interest) != nullptr);
+ Interest interest1(identity1.getName().getPrefix(-1));
+ BOOST_CHECK(anchorContainer.find(interest1) != nullptr);
+ Interest interest2(Name(identity1.getName()).appendVersion());
+ BOOST_CHECK(anchorContainer.find(interest2) == nullptr);
+
+ Certificate cert3 = addCertificate(identity1.getDefaultKey(), "3");
+ Certificate cert4 = addCertificate(identity1.getDefaultKey(), "4");
+ Certificate cert5 = addCertificate(identity1.getDefaultKey(), "5");
+
+ Certificate cert3Copy = cert3;
+ anchorContainer.insert("group2", std::move(cert3Copy));
+ anchorContainer.insert("group3", std::move(cert4));
+ anchorContainer.insert("group4", std::move(cert5));
+
+ Interest interest3(cert3.getKeyName());
+ const Certificate* foundCert = anchorContainer.find(interest3);
+ BOOST_REQUIRE(foundCert != nullptr);
+ BOOST_CHECK(interest3.getName().isPrefixOf(foundCert->getName()));
+ BOOST_CHECK_EQUAL(foundCert->getName(), cert3.getName());
+
+ interest3.setExclude(Exclude().excludeOne(cert3.getName().at(Certificate::ISSUER_ID_OFFSET)));
+ foundCert = anchorContainer.find(interest3);
+ BOOST_REQUIRE(foundCert != nullptr);
+ BOOST_CHECK(interest3.getName().isPrefixOf(foundCert->getName()));
+ BOOST_CHECK_NE(foundCert->getName(), cert3.getName());
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestTrustAnchorContainer
+BOOST_AUTO_TEST_SUITE_END() // Detail
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace v2
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/v2/validation-error.t.cpp b/tests/unit/security/v2/validation-error.t.cpp
new file mode 100644
index 0000000..1db3aac
--- /dev/null
+++ b/tests/unit/security/v2/validation-error.t.cpp
@@ -0,0 +1,61 @@
+/* -*- 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 "security/v2/validation-error.hpp"
+
+#include "boost-test.hpp"
+#include <boost/lexical_cast.hpp>
+
+namespace ndn {
+namespace security {
+namespace v2 {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(V2)
+BOOST_AUTO_TEST_SUITE(TestValidationError)
+
+BOOST_AUTO_TEST_CASE(Basic)
+{
+ ValidationError e1{ValidationError::Code::INVALID_SIGNATURE};
+ BOOST_CHECK_EQUAL(e1.getCode(), 1);
+ BOOST_CHECK_EQUAL(e1.getInfo(), "");
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(e1), "Invalid signature");
+
+ ValidationError e2{ValidationError::Code::NO_SIGNATURE, "message"};
+ BOOST_CHECK_EQUAL(e2.getCode(), 2);
+ BOOST_CHECK_EQUAL(e2.getInfo(), "message");
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(e2), "Missing signature (message)");
+
+ ValidationError e3{65535, "other message"};
+ BOOST_CHECK_EQUAL(e3.getCode(), 65535);
+ BOOST_CHECK_EQUAL(e3.getInfo(), "other message");
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(e3), "Custom error code 65535 (other message)");
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestValidationError
+BOOST_AUTO_TEST_SUITE_END() // V2
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace v2
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/v2/validation-policy-accept-all.t.cpp b/tests/unit/security/v2/validation-policy-accept-all.t.cpp
new file mode 100644
index 0000000..61466d6
--- /dev/null
+++ b/tests/unit/security/v2/validation-policy-accept-all.t.cpp
@@ -0,0 +1,79 @@
+/* -*- 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 "security/v2/validation-policy-accept-all.hpp"
+
+#include "boost-test.hpp"
+#include "validator-fixture.hpp"
+
+#include <boost/mpl/vector.hpp>
+
+namespace ndn {
+namespace security {
+namespace v2 {
+namespace tests {
+
+using namespace ndn::tests;
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(V2)
+
+class ValidationPolicyAcceptAllFixture : public ValidatorFixture<ValidationPolicyAcceptAll>
+{
+public:
+ ValidationPolicyAcceptAllFixture()
+ {
+ identity = addIdentity("/Security/V2/TestValidationPolicyAcceptAll");
+ // don't add trust anchors
+ }
+
+public:
+ Identity identity;
+};
+
+BOOST_FIXTURE_TEST_SUITE(TestValidationPolicyAcceptAll, ValidationPolicyAcceptAllFixture)
+
+typedef boost::mpl::vector<Interest, Data> Packets;
+
+BOOST_AUTO_TEST_CASE_TEMPLATE(Validate, Packet, Packets)
+{
+ Packet unsignedPacket("/Security/V2/TestValidationPolicyAcceptAll/Sub/Packet");
+
+ Packet packet = unsignedPacket;
+ VALIDATE_SUCCESS(packet, "Should accept unsigned");
+
+ packet = unsignedPacket;
+ m_keyChain.sign(packet, signingWithSha256());
+ VALIDATE_SUCCESS(packet, "Should accept Sha256Digest signature");
+
+ packet = unsignedPacket;
+ m_keyChain.sign(packet, signingByIdentity(identity));
+ VALIDATE_SUCCESS(packet, "Should accept signature while no trust anchors configured");
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestValidationPolicyAcceptAll
+BOOST_AUTO_TEST_SUITE_END() // V2
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace v2
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/v2/validation-policy-command-interest.t.cpp b/tests/unit/security/v2/validation-policy-command-interest.t.cpp
new file mode 100644
index 0000000..7bd42ea
--- /dev/null
+++ b/tests/unit/security/v2/validation-policy-command-interest.t.cpp
@@ -0,0 +1,485 @@
+/* -*- 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 "security/v2/validation-policy-command-interest.hpp"
+#include "security/v2/validation-policy-simple-hierarchy.hpp"
+#include "security/v2/validation-policy-accept-all.hpp"
+#include "security/command-interest-signer.hpp"
+#include "security/signing-helpers.hpp"
+
+#include "boost-test.hpp"
+#include "validator-fixture.hpp"
+#include "make-interest-data.hpp"
+
+#include <boost/lexical_cast.hpp>
+#include <boost/mpl/vector.hpp>
+
+namespace ndn {
+namespace security {
+namespace v2 {
+namespace tests {
+
+using namespace ndn::tests;
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(V2)
+
+class DefaultOptions
+{
+public:
+ static ValidationPolicyCommandInterest::Options
+ getOptions()
+ {
+ return {};
+ }
+};
+
+template<class T, class InnerPolicy>
+class CommandInterestPolicyWrapper : public ValidationPolicyCommandInterest
+{
+public:
+ CommandInterestPolicyWrapper()
+ : ValidationPolicyCommandInterest(make_unique<InnerPolicy>(), T::getOptions())
+ {
+ }
+};
+
+template<class T, class InnerPolicy = ValidationPolicySimpleHierarchy>
+class ValidationPolicyCommandInterestFixture : public HierarchicalValidatorFixture<CommandInterestPolicyWrapper<T, InnerPolicy>>
+{
+public:
+ ValidationPolicyCommandInterestFixture()
+ : m_signer(this->m_keyChain)
+ {
+ }
+
+ Interest
+ makeCommandInterest(const Identity& identity)
+ {
+ return m_signer.makeCommandInterest(Name(identity.getName()).append("CMD"),
+ signingByIdentity(identity));
+ }
+
+public:
+ CommandInterestSigner m_signer;
+};
+
+BOOST_FIXTURE_TEST_SUITE(TestValidationPolicyCommandInterest, ValidationPolicyCommandInterestFixture<DefaultOptions>)
+
+BOOST_AUTO_TEST_SUITE(Accepts)
+
+BOOST_AUTO_TEST_CASE(Basic)
+{
+ auto i1 = makeCommandInterest(identity);
+ VALIDATE_SUCCESS(i1, "Should succeed (within grace period)");
+ VALIDATE_FAILURE(i1, "Should fail (replay attack)");
+
+ advanceClocks(5_ms);
+ auto i2 = makeCommandInterest(identity);
+ VALIDATE_SUCCESS(i2, "Should succeed (timestamp larger than previous)");
+
+ auto i3 = m_signer.makeCommandInterest(Name(identity.getName()).append("CMD"), signingWithSha256());
+ VALIDATE_FAILURE(i3, "Should fail (Sha256 signature violates policy)");
+}
+
+BOOST_AUTO_TEST_CASE(DataPassthru)
+{
+ Data d1("/Security/V2/ValidatorFixture/Sub1");
+ m_keyChain.sign(d1);
+ VALIDATE_SUCCESS(d1, "Should succeed (fallback on inner validation policy for data)");
+}
+
+using ValidationPolicyAcceptAllCommands = ValidationPolicyCommandInterestFixture<DefaultOptions,
+ ValidationPolicyAcceptAll>;
+
+BOOST_FIXTURE_TEST_CASE(SignedWithSha256, ValidationPolicyAcceptAllCommands) // Bug 4635
+{
+ auto i1 = m_signer.makeCommandInterest("/hello/world/CMD", signingWithSha256());
+ VALIDATE_SUCCESS(i1, "Should succeed (within grace period)");
+ VALIDATE_FAILURE(i1, "Should fail (replay attack)");
+
+ advanceClocks(5_ms);
+ auto i2 = m_signer.makeCommandInterest("/hello/world/CMD", signingWithSha256());
+ VALIDATE_SUCCESS(i2, "Should succeed (timestamp larger than previous)");
+}
+
+BOOST_AUTO_TEST_SUITE_END() // Accepts
+
+BOOST_AUTO_TEST_SUITE(Rejects)
+
+BOOST_AUTO_TEST_CASE(NameTooShort)
+{
+ auto i1 = makeInterest("/name/too/short");
+ VALIDATE_FAILURE(*i1, "Should fail (name is too short)");
+}
+
+BOOST_AUTO_TEST_CASE(BadTimestamp)
+{
+ auto i1 = makeCommandInterest(identity);
+ setNameComponent(i1, command_interest::POS_TIMESTAMP, "not-timestamp");
+ VALIDATE_FAILURE(i1, "Should fail (timestamp is missing)");
+}
+
+BOOST_AUTO_TEST_CASE(BadSigInfo)
+{
+ auto i1 = makeCommandInterest(identity);
+ setNameComponent(i1, command_interest::POS_SIG_INFO, "not-SignatureInfo");
+ VALIDATE_FAILURE(i1, "Should fail (signature info is missing)");
+}
+
+BOOST_AUTO_TEST_CASE(MissingKeyLocator)
+{
+ auto i1 = makeCommandInterest(identity);
+ SignatureInfo sigInfo(tlv::SignatureSha256WithRsa);
+ setNameComponent(i1, command_interest::POS_SIG_INFO,
+ sigInfo.wireEncode().begin(), sigInfo.wireEncode().end());
+ VALIDATE_FAILURE(i1, "Should fail (missing KeyLocator)");
+}
+
+BOOST_AUTO_TEST_CASE(BadKeyLocatorType)
+{
+ auto i1 = makeCommandInterest(identity);
+ KeyLocator kl;
+ kl.setKeyDigest(makeBinaryBlock(tlv::KeyDigest, "\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD", 8));
+ SignatureInfo sigInfo(tlv::SignatureSha256WithRsa);
+ sigInfo.setKeyLocator(kl);
+ setNameComponent(i1, command_interest::POS_SIG_INFO,
+ sigInfo.wireEncode().begin(), sigInfo.wireEncode().end());
+ VALIDATE_FAILURE(i1, "Should fail (bad KeyLocator type)");
+}
+
+BOOST_AUTO_TEST_CASE(BadCertName)
+{
+ auto i1 = makeCommandInterest(identity);
+ KeyLocator kl;
+ kl.setName("/bad/cert/name");
+ SignatureInfo sigInfo(tlv::SignatureSha256WithRsa);
+ sigInfo.setKeyLocator(kl);
+ setNameComponent(i1, command_interest::POS_SIG_INFO,
+ sigInfo.wireEncode().begin(), sigInfo.wireEncode().end());
+ VALIDATE_FAILURE(i1, "Should fail (bad certificate name)");
+}
+
+BOOST_AUTO_TEST_CASE(InnerPolicyReject)
+{
+ auto i1 = makeCommandInterest(otherIdentity);
+ VALIDATE_FAILURE(i1, "Should fail (inner policy should reject)");
+}
+
+class GracePeriod15Sec
+{
+public:
+ static ValidationPolicyCommandInterest::Options
+ getOptions()
+ {
+ ValidationPolicyCommandInterest::Options options;
+ options.gracePeriod = 15_s;
+ return options;
+ }
+};
+
+BOOST_FIXTURE_TEST_CASE(TimestampOutOfGracePositive, ValidationPolicyCommandInterestFixture<GracePeriod15Sec>)
+{
+ auto i1 = makeCommandInterest(identity); // signed at 0s
+ advanceClocks(16_s); // verifying at +16s
+ VALIDATE_FAILURE(i1, "Should fail (timestamp outside the grace period)");
+ rewindClockAfterValidation();
+
+ auto i2 = makeCommandInterest(identity); // signed at +16s
+ VALIDATE_SUCCESS(i2, "Should succeed");
+}
+
+BOOST_FIXTURE_TEST_CASE(TimestampOutOfGraceNegative, ValidationPolicyCommandInterestFixture<GracePeriod15Sec>)
+{
+ auto i1 = makeCommandInterest(identity); // signed at 0s
+ advanceClocks(1_s);
+ auto i2 = makeCommandInterest(identity); // signed at +1s
+ advanceClocks(1_s);
+ auto i3 = makeCommandInterest(identity); // signed at +2s
+
+ systemClock->advance(-18_s); // verifying at -16s
+ VALIDATE_FAILURE(i1, "Should fail (timestamp outside the grace period)");
+ rewindClockAfterValidation();
+
+ // CommandInterestValidator should not remember i1's timestamp
+ VALIDATE_FAILURE(i2, "Should fail (timestamp outside the grace period)");
+ rewindClockAfterValidation();
+
+ // CommandInterestValidator should not remember i2's timestamp, and should treat i3 as initial
+ advanceClocks(18_s); // verifying at +2s
+ VALIDATE_SUCCESS(i3, "Should succeed");
+}
+
+BOOST_AUTO_TEST_CASE(TimestampReorderEqual)
+{
+ auto i1 = makeCommandInterest(identity); // signed at 0s
+ VALIDATE_SUCCESS(i1, "Should succeed");
+
+ auto i2 = makeCommandInterest(identity); // signed at 0s
+ setNameComponent(i2, command_interest::POS_TIMESTAMP,
+ i1.getName()[command_interest::POS_TIMESTAMP]);
+ VALIDATE_FAILURE(i2, "Should fail (timestamp reordered)");
+
+ advanceClocks(2_s);
+ auto i3 = makeCommandInterest(identity); // signed at +2s
+ VALIDATE_SUCCESS(i3, "Should succeed");
+}
+
+BOOST_AUTO_TEST_CASE(TimestampReorderNegative)
+{
+ auto i2 = makeCommandInterest(identity); // signed at 0ms
+ advanceClocks(200_ms);
+ auto i3 = makeCommandInterest(identity); // signed at +200ms
+ advanceClocks(900_ms);
+ auto i1 = makeCommandInterest(identity); // signed at +1100ms
+ advanceClocks(300_ms);
+ auto i4 = makeCommandInterest(identity); // signed at +1400ms
+
+ systemClock->advance(-300_ms); // verifying at +1100ms
+ VALIDATE_SUCCESS(i1, "Should succeed");
+ rewindClockAfterValidation();
+
+ systemClock->advance(-1100_ms); // verifying at 0ms
+ VALIDATE_FAILURE(i2, "Should fail (timestamp reordered)");
+ rewindClockAfterValidation();
+
+ // CommandInterestValidator should not remember i2's timestamp
+ advanceClocks(200_ms); // verifying at +200ms
+ VALIDATE_FAILURE(i3, "Should fail (timestamp reordered)");
+ rewindClockAfterValidation();
+
+ advanceClocks(1200_ms); // verifying at 1400ms
+ VALIDATE_SUCCESS(i4, "Should succeed");
+}
+
+BOOST_AUTO_TEST_SUITE_END() // Rejects
+
+BOOST_AUTO_TEST_SUITE(Options)
+
+template<class T>
+class GracePeriod
+{
+public:
+ static ValidationPolicyCommandInterest::Options
+ getOptions()
+ {
+ ValidationPolicyCommandInterest::Options options;
+ options.gracePeriod = time::seconds(T::value);
+ return options;
+ }
+};
+
+typedef boost::mpl::vector<
+ GracePeriod<boost::mpl::int_<0>>,
+ GracePeriod<boost::mpl::int_<-1>>
+> GraceNonPositiveValues;
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE(GraceNonPositive, GracePeriod, GraceNonPositiveValues,
+ ValidationPolicyCommandInterestFixture<GracePeriod>)
+{
+ auto i1 = this->makeCommandInterest(this->identity); // signed at 0ms
+ auto i2 = this->makeCommandInterest(this->subIdentity); // signed at 0ms
+ for (auto interest : {&i1, &i2}) {
+ setNameComponent(*interest, command_interest::POS_TIMESTAMP,
+ name::Component::fromNumber(time::toUnixTimestamp(time::system_clock::now()).count()));
+ } // ensure timestamps are exactly 0ms
+
+ VALIDATE_SUCCESS(i1, "Should succeed when validating at 0ms");
+ this->rewindClockAfterValidation();
+
+ this->advanceClocks(1_ms);
+ VALIDATE_FAILURE(i2, "Should fail when validating at 1ms");
+}
+
+class LimitedRecordsOptions
+{
+public:
+ static ValidationPolicyCommandInterest::Options
+ getOptions()
+ {
+ ValidationPolicyCommandInterest::Options options;
+ options.gracePeriod = 15_s;
+ options.maxRecords = 3;
+ return options;
+ }
+};
+
+BOOST_FIXTURE_TEST_CASE(LimitedRecords, ValidationPolicyCommandInterestFixture<LimitedRecordsOptions>)
+{
+ Identity id1 = this->addSubCertificate("/Security/V2/ValidatorFixture/Sub1", identity);
+ this->cache.insert(id1.getDefaultKey().getDefaultCertificate());
+ Identity id2 = this->addSubCertificate("/Security/V2/ValidatorFixture/Sub2", identity);
+ this->cache.insert(id2.getDefaultKey().getDefaultCertificate());
+ Identity id3 = this->addSubCertificate("/Security/V2/ValidatorFixture/Sub3", identity);
+ this->cache.insert(id3.getDefaultKey().getDefaultCertificate());
+ Identity id4 = this->addSubCertificate("/Security/V2/ValidatorFixture/Sub4", identity);
+ this->cache.insert(id4.getDefaultKey().getDefaultCertificate());
+
+ auto i1 = makeCommandInterest(id2);
+ auto i2 = makeCommandInterest(id3);
+ auto i3 = makeCommandInterest(id4);
+ auto i00 = makeCommandInterest(id1); // signed at 0s
+ advanceClocks(1_s);
+ auto i01 = makeCommandInterest(id1); // signed at 1s
+ advanceClocks(1_s);
+ auto i02 = makeCommandInterest(id1); // signed at 2s
+
+ VALIDATE_SUCCESS(i00, "Should succeed");
+ rewindClockAfterValidation();
+
+ VALIDATE_SUCCESS(i02, "Should succeed");
+ rewindClockAfterValidation();
+
+ VALIDATE_SUCCESS(i1, "Should succeed");
+ rewindClockAfterValidation();
+
+ VALIDATE_SUCCESS(i2, "Should succeed");
+ rewindClockAfterValidation();
+
+ VALIDATE_SUCCESS(i3, "Should succeed, forgets identity id1");
+ rewindClockAfterValidation();
+
+ VALIDATE_SUCCESS(i01, "Should succeed despite timestamp is reordered, because record has been evicted");
+}
+
+class UnlimitedRecordsOptions
+{
+public:
+ static ValidationPolicyCommandInterest::Options
+ getOptions()
+ {
+ ValidationPolicyCommandInterest::Options options;
+ options.gracePeriod = 15_s;
+ options.maxRecords = -1;
+ return options;
+ }
+};
+
+BOOST_FIXTURE_TEST_CASE(UnlimitedRecords, ValidationPolicyCommandInterestFixture<UnlimitedRecordsOptions>)
+{
+ std::vector<Identity> identities;
+ for (int i = 0; i < 20; ++i) {
+ Identity id = this->addSubCertificate("/Security/V2/ValidatorFixture/Sub" + to_string(i), identity);
+ this->cache.insert(id.getDefaultKey().getDefaultCertificate());
+ identities.push_back(id);
+ }
+
+ auto i1 = makeCommandInterest(identities.at(0)); // signed at 0s
+ advanceClocks(1_s);
+ for (int i = 0; i < 20; ++i) {
+ auto i2 = makeCommandInterest(identities.at(i)); // signed at +1s
+
+ VALIDATE_SUCCESS(i2, "Should succeed");
+ rewindClockAfterValidation();
+ }
+ VALIDATE_FAILURE(i1, "Should fail (timestamp reorder)");
+}
+
+class ZeroRecordsOptions
+{
+public:
+ static ValidationPolicyCommandInterest::Options
+ getOptions()
+ {
+ ValidationPolicyCommandInterest::Options options;
+ options.gracePeriod = 15_s;
+ options.maxRecords = 0;
+ return options;
+ }
+};
+
+BOOST_FIXTURE_TEST_CASE(ZeroRecords, ValidationPolicyCommandInterestFixture<ZeroRecordsOptions>)
+{
+ auto i1 = makeCommandInterest(identity); // signed at 0s
+ advanceClocks(1_s);
+ auto i2 = makeCommandInterest(identity); // signed at +1s
+ VALIDATE_SUCCESS(i2, "Should succeed");
+ rewindClockAfterValidation();
+
+ VALIDATE_SUCCESS(i1, "Should succeed despite timestamp is reordered, because record isn't kept");
+}
+
+class LimitedRecordLifetimeOptions
+{
+public:
+ static ValidationPolicyCommandInterest::Options
+ getOptions()
+ {
+ ValidationPolicyCommandInterest::Options options;
+ options.gracePeriod = 400_s;
+ options.recordLifetime = 300_s;
+ return options;
+ }
+};
+
+BOOST_FIXTURE_TEST_CASE(LimitedRecordLifetime, ValidationPolicyCommandInterestFixture<LimitedRecordLifetimeOptions>)
+{
+ auto i1 = makeCommandInterest(identity); // signed at 0s
+ advanceClocks(240_s);
+ auto i2 = makeCommandInterest(identity); // signed at +240s
+ advanceClocks(120_s);
+ auto i3 = makeCommandInterest(identity); // signed at +360s
+
+ systemClock->advance(-360_s); // rewind system clock to 0s
+ VALIDATE_SUCCESS(i1, "Should succeed");
+ rewindClockAfterValidation();
+
+ VALIDATE_SUCCESS(i3, "Should succeed");
+ rewindClockAfterValidation();
+
+ advanceClocks(30_s, 301_s); // advance steady clock by 301s, and system clock to +301s
+ VALIDATE_SUCCESS(i2, "Should succeed despite timestamp is reordered, because record has been expired");
+}
+
+class ZeroRecordLifetimeOptions
+{
+public:
+ static ValidationPolicyCommandInterest::Options
+ getOptions()
+ {
+ ValidationPolicyCommandInterest::Options options;
+ options.gracePeriod = 15_s;
+ options.recordLifetime = time::seconds::zero();
+ return options;
+ }
+};
+
+BOOST_FIXTURE_TEST_CASE(ZeroRecordLifetime, ValidationPolicyCommandInterestFixture<ZeroRecordLifetimeOptions>)
+{
+ auto i1 = makeCommandInterest(identity); // signed at 0s
+ advanceClocks(1_s);
+ auto i2 = makeCommandInterest(identity); // signed at +1s
+ VALIDATE_SUCCESS(i2, "Should succeed");
+ rewindClockAfterValidation();
+
+ VALIDATE_SUCCESS(i1, "Should succeed despite timestamp is reordered, because record has been expired");
+}
+
+BOOST_AUTO_TEST_SUITE_END() // Options
+
+BOOST_AUTO_TEST_SUITE_END() // TestValidationPolicyCommandInterest
+BOOST_AUTO_TEST_SUITE_END() // V2
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace v2
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/v2/validation-policy-config.t.cpp b/tests/unit/security/v2/validation-policy-config.t.cpp
new file mode 100644
index 0000000..cca3e7a
--- /dev/null
+++ b/tests/unit/security/v2/validation-policy-config.t.cpp
@@ -0,0 +1,539 @@
+/* -*- 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 "security/v2/validation-policy-config.hpp"
+#include "security/transform/base64-encode.hpp"
+#include "security/transform/buffer-source.hpp"
+#include "security/transform/stream-sink.hpp"
+#include "util/logger.hpp"
+#include "util/io.hpp"
+
+#include "boost-test.hpp"
+#include "validator-config/common.hpp"
+#include "validator-fixture.hpp"
+
+namespace ndn {
+namespace security {
+namespace v2 {
+namespace validator_config {
+namespace tests {
+
+using namespace ndn::tests;
+using namespace ndn::security::v2::tests;
+
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(V2)
+BOOST_AUTO_TEST_SUITE(TestValidationPolicyConfig)
+
+template<typename Packet>
+class PacketName;
+
+template<>
+class PacketName<Interest>
+{
+public:
+ static std::string
+ getName()
+ {
+ return "interest";
+ }
+};
+
+template<>
+class PacketName<Data>
+{
+public:
+ static std::string
+ getName()
+ {
+ return "data";
+ }
+};
+
+template<typename PacketType>
+class ValidationPolicyConfigFixture : public HierarchicalValidatorFixture<ValidationPolicyConfig>
+{
+public:
+ ValidationPolicyConfigFixture()
+ : path(boost::filesystem::path(UNIT_TEST_CONFIG_PATH) / "security" / "v2" / "validation-policy-config")
+ {
+ boost::filesystem::create_directories(path);
+ baseConfig = R"CONF(
+ rule
+ {
+ id test-rule-id
+ for )CONF" + PacketName<Packet>::getName() + R"CONF(
+ filter
+ {
+ type name
+ name )CONF" + identity.getName().toUri() + R"CONF(
+ relation is-prefix-of
+ }
+ checker
+ {
+ type hierarchical
+ sig-type rsa-sha256
+ }
+ }
+ )CONF";
+ }
+
+ ~ValidationPolicyConfigFixture()
+ {
+ boost::filesystem::remove_all(path);
+ }
+
+public:
+ using Packet = PacketType;
+
+ const boost::filesystem::path path;
+ std::string baseConfig;
+};
+
+template<typename PacketType>
+class LoadStringWithFileAnchor : public ValidationPolicyConfigFixture<PacketType>
+{
+public:
+ LoadStringWithFileAnchor()
+ {
+ BOOST_CHECK_EQUAL(this->policy.m_isConfigured, false);
+
+ this->saveCertificate(this->identity, (this->path / "identity.ndncert").string());
+ this->policy.load(this->baseConfig + R"CONF(
+ trust-anchor
+ {
+ type file
+ file-name "trust-anchor.ndncert"
+ }
+ )CONF", (this->path / "test-config").string());
+
+ BOOST_CHECK_EQUAL(this->policy.m_isConfigured, true);
+ BOOST_CHECK_EQUAL(this->policy.m_shouldBypass, false);
+ }
+};
+
+template<typename PacketType>
+class LoadFileWithFileAnchor : public ValidationPolicyConfigFixture<PacketType>
+{
+public:
+ LoadFileWithFileAnchor()
+ {
+ std::string configFile = (this->path / "config.conf").string();
+ {
+ std::ofstream config(configFile.c_str());
+ config << this->baseConfig << R"CONF(
+ trust-anchor
+ {
+ type file
+ file-name "trust-anchor.ndncert"
+ }
+ )CONF";
+ }
+ this->saveCertificate(this->identity, (this->path / "identity.ndncert").string());
+
+ BOOST_CHECK_EQUAL(this->policy.m_isConfigured, false);
+
+ this->policy.load(configFile);
+
+ BOOST_CHECK_EQUAL(this->policy.m_isConfigured, true);
+ BOOST_CHECK_EQUAL(this->policy.m_shouldBypass, false);
+ }
+};
+
+template<typename PacketType>
+class LoadSectionWithFileAnchor : public ValidationPolicyConfigFixture<PacketType>
+{
+public:
+ LoadSectionWithFileAnchor()
+ {
+ auto section = makeSection(this->baseConfig + R"CONF(
+ trust-anchor
+ {
+ type file
+ file-name "trust-anchor.ndncert"
+ }
+ )CONF");
+
+ this->saveCertificate(this->identity, (this->path / "identity.ndncert").string());
+
+ BOOST_CHECK_EQUAL(this->policy.m_isConfigured, false);
+
+ this->policy.load(section, (this->path / "test-config").string());
+
+ BOOST_CHECK_EQUAL(this->policy.m_isConfigured, true);
+ BOOST_CHECK_EQUAL(this->policy.m_shouldBypass, false);
+ }
+};
+
+template<typename PacketType>
+class LoadStringWithBase64Anchor : public ValidationPolicyConfigFixture<PacketType>
+{
+public:
+ LoadStringWithBase64Anchor()
+ {
+ BOOST_CHECK_EQUAL(this->policy.m_isConfigured, false);
+
+ std::ostringstream os;
+ using namespace ndn::security::transform;
+ const auto& cert = this->identity.getDefaultKey().getDefaultCertificate();
+ bufferSource(cert.wireEncode().wire(), cert.wireEncode().size()) >> base64Encode(false) >> streamSink(os);
+
+ this->policy.load(this->baseConfig + R"CONF(
+ trust-anchor
+ {
+ type base64
+ base64-string ")CONF" + os.str() + R"CONF("
+ }
+ )CONF", (this->path / "test-config").string());
+
+ BOOST_CHECK_EQUAL(this->policy.m_isConfigured, true);
+ BOOST_CHECK_EQUAL(this->policy.m_shouldBypass, false);
+ }
+};
+
+class NoRefresh
+{
+public:
+ static std::string
+ getRefreshString()
+ {
+ return "";
+ }
+};
+
+class Refresh1h
+{
+public:
+ static std::string
+ getRefreshString()
+ {
+ return "refresh 1h";
+ }
+
+ static time::milliseconds
+ getRefreshTime()
+ {
+ return 1_h;
+ }
+};
+
+class Refresh1m
+{
+public:
+ static std::string
+ getRefreshString()
+ {
+ return "refresh 1m";
+ }
+
+ static time::milliseconds
+ getRefreshTime()
+ {
+ return 1_min;
+ }
+};
+
+class Refresh1s
+{
+public:
+ static std::string
+ getRefreshString()
+ {
+ return "refresh 1s";
+ }
+
+ static time::milliseconds
+ getRefreshTime()
+ {
+ return 1_s;
+ }
+};
+
+template<typename PacketType, typename Refresh = NoRefresh>
+class LoadStringWithDirAnchor : public ValidationPolicyConfigFixture<PacketType>
+{
+public:
+ LoadStringWithDirAnchor()
+ {
+ BOOST_CHECK_EQUAL(this->policy.m_isConfigured, false);
+
+ boost::filesystem::create_directories(this->path / "keys");
+ this->saveCertificate(this->identity, (this->path / "keys" / "identity.ndncert").string());
+
+ this->policy.load(this->baseConfig + R"CONF(
+ trust-anchor
+ {
+ type dir
+ dir keys
+ )CONF" + Refresh::getRefreshString() + R"CONF(
+ }
+ )CONF", (this->path / "test-config").string());
+
+ BOOST_CHECK_EQUAL(this->policy.m_isConfigured, true);
+ BOOST_CHECK_EQUAL(this->policy.m_shouldBypass, false);
+ }
+};
+
+using DataPolicies = boost::mpl::vector<LoadStringWithFileAnchor<Data>,
+ LoadFileWithFileAnchor<Data>,
+ LoadSectionWithFileAnchor<Data>,
+ LoadStringWithBase64Anchor<Data>,
+ LoadStringWithDirAnchor<Data>,
+ LoadStringWithDirAnchor<Data, Refresh1h>,
+ LoadStringWithDirAnchor<Data, Refresh1m>,
+ LoadStringWithDirAnchor<Data, Refresh1s>
+ >;
+
+using InterestPolicies = boost::mpl::vector<LoadStringWithFileAnchor<Interest>,
+ LoadFileWithFileAnchor<Interest>,
+ LoadSectionWithFileAnchor<Interest>,
+ LoadStringWithBase64Anchor<Interest>,
+ LoadStringWithDirAnchor<Interest>,
+ LoadStringWithDirAnchor<Interest, Refresh1h>,
+ LoadStringWithDirAnchor<Interest, Refresh1m>,
+ LoadStringWithDirAnchor<Interest, Refresh1s>
+ >;
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE(ValidateData, Policy, DataPolicies, Policy)
+{
+ BOOST_CHECK_EQUAL(this->policy.m_dataRules.size(), 1);
+ BOOST_CHECK_EQUAL(this->policy.m_interestRules.size(), 0);
+
+ using Packet = typename Policy::Packet;
+ Packet unsignedPacket("/Security/V2/ValidatorFixture/Sub1/Sub2/Packet");
+
+ Packet packet = unsignedPacket;
+ VALIDATE_FAILURE(packet, "Unsigned");
+
+ packet = unsignedPacket;
+ this->m_keyChain.sign(packet, signingWithSha256());
+ VALIDATE_FAILURE(packet, "Policy doesn't accept Sha256Digest signature");
+
+ packet = unsignedPacket;
+ this->m_keyChain.sign(packet, signingByIdentity(this->identity));
+ VALIDATE_SUCCESS(packet, "Should get accepted, as signed by the anchor");
+
+ packet = unsignedPacket;
+ this->m_keyChain.sign(packet, signingByIdentity(this->subIdentity));
+ VALIDATE_SUCCESS(packet, "Should get accepted, as signed by the policy-compliant cert");
+
+ packet = unsignedPacket;
+ this->m_keyChain.sign(packet, signingByIdentity(this->otherIdentity));
+ VALIDATE_FAILURE(packet, "Should fail, as signed by the policy-violating cert");
+
+ packet = unsignedPacket;
+ this->m_keyChain.sign(packet, signingByIdentity(this->subSelfSignedIdentity));
+ VALIDATE_FAILURE(packet, "Should fail, because subSelfSignedIdentity is not a trust anchor");
+}
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE(ValidateInterest, Policy, InterestPolicies, Policy)
+{
+ BOOST_CHECK_EQUAL(this->policy.m_dataRules.size(), 0);
+ BOOST_CHECK_EQUAL(this->policy.m_interestRules.size(), 1);
+
+ using Packet = typename Policy::Packet;
+ Packet unsignedPacket("/Security/V2/ValidatorFixture/Sub1/Sub2/Packet");
+
+ Packet packet = unsignedPacket;
+ VALIDATE_FAILURE(packet, "Unsigned");
+
+ packet = unsignedPacket;
+ this->m_keyChain.sign(packet, signingWithSha256());
+ VALIDATE_FAILURE(packet, "Policy doesn't accept Sha256Digest signature");
+
+ packet = unsignedPacket;
+ this->m_keyChain.sign(packet, signingByIdentity(this->identity));
+ VALIDATE_SUCCESS(packet, "Should get accepted, as signed by the anchor");
+
+ packet = unsignedPacket;
+ this->m_keyChain.sign(packet, signingByIdentity(this->subIdentity));
+ VALIDATE_FAILURE(packet, "Should fail, as there is no matching rule for data");
+
+ packet = unsignedPacket;
+ this->m_keyChain.sign(packet, signingByIdentity(this->otherIdentity));
+ VALIDATE_FAILURE(packet, "Should fail, as signed by the policy-violating cert");
+
+ packet = unsignedPacket;
+ this->m_keyChain.sign(packet, signingByIdentity(this->subSelfSignedIdentity));
+ VALIDATE_FAILURE(packet, "Should fail, because subSelfSignedIdentity is not a trust anchor");
+}
+
+BOOST_FIXTURE_TEST_CASE(Reload, HierarchicalValidatorFixture<ValidationPolicyConfig>)
+{
+ BOOST_CHECK_EQUAL(this->policy.m_isConfigured, false);
+ this->policy.load(R"CONF(
+ rule
+ {
+ id test-rule-data-id
+ for data
+ filter
+ {
+ type name
+ name /foo/bar
+ relation is-prefix-of
+ }
+ checker
+ {
+ type hierarchical
+ sig-type rsa-sha256
+ }
+ }
+ rule
+ {
+ id test-rule-interest-id
+ for interest
+ filter
+ {
+ type name
+ name /foo/bar
+ relation is-prefix-of
+ }
+ checker
+ {
+ type hierarchical
+ sig-type rsa-sha256
+ }
+ }
+ trust-anchor
+ {
+ type dir
+ dir keys
+ refresh 1h
+ }
+ )CONF", "test-config");
+ BOOST_CHECK_EQUAL(this->policy.m_isConfigured, true);
+ BOOST_CHECK_EQUAL(this->policy.m_shouldBypass, false);
+ BOOST_CHECK_EQUAL(this->policy.m_dataRules.size(), 1);
+ BOOST_CHECK_EQUAL(this->policy.m_interestRules.size(), 1);
+
+ this->policy.load(R"CONF(
+ trust-anchor
+ {
+ type any
+ }
+ )CONF", "test-config");
+ BOOST_CHECK_EQUAL(this->policy.m_isConfigured, true);
+ BOOST_CHECK_EQUAL(this->policy.m_shouldBypass, true);
+ BOOST_CHECK_EQUAL(this->policy.m_dataRules.size(), 0);
+ BOOST_CHECK_EQUAL(this->policy.m_interestRules.size(), 0);
+}
+
+using Packets = boost::mpl::vector<Interest, Data>;
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE(TrustAnchorWildcard, Packet, Packets, ValidationPolicyConfigFixture<Packet>)
+{
+ this->policy.load(R"CONF(
+ trust-anchor
+ {
+ type any
+ }
+ )CONF", "test-config");
+
+ BOOST_CHECK_EQUAL(this->policy.m_isConfigured, true);
+ BOOST_CHECK_EQUAL(this->policy.m_shouldBypass, true);
+ BOOST_CHECK_EQUAL(this->policy.m_dataRules.size(), 0);
+ BOOST_CHECK_EQUAL(this->policy.m_interestRules.size(), 0);
+
+ Packet unsignedPacket("/Security/V2/ValidatorFixture/Sub1/Sub2/Packet");
+
+ Packet packet = unsignedPacket;
+ VALIDATE_SUCCESS(packet, "Policy should accept everything");
+
+ packet = unsignedPacket;
+ this->m_keyChain.sign(packet, signingWithSha256());
+ VALIDATE_SUCCESS(packet, "Policy should accept everything");
+
+ packet = unsignedPacket;
+ this->m_keyChain.sign(packet, signingByIdentity(this->identity));
+ VALIDATE_SUCCESS(packet, "Policy should accept everything");
+
+ packet = unsignedPacket;
+ this->m_keyChain.sign(packet, signingByIdentity(this->subIdentity));
+ VALIDATE_SUCCESS(packet, "Policy should accept everything");
+
+ packet = unsignedPacket;
+ this->m_keyChain.sign(packet, signingByIdentity(this->otherIdentity));
+ VALIDATE_SUCCESS(packet, "Policy should accept everything");
+
+ packet = unsignedPacket;
+ this->m_keyChain.sign(packet, signingByIdentity(this->subSelfSignedIdentity));
+ VALIDATE_SUCCESS(packet, "Policy should accept everything");
+}
+
+using RefreshPolicies = boost::mpl::vector<Refresh1h, Refresh1m, Refresh1s>;
+
+// Somehow, didn't work without this wrapper
+template<typename RefreshPolicy>
+class RefreshPolicyFixture : public LoadStringWithDirAnchor<Data, RefreshPolicy>
+{
+public:
+};
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE(ValidateRefresh, Refresh, RefreshPolicies, RefreshPolicyFixture<Refresh>)
+{
+ using Packet = Data;
+ Packet unsignedPacket("/Security/V2/ValidatorFixture/Sub1/Sub2/Packet");
+
+ boost::filesystem::remove(this->path / "keys" / "identity.ndncert");
+ this->advanceClocks(Refresh::getRefreshTime(), 3);
+
+ Packet packet = unsignedPacket;
+ this->m_keyChain.sign(packet, signingByIdentity(this->identity));
+ VALIDATE_FAILURE(packet, "Should fail, as the trust anchor should no longer exist");
+
+ packet = unsignedPacket;
+ this->m_keyChain.sign(packet, signingByIdentity(this->subIdentity));
+ VALIDATE_FAILURE(packet, "Should fail, as the trust anchor should no longer exist");
+}
+
+BOOST_FIXTURE_TEST_CASE(OrphanedPolicyLoad, HierarchicalValidatorFixture<ValidationPolicyConfig>) // Bug #4758
+{
+ ValidationPolicyConfig policy1;
+ BOOST_CHECK_THROW(policy1.load("trust-anchor { type any }", "test-config"), Error);
+
+ // Reloading would have triggered a segfault
+ BOOST_CHECK_THROW(policy1.load("trust-anchor { type any }", "test-config"), Error);
+
+ ValidationPolicyConfig policy2;
+
+ std::string config = R"CONF(
+ trust-anchor
+ {
+ type dir
+ dir keys
+ refresh 1h
+ }
+ )CONF";
+
+ // Inserting trust anchor would have triggered a segfault
+ BOOST_CHECK_THROW(policy2.load(config, "test-config"), Error);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestValidationPolicyConfig
+BOOST_AUTO_TEST_SUITE_END() // V2
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace validator_config
+} // namespace v2
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/v2/validation-policy-simple-hierarchy.t.cpp b/tests/unit/security/v2/validation-policy-simple-hierarchy.t.cpp
new file mode 100644
index 0000000..8661ac5
--- /dev/null
+++ b/tests/unit/security/v2/validation-policy-simple-hierarchy.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 "security/v2/validation-policy-simple-hierarchy.hpp"
+
+#include "boost-test.hpp"
+#include "validator-fixture.hpp"
+
+#include <boost/mpl/vector.hpp>
+
+namespace ndn {
+namespace security {
+namespace v2 {
+namespace tests {
+
+using namespace ndn::tests;
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(V2)
+BOOST_FIXTURE_TEST_SUITE(TestValidationPolicySimpleHierarchy,
+ HierarchicalValidatorFixture<ValidationPolicySimpleHierarchy>)
+
+typedef boost::mpl::vector<Interest, Data> Packets;
+
+BOOST_AUTO_TEST_CASE_TEMPLATE(Validate, Packet, Packets)
+{
+ Packet unsignedPacket("/Security/V2/ValidatorFixture/Sub1/Sub2/Packet");
+
+ Packet packet = unsignedPacket;
+ VALIDATE_FAILURE(packet, "Unsigned");
+
+ packet = unsignedPacket;
+ m_keyChain.sign(packet, signingWithSha256());
+ VALIDATE_FAILURE(packet, "Policy doesn't accept Sha256Digest signature");
+
+ packet = unsignedPacket;
+ m_keyChain.sign(packet, signingByIdentity(identity));
+ VALIDATE_SUCCESS(packet, "Should get accepted, as signed by the anchor");
+
+ packet = unsignedPacket;
+ m_keyChain.sign(packet, signingByIdentity(subIdentity));
+ VALIDATE_SUCCESS(packet, "Should get accepted, as signed by the policy-compliant cert");
+
+ packet = unsignedPacket;
+ m_keyChain.sign(packet, signingByIdentity(otherIdentity));
+ VALIDATE_FAILURE(packet, "Should fail, as signed by the policy-violating cert");
+
+ packet = unsignedPacket;
+ m_keyChain.sign(packet, signingByIdentity(subSelfSignedIdentity));
+ VALIDATE_FAILURE(packet, "Should fail, because subSelfSignedIdentity is not a trust anchor");
+
+ // TODO add checks with malformed packets
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestValidator
+BOOST_AUTO_TEST_SUITE_END() // V2
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace v2
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/v2/validator-config/checker.t.cpp b/tests/unit/security/v2/validator-config/checker.t.cpp
new file mode 100644
index 0000000..2170908
--- /dev/null
+++ b/tests/unit/security/v2/validator-config/checker.t.cpp
@@ -0,0 +1,374 @@
+/* -*- 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 "security/v2/validator-config/checker.hpp"
+#include "security/command-interest-signer.hpp"
+#include "security/v2/validation-policy.hpp"
+#include "security/v2/validation-state.hpp"
+
+#include "boost-test.hpp"
+#include "common.hpp"
+#include "identity-management-fixture.hpp"
+#include "../validator-fixture.hpp"
+
+namespace ndn {
+namespace security {
+namespace v2 {
+namespace validator_config {
+namespace tests {
+
+using namespace ndn::tests;
+using namespace ndn::security::v2::tests;
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(V2)
+BOOST_AUTO_TEST_SUITE(ValidatorConfig)
+
+class CheckerFixture : public IdentityManagementFixture
+{
+public:
+ CheckerFixture()
+ {
+ names.push_back("/foo/bar");
+ names.push_back("/foo/bar/bar");
+ names.push_back("/foo");
+ names.push_back("/other/prefix");
+ }
+
+ Name
+ makeSignedInterestName(const Name& name)
+ {
+ return Name(name).append("SignatureInfo").append("SignatureValue");
+ }
+
+ Name
+ makeKeyLocatorName(const Name& name)
+ {
+ return Name(name).append("KEY").append("v=1");
+ }
+
+public:
+ std::vector<Name> names;
+};
+
+BOOST_FIXTURE_TEST_SUITE(TestChecker, CheckerFixture)
+
+class NameRelationEqual : public CheckerFixture
+{
+public:
+ NameRelationEqual()
+ : checker("/foo/bar", NameRelation::EQUAL)
+ {
+ }
+
+public:
+ NameRelationChecker checker;
+ std::vector<std::vector<bool>> outcomes = {{true, false, false, false},
+ {true, false, false, false},
+ {true, false, false, false},
+ {true, false, false, false}};
+};
+
+class NameRelationIsPrefixOf : public CheckerFixture
+{
+public:
+ NameRelationIsPrefixOf()
+ : checker("/foo/bar", NameRelation::IS_PREFIX_OF)
+ {
+ }
+
+public:
+ NameRelationChecker checker;
+ std::vector<std::vector<bool>> outcomes = {{true, true, false, false},
+ {true, true, false, false},
+ {true, true, false, false},
+ {true, true, false, false}};
+};
+
+class NameRelationIsStrictPrefixOf : public CheckerFixture
+{
+public:
+ NameRelationIsStrictPrefixOf()
+ : checker("/foo/bar", NameRelation::IS_STRICT_PREFIX_OF)
+ {
+ }
+
+public:
+ NameRelationChecker checker;
+ std::vector<std::vector<bool>> outcomes = {{false, true, false, false},
+ {false, true, false, false},
+ {false, true, false, false},
+ {false, true, false, false}};
+};
+
+class RegexEqual : public CheckerFixture
+{
+public:
+ RegexEqual()
+ : checker(Regex("^<foo><bar><KEY><>$"))
+ {
+ }
+
+public:
+ RegexChecker checker;
+ std::vector<std::vector<bool>> outcomes = {{true, false, false, false},
+ {true, false, false, false},
+ {true, false, false, false},
+ {true, false, false, false}};
+};
+
+class RegexIsPrefixOf : public CheckerFixture
+{
+public:
+ RegexIsPrefixOf()
+ : checker(Regex("^<foo><bar><>*<KEY><>$"))
+ {
+ }
+
+public:
+ RegexChecker checker;
+ std::vector<std::vector<bool>> outcomes = {{true, true, false, false},
+ {true, true, false, false},
+ {true, true, false, false},
+ {true, true, false, false}};
+};
+
+class RegexIsStrictPrefixOf : public CheckerFixture
+{
+public:
+ RegexIsStrictPrefixOf()
+ : checker(Regex("^<foo><bar><>+<KEY><>$"))
+ {
+ }
+
+public:
+ RegexChecker checker;
+ std::vector<std::vector<bool>> outcomes = {{false, true, false, false},
+ {false, true, false, false},
+ {false, true, false, false},
+ {false, true, false, false}};
+};
+
+class HyperRelationEqual : public CheckerFixture
+{
+public:
+ HyperRelationEqual()
+ : checker("^(<>+)$", "\\1", "^(<>+)<KEY><>$", "\\1", NameRelation::EQUAL)
+ {
+ }
+
+public:
+ HyperRelationChecker checker;
+ std::vector<std::vector<bool>> outcomes = {{true, false, false, false},
+ {false, true, false, false},
+ {false, false, true, false},
+ {false, false, false, true}};
+};
+
+class HyperRelationIsPrefixOf : public CheckerFixture
+{
+public:
+ HyperRelationIsPrefixOf()
+ : checker("^(<>+)$", "\\1", "^(<>+)<KEY><>$", "\\1", NameRelation::IS_PREFIX_OF)
+ {
+ }
+
+public:
+ HyperRelationChecker checker;
+ std::vector<std::vector<bool>> outcomes = {{true, false, true, false},
+ {true, true, true, false},
+ {false, false, true, false},
+ {false, false, false, true}};
+};
+
+class HyperRelationIsStrictPrefixOf : public CheckerFixture
+{
+public:
+ HyperRelationIsStrictPrefixOf()
+ : checker("^(<>+)$", "\\1", "^(<>+)<KEY><>$", "\\1", NameRelation::IS_STRICT_PREFIX_OF)
+ {
+ }
+
+public:
+ HyperRelationChecker checker;
+ std::vector<std::vector<bool>> outcomes = {{false, false, true, false},
+ {true, false, true, false},
+ {false, false, false, false},
+ {false, false, false, false}};
+};
+
+class Hierarchical : public CheckerFixture
+{
+public:
+ Hierarchical()
+ : checkerPtr(Checker::create(makeSection(R"CONF(
+ type hierarchical
+ sig-type rsa-sha256
+ )CONF"), "test-config"))
+ , checker(*checkerPtr)
+ {
+ }
+
+public:
+ std::unique_ptr<Checker> checkerPtr;
+ Checker& checker;
+
+ std::vector<std::vector<bool>> outcomes = {{true, false, true, false},
+ {true, true, true, false},
+ {false, false, true, false},
+ {false, false, false, true}};
+};
+
+class CustomizedNameRelation : public CheckerFixture
+{
+public:
+ CustomizedNameRelation()
+ : checkerPtr(Checker::create(makeSection(R"CONF(
+ type customized
+ sig-type rsa-sha256
+ key-locator
+ {
+ type name
+ name /foo/bar
+ relation equal
+ }
+ )CONF"), "test-config"))
+ , checker(*checkerPtr)
+ {
+ }
+
+public:
+ std::unique_ptr<Checker> checkerPtr;
+ Checker& checker;
+
+ std::vector<std::vector<bool>> outcomes = {{true, false, false, false},
+ {true, false, false, false},
+ {true, false, false, false},
+ {true, false, false, false}};
+};
+
+class CustomizedRegex : public CheckerFixture
+{
+public:
+ CustomizedRegex()
+ : checkerPtr(Checker::create(makeSection(R"CONF(
+ type customized
+ sig-type rsa-sha256
+ key-locator
+ {
+ type name
+ regex ^<foo><bar><KEY><>$
+ }
+ )CONF"), "test-config"))
+ , checker(*checkerPtr)
+ {
+ }
+
+public:
+ std::unique_ptr<Checker> checkerPtr;
+ Checker& checker;
+
+ std::vector<std::vector<bool>> outcomes = {{true, false, false, false},
+ {true, false, false, false},
+ {true, false, false, false},
+ {true, false, false, false}};
+};
+
+class CustomizedHyperRelation : public CheckerFixture
+{
+public:
+ CustomizedHyperRelation()
+ : checkerPtr(Checker::create(makeSection(R"CONF(
+ type customized
+ sig-type rsa-sha256
+ key-locator
+ {
+ type name
+ hyper-relation
+ {
+ k-regex ^(<>+)<KEY><>$
+ k-expand \\1
+ h-relation is-prefix-of
+ p-regex ^(<>+)$
+ p-expand \\1
+ }
+ }
+ )CONF"), "test-config"))
+ , checker(*checkerPtr)
+ {
+ }
+
+public:
+ std::unique_ptr<Checker> checkerPtr;
+ Checker& checker;
+
+ std::vector<std::vector<bool>> outcomes = {{true, false, true, false},
+ {true, true, true, false},
+ {false, false, true, false},
+ {false, false, false, true}};
+};
+
+
+using Tests = boost::mpl::vector<NameRelationEqual, NameRelationIsPrefixOf, NameRelationIsStrictPrefixOf,
+ RegexEqual, RegexIsPrefixOf, RegexIsStrictPrefixOf,
+ HyperRelationEqual, HyperRelationIsPrefixOf, HyperRelationIsStrictPrefixOf,
+ Hierarchical,
+ CustomizedNameRelation, CustomizedRegex, CustomizedHyperRelation>;
+
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE(Checks, T, Tests, T)
+{
+ BOOST_ASSERT(this->outcomes.size() == this->names.size());
+ for (size_t i = 0; i < this->names.size(); ++i) {
+ BOOST_ASSERT(this->outcomes[i].size() == this->names.size());
+ for (size_t j = 0; j < this->names.size(); ++j) {
+ const Name& pktName = this->names[i];
+ Name klName = this->makeKeyLocatorName(this->names[j]);
+ bool expectedOutcome = this->outcomes[i][j];
+
+ auto dataState = make_shared<DummyValidationState>();
+ BOOST_CHECK_EQUAL(this->checker.check(tlv::Data, pktName, klName, dataState), expectedOutcome);
+ BOOST_CHECK_EQUAL(boost::logic::indeterminate(dataState->getOutcome()), expectedOutcome);
+ if (boost::logic::indeterminate(dataState->getOutcome()) == !expectedOutcome) {
+ BOOST_CHECK_EQUAL(dataState->getOutcome(), !expectedOutcome);
+ }
+
+ auto interestState = make_shared<DummyValidationState>();
+ BOOST_CHECK_EQUAL(this->checker.check(tlv::Interest, this->makeSignedInterestName(pktName),
+ klName, interestState), expectedOutcome);
+ BOOST_CHECK_EQUAL(boost::logic::indeterminate(interestState->getOutcome()), expectedOutcome);
+ if (boost::logic::indeterminate(interestState->getOutcome()) == !expectedOutcome) {
+ BOOST_CHECK_EQUAL(interestState->getOutcome(), !expectedOutcome);
+ }
+ }
+ }
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestChecker
+BOOST_AUTO_TEST_SUITE_END() // ValidatorConfig
+BOOST_AUTO_TEST_SUITE_END() // V2
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace validator_config
+} // namespace v2
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/v2/validator-config/common.hpp b/tests/unit/security/v2/validator-config/common.hpp
new file mode 100644
index 0000000..231e8eb
--- /dev/null
+++ b/tests/unit/security/v2/validator-config/common.hpp
@@ -0,0 +1,48 @@
+/* -*- 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.
+ */
+
+#ifndef NDN_TESTS_SECURITY_V2_VALIDATOR_CONFIG_COMMON_HPP
+#define NDN_TESTS_SECURITY_V2_VALIDATOR_CONFIG_COMMON_HPP
+
+#include <boost/property_tree/info_parser.hpp>
+
+namespace ndn {
+namespace security {
+namespace v2 {
+namespace validator_config {
+namespace tests {
+
+inline ConfigSection
+makeSection(const std::string& config)
+{
+ std::istringstream inputStream(config);
+ ConfigSection section;
+ boost::property_tree::read_info(inputStream, section);
+ return section;
+}
+
+} // namespace tests
+} // namespace validator_config
+} // namespace v2
+} // namespace security
+} // namespace ndn
+
+#endif // NDN_TESTS_SECURITY_V2_VALIDATOR_CONFIG_COMMON_HPP
diff --git a/tests/unit/security/v2/validator-config/filter.t.cpp b/tests/unit/security/v2/validator-config/filter.t.cpp
new file mode 100644
index 0000000..fbd1264
--- /dev/null
+++ b/tests/unit/security/v2/validator-config/filter.t.cpp
@@ -0,0 +1,201 @@
+/* -*- 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 "security/v2/validator-config/filter.hpp"
+#include "security/command-interest-signer.hpp"
+
+#include "boost-test.hpp"
+#include "common.hpp"
+#include "identity-management-fixture.hpp"
+
+namespace ndn {
+namespace security {
+namespace v2 {
+namespace validator_config {
+namespace tests {
+
+using namespace ndn::tests;
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(V2)
+BOOST_AUTO_TEST_SUITE(ValidatorConfig)
+
+class FilterFixture : public IdentityManagementFixture
+{
+public:
+ Interest
+ makeSignedInterest(const Name& name)
+ {
+ Interest interest(name);
+ m_keyChain.sign(interest);
+ return interest;
+ }
+};
+
+BOOST_FIXTURE_TEST_SUITE(TestFilter, FilterFixture)
+
+#define CHECK_FOR_MATCHES(filter, same, longer, shorter, different) \
+ BOOST_CHECK_EQUAL(filter.match(tlv::Interest, makeSignedInterest("/foo/bar").getName()), same); \
+ BOOST_CHECK_EQUAL(filter.match(tlv::Data, Data("/foo/bar").getName()), same); \
+ BOOST_CHECK_EQUAL(filter.match(tlv::Interest, makeSignedInterest("/foo/bar/bar").getName()), longer); \
+ BOOST_CHECK_EQUAL(filter.match(tlv::Data, Data("/foo/bar/bar").getName()), longer); \
+ BOOST_CHECK_EQUAL(filter.match(tlv::Interest, makeSignedInterest("/foo").getName()), shorter); \
+ BOOST_CHECK_EQUAL(filter.match(tlv::Data, Data("/foo").getName()), shorter); \
+ BOOST_CHECK_EQUAL(filter.match(tlv::Interest, makeSignedInterest("/other/prefix").getName()), different); \
+ BOOST_CHECK_EQUAL(filter.match(tlv::Data, Data("/other/prefix").getName()), different);
+
+BOOST_AUTO_TEST_CASE(RelationName)
+{
+ RelationNameFilter f1("/foo/bar", NameRelation::EQUAL);
+ CHECK_FOR_MATCHES(f1, true, false, false, false);
+
+ RelationNameFilter f2("/foo/bar", NameRelation::IS_PREFIX_OF);
+ CHECK_FOR_MATCHES(f2, true, true, false, false);
+
+ RelationNameFilter f3("/foo/bar", NameRelation::IS_STRICT_PREFIX_OF);
+ CHECK_FOR_MATCHES(f3, false, true, false, false);
+}
+
+BOOST_AUTO_TEST_CASE(RegexName)
+{
+ RegexNameFilter f1(Regex("^<foo><bar>$"));
+ CHECK_FOR_MATCHES(f1, true, false, false, false);
+
+ RegexNameFilter f2(Regex("^<foo><bar><>*$"));
+ CHECK_FOR_MATCHES(f2, true, true, false, false);
+
+ RegexNameFilter f3(Regex("^<foo><bar><>+$"));
+ CHECK_FOR_MATCHES(f3, false, true, false, false);
+}
+
+BOOST_FIXTURE_TEST_SUITE(Create, FilterFixture)
+
+BOOST_AUTO_TEST_CASE(Errors)
+{
+ BOOST_CHECK_THROW(Filter::create(makeSection(""), "test-config"), Error);
+ BOOST_CHECK_THROW(Filter::create(makeSection("type unknown"), "test-config"), Error);
+ BOOST_CHECK_THROW(Filter::create(makeSection("type name"), "test-config"), Error);
+
+ std::string config = R"CONF(
+ type name
+ not-name-or-regex stuff
+ )CONF";
+ BOOST_CHECK_THROW(Filter::create(makeSection(config), "test-config"), Error);
+
+ config = R"CONF(
+ type name
+ name /foo/bar
+ )CONF";
+ BOOST_CHECK_THROW(Filter::create(makeSection(config), "test-config"), Error);
+
+ config = R"CONF(
+ type name
+ name /foo/bar
+ not-relation stuff
+ )CONF";
+ BOOST_CHECK_THROW(Filter::create(makeSection(config), "test-config"), Error);
+
+ config = R"CONF(
+ type name
+ name /foo/bar
+ relation equal
+ not-end stuff
+ )CONF";
+ BOOST_CHECK_THROW(Filter::create(makeSection(config), "test-config"), Error);
+
+ config = R"CONF(
+ type name
+ regex ^<foo><bar>$
+ not-end stuff
+ )CONF";
+ BOOST_CHECK_THROW(Filter::create(makeSection(config), "test-config"), Error);
+}
+
+BOOST_AUTO_TEST_CASE(NameFilter)
+{
+ std::string config = R"CONF(
+ type name
+ name /foo/bar
+ relation equal
+ )CONF";
+ auto f1 = Filter::create(makeSection(config), "test-config");
+ CHECK_FOR_MATCHES((*f1), true, false, false, false);
+
+ config = R"CONF(
+ type name
+ name /foo/bar
+ relation is-prefix-of
+ )CONF";
+ auto f2 = Filter::create(makeSection(config), "test-config");
+ CHECK_FOR_MATCHES((*f2), true, true, false, false);
+
+ config = R"CONF(
+ type name
+ name /foo/bar
+ relation is-strict-prefix-of
+ )CONF";
+ auto f3 = Filter::create(makeSection(config), "test-config");
+ CHECK_FOR_MATCHES((*f3), false, true, false, false);
+}
+
+BOOST_AUTO_TEST_CASE(RegexFilter)
+{
+ std::string config = R"CONF(
+ type name
+ regex ^<foo><bar>$
+ )CONF";
+ auto f1 = Filter::create(makeSection(config), "test-config");
+ CHECK_FOR_MATCHES((*f1), true, false, false, false);
+
+ config = R"CONF(
+ type name
+ regex ^<foo><bar><>*$
+ )CONF";
+ auto f2 = Filter::create(makeSection(config), "test-config");
+ CHECK_FOR_MATCHES((*f2), true, true, false, false);
+
+ config = R"CONF(
+ type name
+ regex ^<foo><bar><>+$
+ )CONF";
+ auto f3 = Filter::create(makeSection(config), "test-config");
+ CHECK_FOR_MATCHES((*f3), false, true, false, false);
+
+ config = R"CONF(
+ type name
+ regex ^<>*$
+ )CONF";
+ auto f4 = Filter::create(makeSection(config), "test-config");
+ CHECK_FOR_MATCHES((*f4), true, true, true, true);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // Create
+
+BOOST_AUTO_TEST_SUITE_END() // TestFilter
+BOOST_AUTO_TEST_SUITE_END() // ValidatorConfig
+BOOST_AUTO_TEST_SUITE_END() // V2
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace validator_config
+} // namespace v2
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/v2/validator-config/name-relation.t.cpp b/tests/unit/security/v2/validator-config/name-relation.t.cpp
new file mode 100644
index 0000000..157b55a
--- /dev/null
+++ b/tests/unit/security/v2/validator-config/name-relation.t.cpp
@@ -0,0 +1,79 @@
+/* -*- 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 "security/v2/validator-config/name-relation.hpp"
+
+#include "boost-test.hpp"
+
+#include <boost/lexical_cast.hpp>
+
+namespace ndn {
+namespace security {
+namespace v2 {
+namespace validator_config {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(V2)
+BOOST_AUTO_TEST_SUITE(ValidatorConfig)
+BOOST_AUTO_TEST_SUITE(TestNameRelation)
+
+BOOST_AUTO_TEST_CASE(ToString)
+{
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(NameRelation::EQUAL), "equal");
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(NameRelation::IS_PREFIX_OF),
+ "is-prefix-of");
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(NameRelation::IS_STRICT_PREFIX_OF),
+ "is-strict-prefix-of");
+}
+
+BOOST_AUTO_TEST_CASE(FromString)
+{
+ BOOST_CHECK_EQUAL(getNameRelationFromString("equal"), NameRelation::EQUAL);
+ BOOST_CHECK_EQUAL(getNameRelationFromString("is-prefix-of"), NameRelation::IS_PREFIX_OF);
+ BOOST_CHECK_EQUAL(getNameRelationFromString("is-strict-prefix-of"), NameRelation::IS_STRICT_PREFIX_OF);
+ BOOST_CHECK_THROW(getNameRelationFromString("unknown"), validator_config::Error);
+}
+
+BOOST_AUTO_TEST_CASE(CheckRelation)
+{
+ BOOST_CHECK(checkNameRelation(NameRelation::EQUAL, "/prefix", "/prefix"));
+ BOOST_CHECK(!checkNameRelation(NameRelation::EQUAL, "/prefix", "/prefix/other"));
+
+ BOOST_CHECK(checkNameRelation(NameRelation::IS_PREFIX_OF, "/prefix", "/prefix"));
+ BOOST_CHECK(checkNameRelation(NameRelation::IS_PREFIX_OF, "/prefix", "/prefix/other"));
+ BOOST_CHECK(!checkNameRelation(NameRelation::IS_PREFIX_OF, "/prefix/other", "/prefix"));
+
+ BOOST_CHECK(!checkNameRelation(NameRelation::IS_STRICT_PREFIX_OF, "/prefix", "/prefix"));
+ BOOST_CHECK(checkNameRelation(NameRelation::IS_STRICT_PREFIX_OF, "/prefix", "/prefix/other"));
+ BOOST_CHECK(!checkNameRelation(NameRelation::IS_STRICT_PREFIX_OF, "/prefix/other", "/prefix"));
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestNameRelation
+BOOST_AUTO_TEST_SUITE_END() // ValidatorConfig
+BOOST_AUTO_TEST_SUITE_END() // V2
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace validator_config
+} // namespace v2
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/v2/validator-config/rule.t.cpp b/tests/unit/security/v2/validator-config/rule.t.cpp
new file mode 100644
index 0000000..bb78dfe
--- /dev/null
+++ b/tests/unit/security/v2/validator-config/rule.t.cpp
@@ -0,0 +1,209 @@
+/* -*- 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 "security/v2/validator-config/rule.hpp"
+
+#include "boost-test.hpp"
+#include "common.hpp"
+#include "identity-management-fixture.hpp"
+#include "../validator-fixture.hpp"
+
+#include <boost/mpl/vector_c.hpp>
+
+namespace ndn {
+namespace security {
+namespace v2 {
+namespace validator_config {
+namespace tests {
+
+using namespace ndn::tests;
+using namespace ndn::security::v2::tests;
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(V2)
+BOOST_AUTO_TEST_SUITE(ValidatorConfig)
+
+template<uint32_t PktType>
+class RuleFixture : public IdentityManagementFixture
+{
+public:
+ RuleFixture()
+ : rule(ruleId, PktType)
+ , pktName("/foo/bar")
+ {
+ if (PktType == tlv::Interest) {
+ pktName = Name("/foo/bar/SigInfo/SigValue");
+ }
+ }
+
+public:
+ const std::string ruleId = "rule-id";
+ Rule rule;
+ Name pktName;
+};
+
+using PktTypes = boost::mpl::vector_c<uint32_t, tlv::Data, tlv::Interest>;
+
+BOOST_AUTO_TEST_SUITE(TestRule)
+
+BOOST_FIXTURE_TEST_CASE(Errors, RuleFixture<tlv::Data>)
+{
+ BOOST_CHECK_THROW(rule.match(tlv::Interest, this->pktName), Error);
+
+ auto state = make_shared<DummyValidationState>();
+ BOOST_CHECK_THROW(rule.check(tlv::Interest, this->pktName, "/foo/bar", state), Error);
+}
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE(Constructor, PktType, PktTypes, RuleFixture<PktType::value>)
+{
+ BOOST_CHECK_EQUAL(this->rule.getId(), this->ruleId);
+ BOOST_CHECK_EQUAL(this->rule.getPktType(), PktType::value);
+}
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE(EmptyRule, PktType, PktTypes, RuleFixture<PktType::value>)
+{
+ BOOST_CHECK_EQUAL(this->rule.match(PktType::value, this->pktName), true);
+
+ auto state = make_shared<DummyValidationState>();
+ BOOST_CHECK_EQUAL(this->rule.check(PktType::value, this->pktName, "/foo/bar", state), false);
+}
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE(Filters, PktType, PktTypes, RuleFixture<PktType::value>)
+{
+ this->rule.addFilter(make_unique<RegexNameFilter>(Regex("^<foo><bar>$")));
+
+ BOOST_CHECK_EQUAL(this->rule.match(PktType::value, this->pktName), true);
+ BOOST_CHECK_EQUAL(this->rule.match(PktType::value, "/not" + this->pktName.toUri()), false);
+
+ this->rule.addFilter(make_unique<RegexNameFilter>(Regex("^<not><foo><bar>$")));
+
+ BOOST_CHECK_EQUAL(this->rule.match(PktType::value, this->pktName), true);
+ BOOST_CHECK_EQUAL(this->rule.match(PktType::value, "/not" + this->pktName.toUri()), true);
+
+ auto state = make_shared<DummyValidationState>();
+ BOOST_CHECK_EQUAL(this->rule.check(PktType::value, this->pktName, "/foo/bar", state), false);
+}
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE(Checkers, PktType, PktTypes, RuleFixture<PktType::value>)
+{
+ this->rule.addChecker(make_unique<HyperRelationChecker>("^(<>+)$", "\\1",
+ "^<not>?(<>+)$", "\\1",
+ NameRelation::EQUAL));
+
+ auto state = make_shared<DummyValidationState>();
+ BOOST_CHECK_EQUAL(this->rule.check(PktType::value, this->pktName, "/foo/bar", state), true);
+
+ state = make_shared<DummyValidationState>();
+ BOOST_CHECK_EQUAL(this->rule.check(PktType::value, this->pktName, "/not/foo/bar", state), true);
+
+ this->rule.addChecker(make_unique<HyperRelationChecker>("^(<>+)$", "\\1",
+ "^(<>+)$", "\\1",
+ NameRelation::EQUAL));
+ state = make_shared<DummyValidationState>();
+ BOOST_CHECK_EQUAL(this->rule.check(PktType::value, this->pktName, "/foo/bar", state), true);
+
+ state = make_shared<DummyValidationState>();
+ BOOST_CHECK_EQUAL(this->rule.check(PktType::value, this->pktName, "/not/foo/bar", state), false);
+}
+
+BOOST_AUTO_TEST_SUITE(Create)
+
+BOOST_AUTO_TEST_CASE(Errors)
+{
+ BOOST_CHECK_THROW(Rule::create(makeSection(""), "test-config"), Error);
+
+ std::string config = R"CONF(
+ id rule-id
+ for something
+ )CONF";
+ BOOST_CHECK_THROW(Rule::create(makeSection(config), "test-config"), Error);
+
+ config = R"CONF(
+ id rule-id
+ for data
+ )CONF";
+ BOOST_CHECK_THROW(Rule::create(makeSection(config), "test-config"), Error); // at least one checker required
+
+ config = R"CONF(
+ id rule-id
+ for data
+ checker
+ {
+ type hierarchical
+ sig-type rsa-sha256
+ }
+ other stuff
+ )CONF";
+ BOOST_CHECK_THROW(Rule::create(makeSection(config), "test-config"), Error);
+}
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE(FilterAndChecker, PktType, PktTypes, RuleFixture<PktType::value>)
+{
+ std::string config = R"CONF(
+ id rule-id
+ for )CONF" + (PktType::value == tlv::Data ? "data"s : "interest"s) + R"CONF(
+ filter
+ {
+ type name
+ regex ^<foo><bar>$
+ }
+ checker
+ {
+ type customized
+ sig-type rsa-sha256
+ key-locator
+ {
+ type name
+ hyper-relation
+ {
+ k-regex ^(<>+)$
+ k-expand \\1
+ h-relation equal
+ p-regex ^(<>+)$
+ p-expand \\1
+ }
+ }
+ }
+ )CONF";
+ auto rule = Rule::create(makeSection(config), "test-config");
+
+ BOOST_CHECK_EQUAL(rule->match(PktType::value, this->pktName), true);
+ BOOST_CHECK_EQUAL(rule->match(PktType::value, "/not" + this->pktName.toUri()), false);
+
+ auto state = make_shared<DummyValidationState>();
+ BOOST_CHECK_EQUAL(rule->check(PktType::value, this->pktName, "/foo/bar", state), true);
+
+ state = make_shared<DummyValidationState>();
+ BOOST_CHECK_EQUAL(rule->check(PktType::value, this->pktName, "/not/foo/bar", state), false);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // Create
+
+BOOST_AUTO_TEST_SUITE_END() // TestRule
+BOOST_AUTO_TEST_SUITE_END() // ValidatorConfig
+BOOST_AUTO_TEST_SUITE_END() // V2
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace validator_config
+} // namespace v2
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/v2/validator-fixture.hpp b/tests/unit/security/v2/validator-fixture.hpp
new file mode 100644
index 0000000..c9a5763
--- /dev/null
+++ b/tests/unit/security/v2/validator-fixture.hpp
@@ -0,0 +1,183 @@
+/* -*- 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.
+ */
+
+#ifndef NDN_TESTS_SECURITY_V2_VALIDATOR_FIXTURE_HPP
+#define NDN_TESTS_SECURITY_V2_VALIDATOR_FIXTURE_HPP
+
+#include "security/v2/validator.hpp"
+#include "security/v2/certificate-fetcher-from-network.hpp"
+#include "util/dummy-client-face.hpp"
+
+#include "../../identity-management-time-fixture.hpp"
+
+#include <boost/lexical_cast.hpp>
+
+namespace ndn {
+namespace security {
+namespace v2 {
+namespace tests {
+
+template<class ValidationPolicy, class CertificateFetcher = CertificateFetcherFromNetwork>
+class ValidatorFixture : public ndn::tests::IdentityManagementTimeFixture
+{
+public:
+ ValidatorFixture()
+ : face(io, {true, true})
+ , validator(make_unique<ValidationPolicy>(), make_unique<CertificateFetcher>(face))
+ , policy(static_cast<ValidationPolicy&>(validator.getPolicy()))
+ , cache(100_days)
+ {
+ processInterest = [this] (const Interest& interest) {
+ auto cert = cache.find(interest);
+ if (cert != nullptr) {
+ face.receive(*cert);
+ }
+ };
+ }
+
+ virtual
+ ~ValidatorFixture() = default;
+
+ template<class Packet>
+ void
+ validate(const Packet& packet, const std::string& msg, bool expectSuccess, int line)
+ {
+ std::string detailedInfo = msg + " on line " + to_string(line);
+ size_t nCallbacks = 0;
+ this->validator.validate(packet,
+ [&] (const Packet&) {
+ ++nCallbacks;
+ BOOST_CHECK_MESSAGE(expectSuccess,
+ (expectSuccess ? "OK: " : "FAILED: ") + detailedInfo);
+ },
+ [&] (const Packet&, const ValidationError& error) {
+ ++nCallbacks;
+ BOOST_CHECK_MESSAGE(!expectSuccess,
+ (!expectSuccess ? "OK: " : "FAILED: ") + detailedInfo +
+ (expectSuccess ? " (" + boost::lexical_cast<std::string>(error) + ")" : ""));
+ });
+
+ mockNetworkOperations();
+ BOOST_CHECK_EQUAL(nCallbacks, 1);
+ }
+
+ void
+ mockNetworkOperations()
+ {
+ util::signal::ScopedConnection connection = face.onSendInterest.connect([this] (const Interest& interest) {
+ if (processInterest != nullptr) {
+ io.post(bind(processInterest, interest));
+ }
+ });
+ advanceClocks(time::milliseconds(s_mockPeriod), s_mockTimes);
+ }
+
+ /** \brief undo clock advancement of mockNetworkOperations
+ */
+ void
+ rewindClockAfterValidation()
+ {
+ this->systemClock->advance(time::milliseconds(s_mockPeriod * s_mockTimes * -1));
+ }
+
+public:
+ util::DummyClientFace face;
+ std::function<void(const Interest& interest)> processInterest;
+ Validator validator;
+ ValidationPolicy& policy;
+
+ CertificateCache cache;
+
+private:
+ const static int s_mockPeriod;
+ const static int s_mockTimes;
+};
+
+template<class ValidationPolicy, class CertificateFetcher>
+const int ValidatorFixture<ValidationPolicy, CertificateFetcher>::s_mockPeriod = 250;
+
+template<class ValidationPolicy, class CertificateFetcher>
+const int ValidatorFixture<ValidationPolicy, CertificateFetcher>::s_mockTimes = 200;
+
+template<class ValidationPolicy, class CertificateFetcher = CertificateFetcherFromNetwork>
+class HierarchicalValidatorFixture : public ValidatorFixture<ValidationPolicy, CertificateFetcher>
+{
+public:
+ HierarchicalValidatorFixture()
+ {
+ identity = this->addIdentity("/Security/V2/ValidatorFixture");
+ subIdentity = this->addSubCertificate("/Security/V2/ValidatorFixture/Sub1", identity);
+ subSelfSignedIdentity = this->addIdentity("/Security/V2/ValidatorFixture/Sub1/Sub2");
+ otherIdentity = this->addIdentity("/Security/V2/OtherIdentity");
+
+ this->validator.loadAnchor("", Certificate(identity.getDefaultKey().getDefaultCertificate()));
+
+ this->cache.insert(identity.getDefaultKey().getDefaultCertificate());
+ this->cache.insert(subIdentity.getDefaultKey().getDefaultCertificate());
+ this->cache.insert(subSelfSignedIdentity.getDefaultKey().getDefaultCertificate());
+ this->cache.insert(otherIdentity.getDefaultKey().getDefaultCertificate());
+ }
+
+public:
+ Identity identity;
+ Identity subIdentity;
+ Identity subSelfSignedIdentity;
+ Identity otherIdentity;
+};
+
+#define VALIDATE_SUCCESS(packet, message) this->template validate(packet, message, true, __LINE__)
+#define VALIDATE_FAILURE(packet, message) this->template validate(packet, message, false, __LINE__)
+
+class DummyValidationState : public ValidationState
+{
+public:
+ ~DummyValidationState()
+ {
+ m_outcome = false;
+ }
+
+ void
+ fail(const ValidationError& error) override
+ {
+ // BOOST_TEST_MESSAGE(error);
+ m_outcome = false;
+ }
+
+private:
+ void
+ verifyOriginalPacket(const Certificate& trustedCert) override
+ {
+ // do nothing
+ }
+
+ void
+ bypassValidation() override
+ {
+ // do nothing
+ }
+};
+
+} // namespace tests
+} // namespace v2
+} // namespace security
+} // namespace ndn
+
+#endif // NDN_TESTS_SECURITY_V2_VALIDATOR_FIXTURE_HPP
diff --git a/tests/unit/security/v2/validator.t.cpp b/tests/unit/security/v2/validator.t.cpp
new file mode 100644
index 0000000..ff33984
--- /dev/null
+++ b/tests/unit/security/v2/validator.t.cpp
@@ -0,0 +1,344 @@
+/* -*- 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 "security/v2/validator.hpp"
+#include "security/v2/validation-policy-simple-hierarchy.hpp"
+
+#include "boost-test.hpp"
+#include "validator-fixture.hpp"
+
+namespace ndn {
+namespace security {
+namespace v2 {
+namespace tests {
+
+using namespace ndn::tests;
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(V2)
+BOOST_FIXTURE_TEST_SUITE(TestValidator, HierarchicalValidatorFixture<ValidationPolicySimpleHierarchy>)
+
+BOOST_AUTO_TEST_CASE(ConstructorSetValidator)
+{
+ auto middlePolicy = make_unique<ValidationPolicySimpleHierarchy>();
+ auto innerPolicy = make_unique<ValidationPolicySimpleHierarchy>();
+
+ validator.getPolicy().setInnerPolicy(std::move(middlePolicy));
+ validator.getPolicy().setInnerPolicy(std::move(innerPolicy));
+
+ BOOST_CHECK(validator.getPolicy().m_validator != nullptr);
+ BOOST_CHECK(validator.getPolicy().getInnerPolicy().m_validator != nullptr);
+ BOOST_CHECK(validator.getPolicy().getInnerPolicy().getInnerPolicy().m_validator != nullptr);
+}
+
+BOOST_AUTO_TEST_CASE(Timeouts)
+{
+ processInterest = nullptr; // no response for all interests
+
+ Data data("/Security/V2/ValidatorFixture/Sub1/Sub2/Data");
+ m_keyChain.sign(data, signingByIdentity(subIdentity));
+
+ VALIDATE_FAILURE(data, "Should fail to retrieve certificate");
+ BOOST_CHECK_GT(face.sentInterests.size(), 1);
+}
+
+BOOST_AUTO_TEST_CASE(NackedInterests)
+{
+ processInterest = [this] (const Interest& interest) {
+ lp::Nack nack(interest);
+ nack.setReason(lp::NackReason::NO_ROUTE);
+ face.receive(nack);
+ };
+
+ Data data("/Security/V2/ValidatorFixture/Sub1/Sub2/Data");
+ m_keyChain.sign(data, signingByIdentity(subIdentity));
+
+ VALIDATE_FAILURE(data, "All interests should get NACKed");
+ // 1 for the first interest, 3 for the retries on nack
+ BOOST_CHECK_EQUAL(face.sentInterests.size(), 4);
+}
+
+BOOST_AUTO_TEST_CASE(MalformedCert)
+{
+ Data malformedCert = subIdentity.getDefaultKey().getDefaultCertificate();
+ malformedCert.setContentType(tlv::ContentType_Blob);
+ m_keyChain.sign(malformedCert, signingByIdentity(identity));
+ // wrong content type & missing ValidityPeriod
+ BOOST_REQUIRE_THROW(Certificate(malformedCert.wireEncode()), tlv::Error);
+
+ auto originalProcessInterest = processInterest;
+ processInterest = [this, &originalProcessInterest, &malformedCert] (const Interest& interest) {
+ if (interest.getName().isPrefixOf(malformedCert.getName())) {
+ face.receive(malformedCert);
+ }
+ else {
+ originalProcessInterest(interest);
+ }
+ };
+
+ Data data("/Security/V2/ValidatorFixture/Sub1/Sub2/Data");
+ m_keyChain.sign(data, signingByIdentity(subIdentity));
+
+ VALIDATE_FAILURE(data, "Signed by a malformed certificate");
+ BOOST_CHECK_EQUAL(face.sentInterests.size(), 1);
+}
+
+BOOST_AUTO_TEST_CASE(ExpiredCert)
+{
+ Data expiredCert = subIdentity.getDefaultKey().getDefaultCertificate();
+ SignatureInfo info;
+ info.setValidityPeriod(ValidityPeriod(time::system_clock::now() - 2_h,
+ time::system_clock::now() - 1_h));
+ m_keyChain.sign(expiredCert, signingByIdentity(identity).setSignatureInfo(info));
+ BOOST_REQUIRE_NO_THROW(Certificate(expiredCert.wireEncode()));
+
+ auto originalProcessInterest = processInterest;
+ processInterest = [this, &originalProcessInterest, &expiredCert] (const Interest& interest) {
+ if (interest.getName().isPrefixOf(expiredCert.getName())) {
+ face.receive(expiredCert);
+ }
+ else {
+ originalProcessInterest(interest);
+ }
+ };
+
+ Data data("/Security/V2/ValidatorFixture/Sub1/Sub2/Data");
+ m_keyChain.sign(data, signingByIdentity(subIdentity));
+
+ VALIDATE_FAILURE(data, "Signed by an expired certificate");
+ BOOST_CHECK_EQUAL(face.sentInterests.size(), 1);
+}
+
+BOOST_AUTO_TEST_CASE(ResetAnchors)
+{
+ validator.resetAnchors();
+
+ Data data("/Security/V2/ValidatorFixture/Sub1/Sub2/Data");
+ m_keyChain.sign(data, signingByIdentity(subIdentity));
+ VALIDATE_FAILURE(data, "Should fail, as no anchors configured");
+}
+
+BOOST_AUTO_TEST_CASE(TrustedCertCaching)
+{
+ Data data("/Security/V2/ValidatorFixture/Sub1/Sub2/Data");
+ m_keyChain.sign(data, signingByIdentity(subIdentity));
+
+ VALIDATE_SUCCESS(data, "Should get accepted, as signed by the policy-compliant cert");
+ BOOST_CHECK_EQUAL(face.sentInterests.size(), 1);
+ face.sentInterests.clear();
+
+ processInterest = nullptr; // disable data responses from mocked network
+
+ VALIDATE_SUCCESS(data, "Should get accepted, based on the cached trusted cert");
+ BOOST_CHECK_EQUAL(face.sentInterests.size(), 0);
+ face.sentInterests.clear();
+
+ advanceClocks(1_h, 2); // expire trusted cache
+
+ VALIDATE_FAILURE(data, "Should try and fail to retrieve certs");
+ BOOST_CHECK_GT(face.sentInterests.size(), 1);
+ face.sentInterests.clear();
+}
+
+BOOST_AUTO_TEST_CASE(ResetVerifiedCertificates)
+{
+ Data data("/Security/V2/ValidatorFixture/Sub1/Sub2/Data");
+ m_keyChain.sign(data, signingByIdentity(subIdentity));
+ VALIDATE_SUCCESS(data, "Should get accepted, as signed by the policy-compliant cert");
+
+ // reset anchors
+ validator.resetAnchors();
+ VALIDATE_SUCCESS(data, "Should get accepted, as signed by the cert in trusted cache");
+
+ // reset trusted cache
+ validator.resetVerifiedCertificates();
+ VALIDATE_FAILURE(data, "Should fail, as no trusted cache or anchors");
+}
+
+BOOST_AUTO_TEST_CASE(UntrustedCertCaching)
+{
+ Data data("/Security/V2/ValidatorFixture/Sub1/Sub2/Data");
+ m_keyChain.sign(data, signingByIdentity(subSelfSignedIdentity));
+
+ VALIDATE_FAILURE(data, "Should fail, as signed by the policy-violating cert");
+ BOOST_CHECK_EQUAL(face.sentInterests.size(), 1);
+ face.sentInterests.clear();
+
+ processInterest = nullptr; // disable data responses from mocked network
+
+ VALIDATE_FAILURE(data, "Should fail again, but no network operations expected");
+ BOOST_CHECK_EQUAL(face.sentInterests.size(), 0);
+ face.sentInterests.clear();
+
+ advanceClocks(10_min, 2); // expire untrusted cache
+
+ VALIDATE_FAILURE(data, "Should try and fail to retrieve certs");
+ BOOST_CHECK_GT(face.sentInterests.size(), 1);
+ face.sentInterests.clear();
+}
+
+class ValidationPolicySimpleHierarchyForInterestOnly : public ValidationPolicySimpleHierarchy
+{
+public:
+ void
+ checkPolicy(const Data& data, const shared_ptr<ValidationState>& state,
+ const ValidationContinuation& continueValidation) override
+ {
+ continueValidation(nullptr, state);
+ }
+};
+
+BOOST_FIXTURE_TEST_CASE(ValidateInterestsButBypassForData,
+ HierarchicalValidatorFixture<ValidationPolicySimpleHierarchyForInterestOnly>)
+{
+ Interest interest("/Security/V2/ValidatorFixture/Sub1/Sub2/Interest");
+ Data data("/Security/V2/ValidatorFixture/Sub1/Sub2/Interest");
+
+ VALIDATE_FAILURE(interest, "Unsigned");
+ VALIDATE_SUCCESS(data, "Policy requests validation bypassing for all data");
+ BOOST_CHECK_EQUAL(face.sentInterests.size(), 0);
+ face.sentInterests.clear();
+
+ interest = Interest("/Security/V2/ValidatorFixture/Sub1/Sub2/Interest");
+ m_keyChain.sign(interest, signingWithSha256());
+ m_keyChain.sign(data, signingWithSha256());
+ VALIDATE_FAILURE(interest, "Required KeyLocator/Name missing (not passed to policy)");
+ VALIDATE_SUCCESS(data, "Policy requests validation bypassing for all data");
+ BOOST_CHECK_EQUAL(face.sentInterests.size(), 0);
+ face.sentInterests.clear();
+
+ m_keyChain.sign(interest, signingByIdentity(identity));
+ m_keyChain.sign(data, signingByIdentity(identity));
+ VALIDATE_SUCCESS(interest, "Should get accepted, as signed by the anchor");
+ VALIDATE_SUCCESS(data, "Policy requests validation bypassing for all data");
+ BOOST_CHECK_EQUAL(face.sentInterests.size(), 0);
+ face.sentInterests.clear();
+
+ m_keyChain.sign(interest, signingByIdentity(subIdentity));
+ m_keyChain.sign(data, signingByIdentity(subIdentity));
+ VALIDATE_FAILURE(interest, "Should fail, as policy is not allowed to create new trust anchors");
+ VALIDATE_SUCCESS(data, "Policy requests validation bypassing for all data");
+ BOOST_CHECK_EQUAL(face.sentInterests.size(), 1);
+ face.sentInterests.clear();
+
+ m_keyChain.sign(interest, signingByIdentity(otherIdentity));
+ m_keyChain.sign(data, signingByIdentity(otherIdentity));
+ VALIDATE_FAILURE(interest, "Should fail, as signed by the policy-violating cert");
+ VALIDATE_SUCCESS(data, "Policy requests validation bypassing for all data");
+ // no network operations expected, as certificate is not validated by the policy
+ BOOST_CHECK_EQUAL(face.sentInterests.size(), 0);
+ face.sentInterests.clear();
+
+ advanceClocks(1_h, 2); // expire trusted cache
+
+ m_keyChain.sign(interest, signingByIdentity(subSelfSignedIdentity));
+ m_keyChain.sign(data, signingByIdentity(subSelfSignedIdentity));
+ VALIDATE_FAILURE(interest, "Should fail, as policy is not allowed to create new trust anchors");
+ VALIDATE_SUCCESS(data, "Policy requests validation bypassing for all data");
+ BOOST_CHECK_EQUAL(face.sentInterests.size(), 1);
+ face.sentInterests.clear();
+}
+
+BOOST_AUTO_TEST_CASE(InfiniteCertChain)
+{
+ processInterest = [this] (const Interest& interest) {
+ // create another key for the same identity and sign it properly
+ Key parentKey = m_keyChain.createKey(subIdentity);
+ Key requestedKey = subIdentity.getKey(interest.getName());
+
+ Name certificateName = requestedKey.getName();
+ certificateName
+ .append("looper")
+ .appendVersion();
+ v2::Certificate certificate;
+ certificate.setName(certificateName);
+
+ // set metainfo
+ certificate.setContentType(tlv::ContentType_Key);
+ certificate.setFreshnessPeriod(1_h);
+
+ // set content
+ certificate.setContent(requestedKey.getPublicKey().data(), requestedKey.getPublicKey().size());
+
+ // set signature-info
+ SignatureInfo info;
+ info.setValidityPeriod(security::ValidityPeriod(time::system_clock::now() - 10_days,
+ time::system_clock::now() + 10_days));
+
+ m_keyChain.sign(certificate, signingByKey(parentKey).setSignatureInfo(info));
+ face.receive(certificate);
+ };
+
+ Data data("/Security/V2/ValidatorFixture/Sub1/Sub2/Data");
+ m_keyChain.sign(data, signingByIdentity(subIdentity));
+
+ validator.setMaxDepth(40);
+ BOOST_CHECK_EQUAL(validator.getMaxDepth(), 40);
+ VALIDATE_FAILURE(data, "Should fail, as certificate should be looped");
+ BOOST_CHECK_EQUAL(face.sentInterests.size(), 40);
+ face.sentInterests.clear();
+
+ advanceClocks(1_h, 5); // expire caches
+
+ validator.setMaxDepth(30);
+ BOOST_CHECK_EQUAL(validator.getMaxDepth(), 30);
+ VALIDATE_FAILURE(data, "Should fail, as certificate chain is infinite");
+ BOOST_CHECK_EQUAL(face.sentInterests.size(), 30);
+}
+
+BOOST_AUTO_TEST_CASE(LoopedCertChain)
+{
+ auto s1 = addIdentity("/loop");
+ auto k1 = m_keyChain.createKey(s1, RsaKeyParams(name::Component("key1")));
+ auto k2 = m_keyChain.createKey(s1, RsaKeyParams(name::Component("key2")));
+ auto k3 = m_keyChain.createKey(s1, RsaKeyParams(name::Component("key3")));
+
+ auto makeCert = [this] (Key& key, const Key& signer) {
+ v2::Certificate request = key.getDefaultCertificate();
+ request.setName(Name(key.getName()).append("looper").appendVersion());
+
+ SignatureInfo info;
+ info.setValidityPeriod({time::system_clock::now() - 100_days,
+ time::system_clock::now() + 100_days});
+ m_keyChain.sign(request, signingByKey(signer).setSignatureInfo(info));
+ m_keyChain.addCertificate(key, request);
+
+ cache.insert(request);
+ };
+
+ makeCert(k1, k2);
+ makeCert(k2, k3);
+ makeCert(k3, k1);
+
+ Data data("/loop/Data");
+ m_keyChain.sign(data, signingByKey(k1));
+ VALIDATE_FAILURE(data, "Should fail, as certificate chain loops");
+ BOOST_CHECK_EQUAL(face.sentInterests.size(), 3);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestValidator
+BOOST_AUTO_TEST_SUITE_END() // V2
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace v2
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/validator-config.t.cpp b/tests/unit/security/validator-config.t.cpp
new file mode 100644
index 0000000..3e49d24
--- /dev/null
+++ b/tests/unit/security/validator-config.t.cpp
@@ -0,0 +1,159 @@
+/* -*- 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 "security/validator-config.hpp"
+#include "security/command-interest-signer.hpp"
+#include "security/v2/certificate-fetcher-offline.hpp"
+#include "util/dummy-client-face.hpp"
+
+#include "boost-test.hpp"
+#include "identity-management-fixture.hpp"
+#include "v2/validator-config/common.hpp"
+
+namespace ndn {
+namespace security {
+namespace tests {
+
+using namespace ndn::tests;
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_FIXTURE_TEST_SUITE(TestValidatorConfig, IdentityManagementFixture)
+
+// This test only for API, actual tests are in ValidationPolicyConfig and corresponding CertificateFetchers
+
+BOOST_AUTO_TEST_CASE(Construct)
+{
+ util::DummyClientFace face;
+
+ ValidatorConfig v1(face);
+ BOOST_CHECK_EQUAL(v1.m_policyConfig.m_isConfigured, false);
+
+ ValidatorConfig v2(make_unique<v2::CertificateFetcherOffline>());
+ BOOST_CHECK_EQUAL(v2.m_policyConfig.m_isConfigured, false);
+}
+
+class ValidatorConfigFixture : public IdentityManagementFixture
+{
+public:
+ ValidatorConfigFixture()
+ : path(boost::filesystem::path(UNIT_TEST_CONFIG_PATH) / "security" / "validator-config")
+ , validator(make_unique<v2::CertificateFetcherOffline>())
+ {
+ boost::filesystem::create_directories(path);
+ config = R"CONF(
+ trust-anchor
+ {
+ type any
+ }
+ )CONF";
+ configFile = (this->path / "config.conf").string();
+ std::ofstream f(configFile.c_str());
+ f << config;
+ }
+
+ ~ValidatorConfigFixture()
+ {
+ boost::filesystem::remove_all(path);
+ }
+
+public:
+ const boost::filesystem::path path;
+ std::string config;
+ std::string configFile;
+ ValidatorConfig validator;
+};
+
+BOOST_FIXTURE_TEST_SUITE(Loads, ValidatorConfigFixture)
+
+BOOST_AUTO_TEST_CASE(FromFile)
+{
+ validator.load(configFile);
+ BOOST_CHECK_EQUAL(validator.m_policyConfig.m_isConfigured, true);
+
+ // should reload policy
+ validator.load(configFile);
+ BOOST_CHECK_EQUAL(validator.m_policyConfig.m_isConfigured, true);
+}
+
+BOOST_AUTO_TEST_CASE(FromString)
+{
+ validator.load(config, "config-file-from-string");
+ BOOST_CHECK_EQUAL(validator.m_policyConfig.m_isConfigured, true);
+
+ // should reload policy
+ validator.load(config, "config-file-from-string");
+ BOOST_CHECK_EQUAL(validator.m_policyConfig.m_isConfigured, true);
+}
+
+BOOST_AUTO_TEST_CASE(FromIstream)
+{
+ std::istringstream is(config);
+ validator.load(is, "config-file-from-istream");
+ BOOST_CHECK_EQUAL(validator.m_policyConfig.m_isConfigured, true);
+
+ // should reload policy
+ std::istringstream is2(config);
+ validator.load(is2, "config-file-from-istream");
+ BOOST_CHECK_EQUAL(validator.m_policyConfig.m_isConfigured, true);
+}
+
+BOOST_AUTO_TEST_CASE(FromSection)
+{
+ validator.load(v2::validator_config::tests::makeSection(config), "config-file-from-section");
+ BOOST_CHECK_EQUAL(validator.m_policyConfig.m_isConfigured, true);
+
+ // should reload policy
+ validator.load(v2::validator_config::tests::makeSection(config), "config-file-from-section");
+ BOOST_CHECK_EQUAL(validator.m_policyConfig.m_isConfigured, true);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // Loads
+
+
+BOOST_FIXTURE_TEST_CASE(ValidateCommandInterestWithDigestSha256, ValidatorConfigFixture) // Bug 4635
+{
+ validator.load(configFile);
+
+ CommandInterestSigner signer(m_keyChain);
+ auto i = signer.makeCommandInterest("/hello/world/CMD", signingWithSha256());
+ size_t nValidated = 0, nFailed = 0;
+
+ validator.validate(i, [&] (auto&&...) { ++nValidated; }, [&] (auto&&...) { ++nFailed; });
+ BOOST_CHECK_EQUAL(nValidated, 1);
+ BOOST_CHECK_EQUAL(nFailed, 0);
+
+ validator.validate(i, [&] (auto&&...) { ++nValidated; }, [&] (auto&&...) { ++nFailed; });
+ BOOST_CHECK_EQUAL(nValidated, 1);
+ BOOST_CHECK_EQUAL(nFailed, 1);
+
+ i = signer.makeCommandInterest("/hello/world/CMD", signingWithSha256());
+ validator.validate(i, [&] (auto&&...) { ++nValidated; }, [&] (auto&&...) { ++nFailed; });
+ BOOST_CHECK_EQUAL(nValidated, 2);
+ BOOST_CHECK_EQUAL(nFailed, 1);
+}
+
+
+BOOST_AUTO_TEST_SUITE_END() // TestValidatorConfig
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/validator-null.t.cpp b/tests/unit/security/validator-null.t.cpp
new file mode 100644
index 0000000..69cef48
--- /dev/null
+++ b/tests/unit/security/validator-null.t.cpp
@@ -0,0 +1,66 @@
+/* -*- 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 "security/validator-null.hpp"
+
+#include "boost-test.hpp"
+#include "identity-management-fixture.hpp"
+#include "make-interest-data.hpp"
+
+namespace ndn {
+namespace security {
+namespace tests {
+
+using namespace ndn::tests;
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_FIXTURE_TEST_SUITE(TestValidatorNull, IdentityManagementFixture)
+
+BOOST_AUTO_TEST_CASE(ValidateData)
+{
+ auto identity = addIdentity("/TestValidator/Null");
+ Data data("/Some/Other/Data/Name");
+ m_keyChain.sign(data, signingByIdentity(identity));
+
+ ValidatorNull validator;
+ validator.validate(data,
+ bind([] { BOOST_CHECK_MESSAGE(true, "Validation should succeed"); }),
+ bind([] { BOOST_CHECK_MESSAGE(false, "Validation should not have failed"); }));
+}
+
+BOOST_AUTO_TEST_CASE(ValidateInterest)
+{
+ auto identity = addIdentity("/TestValidator/Null");
+ Interest interest("/Some/Other/Interest/Name");
+ m_keyChain.sign(interest, signingByIdentity(identity));
+
+ ValidatorNull validator;
+ validator.validate(interest,
+ bind([] { BOOST_CHECK_MESSAGE(true, "Validation should succeed"); }),
+ bind([] { BOOST_CHECK_MESSAGE(false, "Validation should not have failed"); }));
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestValidatorNull
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/validity-period.t.cpp b/tests/unit/security/validity-period.t.cpp
new file mode 100644
index 0000000..14d2a0f
--- /dev/null
+++ b/tests/unit/security/validity-period.t.cpp
@@ -0,0 +1,200 @@
+/* -*- 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 "security/validity-period.hpp"
+
+#include "boost-test.hpp"
+#include "../unit-test-time-fixture.hpp"
+
+#include <boost/lexical_cast.hpp>
+
+namespace ndn {
+namespace security {
+namespace tests {
+
+using namespace ndn::tests;
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(TestValidityPeriod)
+
+BOOST_FIXTURE_TEST_CASE(ConstructorSetter, UnitTestTimeFixture)
+{
+ time::system_clock::TimePoint now = this->systemClock->getNow();
+
+ time::system_clock::TimePoint notBefore = now - 1_day;
+ time::system_clock::TimePoint notAfter = notBefore + 2_days;
+
+ ValidityPeriod validity1 = ValidityPeriod(notBefore, notAfter);
+
+ auto period = validity1.getPeriod();
+ BOOST_CHECK_GE(period.first, notBefore); // fractional seconds will be removed
+ BOOST_CHECK_LT(period.first, notBefore + 1_s);
+
+ BOOST_CHECK_LE(period.second, notAfter); // fractional seconds will be removed
+ BOOST_CHECK_GT(period.second, notAfter - 1_s);
+ BOOST_CHECK_EQUAL(validity1.isValid(), true);
+
+ BOOST_CHECK_EQUAL(ValidityPeriod(now - 2_days, now - 1_day).isValid(), false);
+
+ BOOST_CHECK_NO_THROW((ValidityPeriod()));
+ ValidityPeriod validity2;
+ BOOST_CHECK_EQUAL(validity2.isValid(), false);
+
+ validity2.setPeriod(notBefore, notAfter);
+ BOOST_CHECK(validity2.getPeriod() != std::make_pair(time::getUnixEpoch(), time::getUnixEpoch()));
+ BOOST_CHECK_EQUAL(validity2, validity1);
+
+ validity1.setPeriod(time::getUnixEpoch(), time::getUnixEpoch() + 10 * 365_days);
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(validity1),
+ "(19700101T000000, 19791230T000000)");
+
+ validity1.setPeriod(time::getUnixEpoch() + 1_ns,
+ time::getUnixEpoch() + (10 * 365_days) + 1_ns);
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(validity1),
+ "(19700101T000001, 19791230T000000)");
+
+ BOOST_CHECK_EQUAL(ValidityPeriod(now, now).isValid(), true);
+ BOOST_CHECK_EQUAL(ValidityPeriod(now + 1_s, now).isValid(), false);
+}
+
+const uint8_t VP1[] = {
+ 0xfd, 0x00, 0xfd, 0x26, // ValidityPeriod
+ 0xfd, 0x00, 0xfe, 0x0f, // NotBefore
+ 0x31, 0x39, 0x37, 0x30, 0x30, 0x31, 0x30, 0x31, // 19700101T000000
+ 0x54, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
+ 0xfd, 0x00, 0xff, 0x0f, // NotAfter
+ 0x31, 0x39, 0x37, 0x30, 0x30, 0x31, 0x30, 0x32, // 19700102T000000
+ 0x54, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30
+};
+
+BOOST_AUTO_TEST_CASE(EncodingDecoding)
+{
+ time::system_clock::TimePoint notBefore = time::getUnixEpoch();
+ time::system_clock::TimePoint notAfter = notBefore + 1_day;
+
+ ValidityPeriod v1(notBefore, notAfter);
+
+ BOOST_CHECK_EQUAL_COLLECTIONS(v1.wireEncode().begin(), v1.wireEncode().end(),
+ VP1, VP1 + sizeof(VP1));
+
+ BOOST_REQUIRE_NO_THROW(ValidityPeriod(Block(VP1, sizeof(VP1))));
+ Block block(VP1, sizeof(VP1));
+ ValidityPeriod v2(block);
+ BOOST_CHECK(v1.getPeriod() == v2.getPeriod());
+}
+
+const uint8_t VP_E1[] = {
+ 0xfd, 0x00, 0xff, 0x26, // ValidityPeriod (error)
+ 0xfd, 0x00, 0xfe, 0x0f, // NotBefore
+ 0x31, 0x39, 0x37, 0x30, 0x30, 0x31, 0x30, 0x31, // 19700101T000000
+ 0x54, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
+ 0xfd, 0x00, 0xff, 0x0f, // NotAfter
+ 0x31, 0x39, 0x37, 0x30, 0x30, 0x31, 0x30, 0x32, // 19700102T000000
+ 0x54, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30
+};
+
+const uint8_t VP_E2[] = {
+ 0xfd, 0x00, 0xfd, 0x26, // ValidityPeriod
+ 0xfd, 0x00, 0xff, 0x0f, // NotBefore (error)
+ 0x31, 0x39, 0x37, 0x30, 0x30, 0x31, 0x30, 0x31, // 19700101T000000
+ 0x54, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
+ 0xfd, 0x00, 0xff, 0x0f, // NotAfter
+ 0x31, 0x39, 0x37, 0x30, 0x30, 0x31, 0x30, 0x32, // 19700102T000000
+ 0x54, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30
+};
+
+const uint8_t VP_E3[] = {
+ 0xfd, 0x00, 0xfd, 0x26, // ValidityPeriod
+ 0xfd, 0x00, 0xfe, 0x0f, // NotBefore
+ 0x31, 0x39, 0x37, 0x30, 0x30, 0x31, 0x30, 0x31, // 19700101T000000
+ 0x54, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
+ 0xfd, 0x00, 0xfe, 0x0f, // NotAfter (error)
+ 0x31, 0x39, 0x37, 0x30, 0x30, 0x31, 0x30, 0x32, // 19700102T000000
+ 0x54, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30
+};
+
+const uint8_t VP_E4[] = {
+ 0xfd, 0x00, 0xfd, 0x39, // ValidityPeriod
+ 0xfd, 0x00, 0xfe, 0x0f, // NotBefore
+ 0x31, 0x39, 0x37, 0x30, 0x30, 0x31, 0x30, 0x31, // 19700101T000000
+ 0x54, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
+ 0xfd, 0x00, 0xff, 0x0f, // NotAfter
+ 0x31, 0x39, 0x37, 0x30, 0x30, 0x31, 0x30, 0x32, // 19700102T000000
+ 0x54, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
+ 0xfd, 0x00, 0xff, 0x0f, // NotAfter (error)
+ 0x31, 0x39, 0x37, 0x30, 0x30, 0x31, 0x30, 0x32, // 19700102T000000
+ 0x54, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30
+};
+
+const uint8_t VP_E5[] = {
+ 0xfd, 0x00, 0xfd, 0x13, // ValidityPeriod
+ 0xfd, 0x00, 0xfe, 0x0f, // NotBefore
+ 0x31, 0x39, 0x37, 0x30, 0x30, 0x31, 0x30, 0x31, // 19700101T000000
+ 0x54, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30
+};
+
+const uint8_t VP_E6[] = {
+ 0xfd, 0x00, 0xfd, 0x26, // ValidityPeriod
+ 0xfd, 0x00, 0xfe, 0x0f, // NotBefore
+ 0x31, 0x39, 0x37, 0x30, 0x30, 0x31, 0x30, 0x31, // 19700101T00000\xFF
+ 0x54, 0x30, 0x30, 0x30, 0x30, 0x30, 0xFF,
+ 0xfd, 0x00, 0xff, 0x0f, // NotAfter
+ 0x31, 0x39, 0x37, 0x30, 0x30, 0x31, 0x30, 0x32, // 19700102T000000
+ 0x54, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30
+};
+
+
+BOOST_AUTO_TEST_CASE(DecodingError)
+{
+ BOOST_CHECK_THROW(ValidityPeriod(Block(VP_E1, sizeof(VP_E1))), ValidityPeriod::Error);
+
+ BOOST_CHECK_THROW(ValidityPeriod(Block(VP_E2, sizeof(VP_E2))), ValidityPeriod::Error);
+ BOOST_CHECK_THROW(ValidityPeriod(Block(VP_E3, sizeof(VP_E3))), ValidityPeriod::Error);
+
+ BOOST_CHECK_THROW(ValidityPeriod(Block(VP_E4, sizeof(VP_E4))), ValidityPeriod::Error);
+ BOOST_CHECK_THROW(ValidityPeriod(Block(VP_E5, sizeof(VP_E5))), ValidityPeriod::Error);
+
+ Block emptyBlock;
+ BOOST_CHECK_THROW((ValidityPeriod(emptyBlock)), ValidityPeriod::Error);
+
+ BOOST_CHECK_THROW(ValidityPeriod(Block(VP_E6, sizeof(VP_E6))), ValidityPeriod::Error);
+}
+
+BOOST_AUTO_TEST_CASE(Comparison)
+{
+ time::system_clock::TimePoint notBefore = time::getUnixEpoch();
+ time::system_clock::TimePoint notAfter = notBefore + 1_day;
+ time::system_clock::TimePoint notAfter2 = notBefore + 2_days;
+
+ ValidityPeriod validity1(notBefore, notAfter);
+ ValidityPeriod validity2(notBefore, notAfter);
+ BOOST_CHECK(validity1 == validity2);
+
+ ValidityPeriod validity3(notBefore, notAfter2);
+ BOOST_CHECK(validity1 != validity3);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestValidityPeriod
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/security/verification-helpers.t.cpp b/tests/unit/security/verification-helpers.t.cpp
new file mode 100644
index 0000000..d06580a
--- /dev/null
+++ b/tests/unit/security/verification-helpers.t.cpp
@@ -0,0 +1,504 @@
+/* -*- 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 "security/verification-helpers.hpp"
+#include "security/transform/public-key.hpp"
+// #include "util/string-helper.hpp"
+
+#include "boost-test.hpp"
+#include "identity-management-fixture.hpp"
+#include "make-interest-data.hpp"
+
+#include <boost/mpl/list.hpp>
+
+namespace ndn {
+namespace security {
+namespace tests {
+
+using namespace ndn::tests;
+
+BOOST_AUTO_TEST_SUITE(Security)
+BOOST_AUTO_TEST_SUITE(TestVerificationHelpers)
+
+// // Using this test case to generate dataset if signature format changes
+// BOOST_FIXTURE_TEST_CASE(Generator, IdentityManagementV2Fixture)
+// {
+// Identity wrongIdentity = addIdentity("/Security/TestVerificationHelpers/Wrong");
+// std::map<std::string, SigningInfo> identities = {
+// {"Ecdsa", signingByIdentity(addIdentity("/Security/TestVerificationHelpers/EC", EcKeyParams()))},
+// {"Rsa", signingByIdentity(addIdentity("/Security/TestVerificationHelpers/RSA", RsaKeyParams()))},
+// {"Sha256", signingWithSha256()}
+// };
+
+// auto print = [] (const std::string& name, const uint8_t* buf, size_t size) {
+// std::cout << " std::vector<uint8_t> " + name + " = {\n ";
+
+// std::string hex = toHex(buf, size);
+
+// for (size_t i = 0; i < hex.size(); i++) {
+// if (i > 0 && i % 32 == 0)
+// std::cout << "\n ";
+
+// std::cout << "0x" << hex[i];
+// std::cout << hex[++i];
+
+// if ((i + 1) != hex.size())
+// std::cout << ", ";
+// }
+// std::cout << "\n };";
+// };
+
+// for (const auto& i : identities) {
+// const std::string& type = i.first;
+// const SigningInfo& signingInfo = i.second;
+
+// std::cout << "struct " + type + "Dataset\n{\n";
+// std::cout << " const std::string name = \"" << type << "\";\n";
+
+// if (signingInfo.getSignerType() == SigningInfo::SIGNER_TYPE_ID) {
+// print("cert", signingInfo.getPibIdentity().getDefaultKey().getDefaultCertificate().wireEncode().wire(),
+// signingInfo.getPibIdentity().getDefaultKey().getDefaultCertificate().wireEncode().size());
+// }
+// else {
+// print("cert", nullptr, 0);
+// }
+// std::cout << "\n";
+
+// // Create data that can be verified by cert
+// Data data(Name("/test/data").append(type));
+// m_keyChain.sign(data, signingInfo);
+// print("goodData", data.wireEncode().wire(), data.wireEncode().size());
+// std::cout << "\n";
+
+// // Create data that cannot be verified by cert
+// m_keyChain.sign(data, signingByIdentity(wrongIdentity));
+// print("badSigData", data.wireEncode().wire(), data.wireEncode().size());
+// std::cout << "\n";
+
+// // Create interest that can be verified by cert
+// Interest interest(Name("/test/interest/").append(type));
+// m_keyChain.sign(interest, signingInfo);
+// print("goodInterest", interest.wireEncode().wire(), interest.wireEncode().size());
+// std::cout << "\n";
+
+// // Create interest that cannot be verified by cert
+// m_keyChain.sign(interest, signingByIdentity(wrongIdentity));
+// print("badSigInterest", interest.wireEncode().wire(), interest.wireEncode().size());
+// std::cout << "\n};\n\n";
+// }
+// }
+
+struct EcdsaDataset
+{
+ const std::string name = "Ecdsa";
+ std::vector<uint8_t> cert = {
+ 0x06, 0xFD, 0x02, 0x59, 0x07, 0x47, 0x08, 0x08, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79,
+ 0x08, 0x17, 0x54, 0x65, 0x73, 0x74, 0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69,
+ 0x6F, 0x6E, 0x48, 0x65, 0x6C, 0x70, 0x65, 0x72, 0x73, 0x08, 0x02, 0x45, 0x43, 0x08, 0x03, 0x4B,
+ 0x45, 0x59, 0x08, 0x08, 0xDC, 0x9E, 0x3B, 0x11, 0x2A, 0x81, 0x7E, 0x92, 0x08, 0x04, 0x73, 0x65,
+ 0x6C, 0x66, 0x08, 0x09, 0xFD, 0x00, 0x00, 0x01, 0x59, 0x90, 0x5F, 0x6F, 0x4F, 0x14, 0x09, 0x18,
+ 0x01, 0x02, 0x19, 0x04, 0x00, 0x36, 0xEE, 0x80, 0x15, 0xFD, 0x01, 0x4F, 0x30, 0x82, 0x01, 0x4B,
+ 0x30, 0x82, 0x01, 0x03, 0x06, 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01, 0x30, 0x81, 0xF7,
+ 0x02, 0x01, 0x01, 0x30, 0x2C, 0x06, 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x01, 0x01, 0x02, 0x21,
+ 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0x30, 0x5B, 0x04, 0x20, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x04, 0x20, 0x5A, 0xC6, 0x35, 0xD8, 0xAA, 0x3A, 0x93, 0xE7, 0xB3,
+ 0xEB, 0xBD, 0x55, 0x76, 0x98, 0x86, 0xBC, 0x65, 0x1D, 0x06, 0xB0, 0xCC, 0x53, 0xB0, 0xF6, 0x3B,
+ 0xCE, 0x3C, 0x3E, 0x27, 0xD2, 0x60, 0x4B, 0x03, 0x15, 0x00, 0xC4, 0x9D, 0x36, 0x08, 0x86, 0xE7,
+ 0x04, 0x93, 0x6A, 0x66, 0x78, 0xE1, 0x13, 0x9D, 0x26, 0xB7, 0x81, 0x9F, 0x7E, 0x90, 0x04, 0x41,
+ 0x04, 0x6B, 0x17, 0xD1, 0xF2, 0xE1, 0x2C, 0x42, 0x47, 0xF8, 0xBC, 0xE6, 0xE5, 0x63, 0xA4, 0x40,
+ 0xF2, 0x77, 0x03, 0x7D, 0x81, 0x2D, 0xEB, 0x33, 0xA0, 0xF4, 0xA1, 0x39, 0x45, 0xD8, 0x98, 0xC2,
+ 0x96, 0x4F, 0xE3, 0x42, 0xE2, 0xFE, 0x1A, 0x7F, 0x9B, 0x8E, 0xE7, 0xEB, 0x4A, 0x7C, 0x0F, 0x9E,
+ 0x16, 0x2B, 0xCE, 0x33, 0x57, 0x6B, 0x31, 0x5E, 0xCE, 0xCB, 0xB6, 0x40, 0x68, 0x37, 0xBF, 0x51,
+ 0xF5, 0x02, 0x21, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xBC, 0xE6, 0xFA, 0xAD, 0xA7, 0x17, 0x9E, 0x84, 0xF3, 0xB9, 0xCA, 0xC2,
+ 0xFC, 0x63, 0x25, 0x51, 0x02, 0x01, 0x01, 0x03, 0x42, 0x00, 0x04, 0x78, 0xF2, 0x68, 0x89, 0x92,
+ 0x8D, 0x84, 0x17, 0xF1, 0x2B, 0x50, 0x4C, 0x9B, 0xFA, 0x6C, 0x4D, 0x8D, 0x29, 0x7D, 0x85, 0xDC,
+ 0x03, 0x09, 0x72, 0xFA, 0x06, 0xD4, 0x5C, 0xCB, 0xA6, 0x62, 0x0A, 0x7E, 0x2D, 0x50, 0xF0, 0x07,
+ 0xDE, 0xE0, 0x34, 0xF6, 0xC2, 0xAE, 0xA9, 0x32, 0x9B, 0x2C, 0xBD, 0x25, 0xAF, 0xB7, 0xE1, 0x7C,
+ 0xCD, 0x85, 0xF4, 0x6D, 0x31, 0xD6, 0xC0, 0x01, 0xC3, 0xEF, 0xB3, 0x16, 0x67, 0x1B, 0x01, 0x03,
+ 0x1C, 0x38, 0x07, 0x36, 0x08, 0x08, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x08, 0x17,
+ 0x54, 0x65, 0x73, 0x74, 0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E,
+ 0x48, 0x65, 0x6C, 0x70, 0x65, 0x72, 0x73, 0x08, 0x02, 0x45, 0x43, 0x08, 0x03, 0x4B, 0x45, 0x59,
+ 0x08, 0x08, 0xDC, 0x9E, 0x3B, 0x11, 0x2A, 0x81, 0x7E, 0x92, 0xFD, 0x00, 0xFD, 0x26, 0xFD, 0x00,
+ 0xFE, 0x0F, 0x31, 0x39, 0x37, 0x30, 0x30, 0x31, 0x30, 0x31, 0x54, 0x30, 0x30, 0x30, 0x30, 0x30,
+ 0x30, 0xFD, 0x00, 0xFF, 0x0F, 0x32, 0x30, 0x33, 0x37, 0x30, 0x31, 0x30, 0x37, 0x54, 0x30, 0x31,
+ 0x35, 0x31, 0x33, 0x30, 0x17, 0x47, 0x30, 0x45, 0x02, 0x21, 0x00, 0xC3, 0xB4, 0x2A, 0x00, 0x58,
+ 0x97, 0x42, 0xDA, 0x54, 0x4C, 0xA6, 0xEF, 0x0C, 0x40, 0x51, 0x88, 0x5C, 0x86, 0x99, 0xF2, 0xBC,
+ 0x15, 0xEA, 0x06, 0x64, 0xA5, 0xD5, 0xFF, 0x2B, 0xFA, 0xD1, 0xD3, 0x02, 0x20, 0x35, 0x4C, 0xC0,
+ 0x0D, 0x0F, 0x8E, 0x43, 0x56, 0x12, 0x60, 0x8C, 0x98, 0xF1, 0x5F, 0xC4, 0xD5, 0xF2, 0x25, 0x21,
+ 0xD4, 0x9C, 0x94, 0x55, 0x0D, 0x4F, 0xDE, 0x14, 0x51, 0x82, 0xB3, 0x8E, 0xA1
+ };
+ std::vector<uint8_t> goodData = {
+ 0x06, 0x9E, 0x07, 0x10, 0x08, 0x04, 0x74, 0x65, 0x73, 0x74, 0x08, 0x04, 0x64, 0x61, 0x74, 0x61,
+ 0x08, 0x02, 0x45, 0x63, 0x14, 0x00, 0x15, 0x00, 0x16, 0x3D, 0x1B, 0x01, 0x03, 0x1C, 0x38, 0x07,
+ 0x36, 0x08, 0x08, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x08, 0x17, 0x54, 0x65, 0x73,
+ 0x74, 0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x48, 0x65, 0x6C,
+ 0x70, 0x65, 0x72, 0x73, 0x08, 0x02, 0x45, 0x43, 0x08, 0x03, 0x4B, 0x45, 0x59, 0x08, 0x08, 0xDC,
+ 0x9E, 0x3B, 0x11, 0x2A, 0x81, 0x7E, 0x92, 0x17, 0x47, 0x30, 0x45, 0x02, 0x20, 0x29, 0x8B, 0xE5,
+ 0x0B, 0xD4, 0x24, 0x92, 0xA7, 0x5A, 0x36, 0x73, 0x5A, 0xC2, 0xBE, 0x17, 0x0D, 0xCA, 0x6C, 0xBB,
+ 0xF9, 0x15, 0x60, 0xBD, 0x08, 0x8E, 0xA9, 0x5B, 0x31, 0x94, 0xF2, 0xB7, 0x97, 0x02, 0x21, 0x00,
+ 0xA9, 0x02, 0xEF, 0x0F, 0x45, 0xCB, 0xC8, 0x5C, 0xF5, 0xD6, 0xCB, 0x90, 0x8E, 0x42, 0x07, 0xA0,
+ 0xA0, 0x34, 0x6A, 0x61, 0xAD, 0x24, 0x9E, 0xB7, 0x73, 0x98, 0xA4, 0x6C, 0x2D, 0xD6, 0x12, 0xE2
+ };
+ std::vector<uint8_t> badSigData = {
+ 0x06, 0xA2, 0x07, 0x10, 0x08, 0x04, 0x74, 0x65, 0x73, 0x74, 0x08, 0x04, 0x64, 0x61, 0x74, 0x61,
+ 0x08, 0x02, 0x45, 0x63, 0x14, 0x00, 0x15, 0x00, 0x16, 0x40, 0x1B, 0x01, 0x03, 0x1C, 0x3B, 0x07,
+ 0x39, 0x08, 0x08, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x08, 0x17, 0x54, 0x65, 0x73,
+ 0x74, 0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x48, 0x65, 0x6C,
+ 0x70, 0x65, 0x72, 0x73, 0x08, 0x05, 0x57, 0x72, 0x6F, 0x6E, 0x67, 0x08, 0x03, 0x4B, 0x45, 0x59,
+ 0x08, 0x08, 0x57, 0x76, 0xDC, 0x93, 0x8D, 0x34, 0x59, 0x9C, 0x17, 0x48, 0x30, 0x46, 0x02, 0x21,
+ 0x00, 0xB9, 0x4B, 0x40, 0x62, 0xAD, 0xAD, 0xFA, 0x75, 0x69, 0xDE, 0x48, 0x90, 0xC4, 0x11, 0x6B,
+ 0xC7, 0x9E, 0x6C, 0x3E, 0x04, 0x78, 0x53, 0xAB, 0xF2, 0x18, 0x16, 0x9F, 0x8D, 0x1A, 0x14, 0x68,
+ 0x57, 0x02, 0x21, 0x00, 0xC1, 0xA4, 0xC8, 0x00, 0x0B, 0x61, 0xA0, 0x2C, 0x88, 0x33, 0x7C, 0xE1,
+ 0x84, 0xE7, 0xF8, 0xAC, 0x8B, 0x46, 0x19, 0x9C, 0x03, 0xE4, 0xF1, 0xBE, 0x83, 0x09, 0x97, 0xD5,
+ 0xA8, 0x98, 0x1A, 0x47
+ };
+ std::vector<uint8_t> goodInterest = {
+ 0x05, 0xA9, 0x07, 0xA1, 0x08, 0x04, 0x74, 0x65, 0x73, 0x74, 0x08, 0x08, 0x69, 0x6E, 0x74, 0x65,
+ 0x72, 0x65, 0x73, 0x74, 0x08, 0x02, 0x45, 0x63, 0x08, 0x3F, 0x16, 0x3D, 0x1B, 0x01, 0x03, 0x1C,
+ 0x38, 0x07, 0x36, 0x08, 0x08, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x08, 0x17, 0x54,
+ 0x65, 0x73, 0x74, 0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x48,
+ 0x65, 0x6C, 0x70, 0x65, 0x72, 0x73, 0x08, 0x02, 0x45, 0x43, 0x08, 0x03, 0x4B, 0x45, 0x59, 0x08,
+ 0x08, 0xDC, 0x9E, 0x3B, 0x11, 0x2A, 0x81, 0x7E, 0x92, 0x08, 0x4A, 0x17, 0x48, 0x30, 0x46, 0x02,
+ 0x21, 0x00, 0xE9, 0x35, 0xF0, 0x7B, 0x51, 0xB5, 0x2E, 0xB7, 0xE8, 0x6B, 0xE8, 0xAC, 0xC7, 0xC1,
+ 0x90, 0x35, 0x64, 0x21, 0x53, 0x0F, 0x6D, 0x03, 0xB5, 0x1A, 0x58, 0xEA, 0xC4, 0x8A, 0xCA, 0xAF,
+ 0xDB, 0x4B, 0x02, 0x21, 0x00, 0x95, 0xF3, 0xEB, 0xF9, 0xF9, 0x66, 0x51, 0x87, 0x97, 0xB0, 0xBF,
+ 0xF8, 0x07, 0x5F, 0xF0, 0x70, 0x90, 0x36, 0xD8, 0x57, 0x65, 0xEA, 0xDB, 0x91, 0x79, 0x0E, 0x7E,
+ 0x0E, 0xD0, 0x20, 0x96, 0x19, 0x0A, 0x04, 0xF7, 0x2C, 0x8A, 0x4B
+ };
+ std::vector<uint8_t> badSigInterest = {
+ 0x05, 0xFD, 0x01, 0x3A, 0x07, 0xFD, 0x01, 0x30, 0x08, 0x04, 0x74, 0x65, 0x73, 0x74, 0x08, 0x08,
+ 0x69, 0x6E, 0x74, 0x65, 0x72, 0x65, 0x73, 0x74, 0x08, 0x02, 0x45, 0x63, 0x08, 0x3F, 0x16, 0x3D,
+ 0x1B, 0x01, 0x03, 0x1C, 0x38, 0x07, 0x36, 0x08, 0x08, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74,
+ 0x79, 0x08, 0x17, 0x54, 0x65, 0x73, 0x74, 0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74,
+ 0x69, 0x6F, 0x6E, 0x48, 0x65, 0x6C, 0x70, 0x65, 0x72, 0x73, 0x08, 0x02, 0x45, 0x43, 0x08, 0x03,
+ 0x4B, 0x45, 0x59, 0x08, 0x08, 0xDC, 0x9E, 0x3B, 0x11, 0x2A, 0x81, 0x7E, 0x92, 0x08, 0x4A, 0x17,
+ 0x48, 0x30, 0x46, 0x02, 0x21, 0x00, 0xE9, 0x35, 0xF0, 0x7B, 0x51, 0xB5, 0x2E, 0xB7, 0xE8, 0x6B,
+ 0xE8, 0xAC, 0xC7, 0xC1, 0x90, 0x35, 0x64, 0x21, 0x53, 0x0F, 0x6D, 0x03, 0xB5, 0x1A, 0x58, 0xEA,
+ 0xC4, 0x8A, 0xCA, 0xAF, 0xDB, 0x4B, 0x02, 0x21, 0x00, 0x95, 0xF3, 0xEB, 0xF9, 0xF9, 0x66, 0x51,
+ 0x87, 0x97, 0xB0, 0xBF, 0xF8, 0x07, 0x5F, 0xF0, 0x70, 0x90, 0x36, 0xD8, 0x57, 0x65, 0xEA, 0xDB,
+ 0x91, 0x79, 0x0E, 0x7E, 0x0E, 0xD0, 0x20, 0x96, 0x19, 0x08, 0x42, 0x16, 0x40, 0x1B, 0x01, 0x03,
+ 0x1C, 0x3B, 0x07, 0x39, 0x08, 0x08, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x08, 0x17,
+ 0x54, 0x65, 0x73, 0x74, 0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E,
+ 0x48, 0x65, 0x6C, 0x70, 0x65, 0x72, 0x73, 0x08, 0x05, 0x57, 0x72, 0x6F, 0x6E, 0x67, 0x08, 0x03,
+ 0x4B, 0x45, 0x59, 0x08, 0x08, 0x57, 0x76, 0xDC, 0x93, 0x8D, 0x34, 0x59, 0x9C, 0x08, 0x49, 0x17,
+ 0x47, 0x30, 0x45, 0x02, 0x20, 0x30, 0x34, 0xB8, 0x9C, 0x33, 0x4C, 0x54, 0x56, 0xF0, 0x34, 0x7A,
+ 0x95, 0x72, 0x20, 0xEC, 0xF5, 0x0F, 0xBB, 0x90, 0x3D, 0xC4, 0x21, 0x90, 0xB8, 0x3D, 0xA1, 0x10,
+ 0x5A, 0x56, 0x71, 0xC3, 0x3F, 0x02, 0x21, 0x00, 0xAE, 0xE0, 0x60, 0xCF, 0x2C, 0xF7, 0x54, 0xC2,
+ 0x1C, 0xC5, 0x54, 0x88, 0xDA, 0x31, 0xD2, 0xDE, 0x53, 0x56, 0xEC, 0xE2, 0xC6, 0x4F, 0xDD, 0xF2,
+ 0x78, 0x5D, 0xB3, 0xD4, 0x8E, 0x45, 0x36, 0x23, 0x0A, 0x04, 0xF7, 0x2C, 0x8A, 0x4B
+ };
+};
+
+struct RsaDataset
+{
+ const std::string name = "Rsa";
+ std::vector<uint8_t> cert = {
+ 0x06, 0xFD, 0x02, 0xED, 0x07, 0x48, 0x08, 0x08, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79,
+ 0x08, 0x17, 0x54, 0x65, 0x73, 0x74, 0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69,
+ 0x6F, 0x6E, 0x48, 0x65, 0x6C, 0x70, 0x65, 0x72, 0x73, 0x08, 0x03, 0x52, 0x53, 0x41, 0x08, 0x03,
+ 0x4B, 0x45, 0x59, 0x08, 0x08, 0xE1, 0x7A, 0x42, 0xD0, 0x60, 0x6F, 0x0F, 0xDF, 0x08, 0x04, 0x73,
+ 0x65, 0x6C, 0x66, 0x08, 0x09, 0xFD, 0x00, 0x00, 0x01, 0x59, 0x90, 0x5F, 0x6F, 0x9F, 0x14, 0x09,
+ 0x18, 0x01, 0x02, 0x19, 0x04, 0x00, 0x36, 0xEE, 0x80, 0x15, 0xFD, 0x01, 0x26, 0x30, 0x82, 0x01,
+ 0x22, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00,
+ 0x03, 0x82, 0x01, 0x0F, 0x00, 0x30, 0x82, 0x01, 0x0A, 0x02, 0x82, 0x01, 0x01, 0x00, 0xE7, 0x7B,
+ 0x1F, 0x75, 0x3C, 0xCD, 0xB4, 0x62, 0x80, 0xC3, 0xA4, 0x5E, 0xA9, 0x8A, 0x7B, 0xC6, 0x4C, 0x43,
+ 0x27, 0x0D, 0xA4, 0x54, 0xEB, 0x67, 0x4F, 0xB4, 0x6E, 0xEF, 0x6D, 0x54, 0x9B, 0x85, 0x44, 0xE9,
+ 0x6B, 0x4D, 0x09, 0x31, 0xC0, 0xDC, 0xC5, 0x06, 0x5E, 0x88, 0x6B, 0xFC, 0x0B, 0x08, 0xDE, 0x14,
+ 0x0F, 0xAB, 0xE8, 0xA7, 0xED, 0x93, 0x5D, 0x17, 0x19, 0x4B, 0x2D, 0x7D, 0x29, 0xE7, 0x43, 0x42,
+ 0x19, 0xE0, 0x3C, 0xBA, 0x8D, 0xAB, 0xE9, 0x4A, 0xBF, 0x21, 0x1E, 0x13, 0xD5, 0x0D, 0x9A, 0xC5,
+ 0xD8, 0x67, 0x4A, 0x7F, 0x2D, 0xA6, 0xA9, 0xCE, 0x31, 0x82, 0x62, 0x6B, 0x89, 0xB1, 0x78, 0xE0,
+ 0x6E, 0x19, 0x8B, 0xE6, 0x5C, 0x1A, 0x10, 0x8B, 0xC2, 0x9D, 0xF4, 0xB6, 0x66, 0xE9, 0x73, 0xD0,
+ 0x93, 0xE9, 0x0A, 0xA8, 0xDA, 0x68, 0xC1, 0x23, 0xBC, 0xBE, 0x17, 0xA0, 0x8E, 0x88, 0xC6, 0x71,
+ 0xFF, 0x25, 0x83, 0x75, 0x2C, 0x0E, 0x49, 0x76, 0x27, 0xA0, 0x9E, 0x08, 0x55, 0xA2, 0xE1, 0x60,
+ 0xAC, 0x5E, 0x03, 0xC3, 0x9E, 0xF3, 0x2B, 0x56, 0x80, 0xE0, 0x30, 0xD8, 0x0A, 0x5A, 0xAB, 0x92,
+ 0xA4, 0x32, 0xF2, 0xEC, 0xE8, 0xFB, 0x6D, 0xE2, 0xE6, 0x2F, 0x87, 0x94, 0xEA, 0xA3, 0x39, 0x47,
+ 0x70, 0x71, 0x08, 0xBC, 0x11, 0x0B, 0xC5, 0x9A, 0xCF, 0x13, 0xCA, 0x68, 0x7C, 0x22, 0xF8, 0x33,
+ 0xCA, 0x5F, 0xEB, 0x98, 0xF3, 0x29, 0x12, 0xF0, 0x33, 0xDE, 0x30, 0xC4, 0x56, 0xB5, 0x3B, 0x0B,
+ 0x0C, 0x23, 0xF2, 0x0F, 0x93, 0x3D, 0x60, 0xC9, 0xD2, 0xAA, 0x5A, 0x94, 0x3E, 0xAB, 0xFB, 0xD5,
+ 0x9B, 0xF0, 0xC9, 0x79, 0x11, 0xAD, 0x78, 0x4E, 0xE9, 0x91, 0x1E, 0x62, 0x8D, 0x81, 0x71, 0xC9,
+ 0xE4, 0x5D, 0x41, 0xDD, 0x19, 0xC6, 0x77, 0x81, 0xF6, 0xA7, 0x38, 0xD4, 0xE1, 0xB3, 0x02, 0x03,
+ 0x01, 0x00, 0x01, 0x16, 0x68, 0x1B, 0x01, 0x01, 0x1C, 0x39, 0x07, 0x37, 0x08, 0x08, 0x53, 0x65,
+ 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x08, 0x17, 0x54, 0x65, 0x73, 0x74, 0x56, 0x65, 0x72, 0x69,
+ 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x48, 0x65, 0x6C, 0x70, 0x65, 0x72, 0x73, 0x08,
+ 0x03, 0x52, 0x53, 0x41, 0x08, 0x03, 0x4B, 0x45, 0x59, 0x08, 0x08, 0xE1, 0x7A, 0x42, 0xD0, 0x60,
+ 0x6F, 0x0F, 0xDF, 0xFD, 0x00, 0xFD, 0x26, 0xFD, 0x00, 0xFE, 0x0F, 0x31, 0x39, 0x37, 0x30, 0x30,
+ 0x31, 0x30, 0x31, 0x54, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0xFD, 0x00, 0xFF, 0x0F, 0x32, 0x30,
+ 0x33, 0x37, 0x30, 0x31, 0x30, 0x37, 0x54, 0x30, 0x31, 0x35, 0x31, 0x33, 0x30, 0x17, 0xFD, 0x01,
+ 0x00, 0xD1, 0xEB, 0xB4, 0x09, 0x2B, 0x9D, 0xFA, 0x82, 0x88, 0x1A, 0xB6, 0x71, 0x99, 0xD0, 0x14,
+ 0x2B, 0xB5, 0xD8, 0xB8, 0x36, 0xA5, 0x70, 0xC0, 0xDD, 0x7E, 0xD1, 0xAD, 0x93, 0xEC, 0xCE, 0x9F,
+ 0x3E, 0x75, 0x1F, 0x7F, 0x95, 0x0A, 0x34, 0xCC, 0x8C, 0x5A, 0x45, 0x37, 0xE2, 0x65, 0xE4, 0x1D,
+ 0xEC, 0xE2, 0xC2, 0x39, 0x7F, 0xD7, 0xB7, 0x9B, 0x78, 0xDF, 0x0B, 0x39, 0x2B, 0xC6, 0x5E, 0x49,
+ 0x6F, 0xDB, 0xB0, 0x44, 0xFD, 0xC7, 0x5B, 0x00, 0xB4, 0xDD, 0x23, 0xBC, 0xA8, 0xC8, 0x9C, 0xCB,
+ 0xAD, 0x63, 0x3C, 0x40, 0x57, 0x07, 0x09, 0xC7, 0x26, 0x84, 0xBF, 0x49, 0x8B, 0xD0, 0x5A, 0xFD,
+ 0xBA, 0xA0, 0x0C, 0x06, 0xE5, 0x84, 0xA3, 0x9B, 0x36, 0x5E, 0x95, 0xBF, 0x34, 0xF5, 0x5A, 0x70,
+ 0x31, 0x3B, 0x70, 0x3E, 0x99, 0x84, 0xBA, 0x03, 0xE7, 0x5A, 0x9D, 0x6F, 0x46, 0x33, 0xA3, 0x95,
+ 0xAD, 0xB5, 0xC9, 0x20, 0x28, 0xA8, 0x6E, 0x52, 0x02, 0x97, 0x49, 0xDA, 0x89, 0x55, 0x6B, 0x5D,
+ 0xCB, 0x84, 0x75, 0x5F, 0x1F, 0x51, 0x0C, 0x59, 0x49, 0xC8, 0xCE, 0x2D, 0xC3, 0xA2, 0x2F, 0x7F,
+ 0x42, 0x71, 0x26, 0x4F, 0xF4, 0x1F, 0xBB, 0x09, 0x7D, 0xF8, 0x7E, 0x6D, 0x44, 0x74, 0xB1, 0x7F,
+ 0x76, 0xAE, 0x7B, 0x1C, 0x56, 0x75, 0x9B, 0xE7, 0x23, 0xF4, 0xF3, 0xA0, 0x3C, 0x47, 0xF0, 0x98,
+ 0x2B, 0xC1, 0x54, 0xDA, 0x75, 0x1B, 0x2E, 0x94, 0xFB, 0xB5, 0xDD, 0x44, 0xE9, 0x16, 0x27, 0xE5,
+ 0xE9, 0xB5, 0xA5, 0x70, 0x5E, 0xBC, 0x48, 0xB1, 0x02, 0x92, 0x64, 0x73, 0x08, 0x8A, 0x5C, 0xD8,
+ 0xF7, 0x58, 0x07, 0x66, 0xEF, 0x4A, 0x9E, 0xB5, 0x77, 0xDF, 0xDE, 0x54, 0xF1, 0x0A, 0xDF, 0xE1,
+ 0xE1, 0xF1, 0x76, 0x91, 0xEB, 0x78, 0xB6, 0x9A, 0x40, 0x57, 0x97, 0x9C, 0x2C, 0x8C, 0x00, 0x05,
+ 0x01
+ };
+ std::vector<uint8_t> goodData = {
+ 0x06, 0xFD, 0x01, 0x5B, 0x07, 0x11, 0x08, 0x04, 0x74, 0x65, 0x73, 0x74, 0x08, 0x04, 0x64, 0x61,
+ 0x74, 0x61, 0x08, 0x03, 0x52, 0x73, 0x61, 0x14, 0x00, 0x15, 0x00, 0x16, 0x3E, 0x1B, 0x01, 0x01,
+ 0x1C, 0x39, 0x07, 0x37, 0x08, 0x08, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x08, 0x17,
+ 0x54, 0x65, 0x73, 0x74, 0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E,
+ 0x48, 0x65, 0x6C, 0x70, 0x65, 0x72, 0x73, 0x08, 0x03, 0x52, 0x53, 0x41, 0x08, 0x03, 0x4B, 0x45,
+ 0x59, 0x08, 0x08, 0xE1, 0x7A, 0x42, 0xD0, 0x60, 0x6F, 0x0F, 0xDF, 0x17, 0xFD, 0x01, 0x00, 0x88,
+ 0xEA, 0x30, 0x2E, 0xF2, 0x40, 0xA4, 0x2F, 0xF6, 0x95, 0x83, 0x1B, 0xD7, 0x89, 0xB2, 0xDB, 0x73,
+ 0xB8, 0x88, 0x97, 0x48, 0xDC, 0xC4, 0x8F, 0xBD, 0x83, 0x49, 0x5E, 0x39, 0x14, 0xE5, 0x15, 0xE2,
+ 0x94, 0xB3, 0x7A, 0x4A, 0x46, 0x1F, 0xEE, 0x50, 0xF5, 0x1F, 0x46, 0xE9, 0xAB, 0x31, 0x94, 0x9C,
+ 0x41, 0xF1, 0xA7, 0x1E, 0xA7, 0x2F, 0x2C, 0x26, 0x5E, 0x97, 0x7D, 0x06, 0x86, 0x77, 0x63, 0x4B,
+ 0xD0, 0x38, 0x91, 0x0F, 0xB6, 0x2B, 0xFD, 0xF0, 0x77, 0xB1, 0xA8, 0x73, 0xBB, 0xF9, 0x8A, 0xED,
+ 0x8D, 0xDD, 0x32, 0x8E, 0x8A, 0xC2, 0xEE, 0x83, 0xA6, 0x72, 0xFF, 0xDB, 0x91, 0xA8, 0x83, 0x1D,
+ 0xDC, 0x37, 0x1F, 0x58, 0xF6, 0x16, 0x5D, 0x89, 0x50, 0xDD, 0x1D, 0x42, 0x96, 0x81, 0x75, 0x94,
+ 0xA7, 0x8D, 0x2E, 0x8C, 0xB2, 0x75, 0x36, 0x99, 0x7A, 0x65, 0x34, 0x07, 0xC8, 0x28, 0x98, 0xA2,
+ 0x46, 0x9D, 0x6F, 0x30, 0x8E, 0x32, 0x49, 0x20, 0xE6, 0xFC, 0x1A, 0x05, 0x4F, 0x6F, 0xE8, 0x5D,
+ 0x34, 0x1D, 0x8D, 0x7F, 0x09, 0xA8, 0xDD, 0xA7, 0x48, 0xB1, 0x14, 0xDC, 0x5A, 0xAF, 0xAA, 0x12,
+ 0xEA, 0x57, 0xDE, 0x47, 0x6C, 0xC6, 0x07, 0xBC, 0x6B, 0x76, 0xB1, 0x40, 0x56, 0x3B, 0x12, 0x47,
+ 0x1D, 0x89, 0x66, 0x12, 0x81, 0xE8, 0x8B, 0xBA, 0x70, 0x8A, 0x3E, 0x78, 0x64, 0x00, 0x3E, 0x56,
+ 0x3B, 0xCF, 0x26, 0xF0, 0x3D, 0x09, 0x1D, 0xB9, 0x22, 0x9E, 0xED, 0x67, 0xB8, 0x86, 0x1F, 0x44,
+ 0x92, 0x0A, 0x08, 0xE1, 0x8F, 0x4C, 0x6D, 0x9C, 0x11, 0x5F, 0x26, 0xEA, 0x3E, 0x95, 0x32, 0x13,
+ 0x20, 0x51, 0x95, 0x24, 0x37, 0x18, 0x13, 0x92, 0xBD, 0x22, 0xF0, 0x0E, 0xAF, 0x34, 0x37, 0x9B,
+ 0xDC, 0x61, 0x03, 0x29, 0x09, 0x9A, 0x45, 0x92, 0x1B, 0xA0, 0x29, 0x9E, 0xD8, 0x98, 0x54
+ };
+ std::vector<uint8_t> badSigData = {
+ 0x06, 0xA1, 0x07, 0x11, 0x08, 0x04, 0x74, 0x65, 0x73, 0x74, 0x08, 0x04, 0x64, 0x61, 0x74, 0x61,
+ 0x08, 0x03, 0x52, 0x73, 0x61, 0x14, 0x00, 0x15, 0x00, 0x16, 0x40, 0x1B, 0x01, 0x03, 0x1C, 0x3B,
+ 0x07, 0x39, 0x08, 0x08, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x08, 0x17, 0x54, 0x65,
+ 0x73, 0x74, 0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x48, 0x65,
+ 0x6C, 0x70, 0x65, 0x72, 0x73, 0x08, 0x05, 0x57, 0x72, 0x6F, 0x6E, 0x67, 0x08, 0x03, 0x4B, 0x45,
+ 0x59, 0x08, 0x08, 0x57, 0x76, 0xDC, 0x93, 0x8D, 0x34, 0x59, 0x9C, 0x17, 0x46, 0x30, 0x44, 0x02,
+ 0x20, 0x5E, 0xF9, 0x02, 0xF4, 0x78, 0xE7, 0x5E, 0x1B, 0xB9, 0x3B, 0x23, 0xDE, 0x9D, 0xB7, 0x87,
+ 0xC6, 0x30, 0x7F, 0x4A, 0xE2, 0xBE, 0x11, 0xFE, 0x29, 0xC7, 0x6F, 0x70, 0x97, 0xAF, 0x45, 0xE1,
+ 0x0B, 0x02, 0x20, 0x67, 0x45, 0x47, 0x52, 0xBE, 0x13, 0x59, 0x76, 0x16, 0x28, 0x70, 0xF6, 0x50,
+ 0x13, 0xB2, 0xC0, 0xFA, 0x8F, 0xF3, 0x05, 0xFF, 0xBC, 0x92, 0xAC, 0xF7, 0xD0, 0x12, 0x3A, 0x6E,
+ 0x31, 0x76, 0x02
+ };
+ std::vector<uint8_t> goodInterest = {
+ 0x05, 0xFD, 0x01, 0x69, 0x07, 0xFD, 0x01, 0x5F, 0x08, 0x04, 0x74, 0x65, 0x73, 0x74, 0x08, 0x08,
+ 0x69, 0x6E, 0x74, 0x65, 0x72, 0x65, 0x73, 0x74, 0x08, 0x03, 0x52, 0x73, 0x61, 0x08, 0x40, 0x16,
+ 0x3E, 0x1B, 0x01, 0x01, 0x1C, 0x39, 0x07, 0x37, 0x08, 0x08, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69,
+ 0x74, 0x79, 0x08, 0x17, 0x54, 0x65, 0x73, 0x74, 0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61,
+ 0x74, 0x69, 0x6F, 0x6E, 0x48, 0x65, 0x6C, 0x70, 0x65, 0x72, 0x73, 0x08, 0x03, 0x52, 0x53, 0x41,
+ 0x08, 0x03, 0x4B, 0x45, 0x59, 0x08, 0x08, 0xE1, 0x7A, 0x42, 0xD0, 0x60, 0x6F, 0x0F, 0xDF, 0x08,
+ 0xFD, 0x01, 0x04, 0x17, 0xFD, 0x01, 0x00, 0x9C, 0x44, 0x23, 0xF4, 0x3A, 0x9F, 0xDD, 0x62, 0xD2,
+ 0x47, 0xE4, 0x73, 0x9E, 0x58, 0x54, 0xF5, 0xE7, 0x72, 0x8D, 0x2E, 0x1B, 0x26, 0xE7, 0xA2, 0xA1,
+ 0x56, 0x23, 0xBD, 0xB8, 0x75, 0x17, 0x78, 0x01, 0x67, 0xF9, 0x4D, 0xA9, 0xCC, 0xB8, 0x00, 0xF6,
+ 0xCD, 0xC4, 0xB9, 0xF6, 0x50, 0xC7, 0xAB, 0x57, 0xD8, 0xA7, 0x8B, 0xA8, 0x5C, 0xED, 0xD0, 0xCC,
+ 0x29, 0xC3, 0x5D, 0x80, 0x2B, 0xFA, 0xAF, 0x0D, 0xCB, 0x29, 0x1E, 0x74, 0xA8, 0x41, 0x80, 0xDE,
+ 0x52, 0x94, 0xDD, 0xE8, 0xAA, 0xA9, 0x61, 0x83, 0xC1, 0x5F, 0xA3, 0x11, 0x48, 0x0B, 0xB6, 0x53,
+ 0xB8, 0xE3, 0x77, 0x6A, 0xED, 0xF0, 0xFA, 0xED, 0x79, 0x43, 0x10, 0x10, 0x79, 0x98, 0x5D, 0xFD,
+ 0x66, 0xBF, 0x2F, 0x14, 0x9F, 0x7D, 0xA4, 0x3C, 0xBA, 0x67, 0x5F, 0xDB, 0xE3, 0x67, 0x13, 0x96,
+ 0x60, 0xC6, 0x69, 0x78, 0x5A, 0x8D, 0x52, 0xB7, 0xB7, 0x6B, 0x7F, 0xEE, 0xF4, 0x22, 0x3A, 0x64,
+ 0xE4, 0xB4, 0xA1, 0x8B, 0xDD, 0x3F, 0x80, 0xCD, 0xF4, 0x9E, 0x92, 0x06, 0x98, 0x23, 0x47, 0x58,
+ 0x70, 0xF0, 0xAC, 0x79, 0x76, 0x91, 0x7A, 0x78, 0xDF, 0xAD, 0xDD, 0x81, 0x30, 0x01, 0x5D, 0xCE,
+ 0x37, 0xEC, 0x7E, 0xDA, 0xDA, 0x36, 0x75, 0x50, 0x52, 0x57, 0x95, 0xBF, 0xCF, 0x3A, 0xC4, 0x9F,
+ 0x52, 0x97, 0x17, 0x15, 0x99, 0xA5, 0x2F, 0x68, 0x35, 0x91, 0x70, 0xDE, 0x98, 0x8A, 0xB0, 0x5F,
+ 0xF4, 0x63, 0x14, 0xB9, 0xCC, 0x76, 0x81, 0x87, 0xAE, 0x10, 0x8E, 0x9F, 0xEC, 0xCB, 0xF2, 0x33,
+ 0x1D, 0x50, 0xD4, 0xAB, 0x5B, 0xBB, 0xB9, 0x7F, 0x8C, 0xAD, 0xEC, 0xE3, 0xF8, 0xE1, 0x63, 0xDA,
+ 0x4E, 0x0D, 0x17, 0x28, 0xCD, 0x8D, 0x16, 0x00, 0x22, 0x4A, 0x51, 0x5C, 0xB2, 0x8C, 0xE7, 0x4B,
+ 0x3B, 0x00, 0x16, 0x92, 0xAD, 0x3A, 0xAB, 0x0A, 0x04, 0x10, 0x04, 0xFB, 0x38
+ };
+ std::vector<uint8_t> badSigInterest = {
+ 0x05, 0xFD, 0x01, 0xF7, 0x07, 0xFD, 0x01, 0xED, 0x08, 0x04, 0x74, 0x65, 0x73, 0x74, 0x08, 0x08,
+ 0x69, 0x6E, 0x74, 0x65, 0x72, 0x65, 0x73, 0x74, 0x08, 0x03, 0x52, 0x73, 0x61, 0x08, 0x40, 0x16,
+ 0x3E, 0x1B, 0x01, 0x01, 0x1C, 0x39, 0x07, 0x37, 0x08, 0x08, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69,
+ 0x74, 0x79, 0x08, 0x17, 0x54, 0x65, 0x73, 0x74, 0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61,
+ 0x74, 0x69, 0x6F, 0x6E, 0x48, 0x65, 0x6C, 0x70, 0x65, 0x72, 0x73, 0x08, 0x03, 0x52, 0x53, 0x41,
+ 0x08, 0x03, 0x4B, 0x45, 0x59, 0x08, 0x08, 0xE1, 0x7A, 0x42, 0xD0, 0x60, 0x6F, 0x0F, 0xDF, 0x08,
+ 0xFD, 0x01, 0x04, 0x17, 0xFD, 0x01, 0x00, 0x9C, 0x44, 0x23, 0xF4, 0x3A, 0x9F, 0xDD, 0x62, 0xD2,
+ 0x47, 0xE4, 0x73, 0x9E, 0x58, 0x54, 0xF5, 0xE7, 0x72, 0x8D, 0x2E, 0x1B, 0x26, 0xE7, 0xA2, 0xA1,
+ 0x56, 0x23, 0xBD, 0xB8, 0x75, 0x17, 0x78, 0x01, 0x67, 0xF9, 0x4D, 0xA9, 0xCC, 0xB8, 0x00, 0xF6,
+ 0xCD, 0xC4, 0xB9, 0xF6, 0x50, 0xC7, 0xAB, 0x57, 0xD8, 0xA7, 0x8B, 0xA8, 0x5C, 0xED, 0xD0, 0xCC,
+ 0x29, 0xC3, 0x5D, 0x80, 0x2B, 0xFA, 0xAF, 0x0D, 0xCB, 0x29, 0x1E, 0x74, 0xA8, 0x41, 0x80, 0xDE,
+ 0x52, 0x94, 0xDD, 0xE8, 0xAA, 0xA9, 0x61, 0x83, 0xC1, 0x5F, 0xA3, 0x11, 0x48, 0x0B, 0xB6, 0x53,
+ 0xB8, 0xE3, 0x77, 0x6A, 0xED, 0xF0, 0xFA, 0xED, 0x79, 0x43, 0x10, 0x10, 0x79, 0x98, 0x5D, 0xFD,
+ 0x66, 0xBF, 0x2F, 0x14, 0x9F, 0x7D, 0xA4, 0x3C, 0xBA, 0x67, 0x5F, 0xDB, 0xE3, 0x67, 0x13, 0x96,
+ 0x60, 0xC6, 0x69, 0x78, 0x5A, 0x8D, 0x52, 0xB7, 0xB7, 0x6B, 0x7F, 0xEE, 0xF4, 0x22, 0x3A, 0x64,
+ 0xE4, 0xB4, 0xA1, 0x8B, 0xDD, 0x3F, 0x80, 0xCD, 0xF4, 0x9E, 0x92, 0x06, 0x98, 0x23, 0x47, 0x58,
+ 0x70, 0xF0, 0xAC, 0x79, 0x76, 0x91, 0x7A, 0x78, 0xDF, 0xAD, 0xDD, 0x81, 0x30, 0x01, 0x5D, 0xCE,
+ 0x37, 0xEC, 0x7E, 0xDA, 0xDA, 0x36, 0x75, 0x50, 0x52, 0x57, 0x95, 0xBF, 0xCF, 0x3A, 0xC4, 0x9F,
+ 0x52, 0x97, 0x17, 0x15, 0x99, 0xA5, 0x2F, 0x68, 0x35, 0x91, 0x70, 0xDE, 0x98, 0x8A, 0xB0, 0x5F,
+ 0xF4, 0x63, 0x14, 0xB9, 0xCC, 0x76, 0x81, 0x87, 0xAE, 0x10, 0x8E, 0x9F, 0xEC, 0xCB, 0xF2, 0x33,
+ 0x1D, 0x50, 0xD4, 0xAB, 0x5B, 0xBB, 0xB9, 0x7F, 0x8C, 0xAD, 0xEC, 0xE3, 0xF8, 0xE1, 0x63, 0xDA,
+ 0x4E, 0x0D, 0x17, 0x28, 0xCD, 0x8D, 0x16, 0x00, 0x22, 0x4A, 0x51, 0x5C, 0xB2, 0x8C, 0xE7, 0x4B,
+ 0x3B, 0x00, 0x16, 0x92, 0xAD, 0x3A, 0xAB, 0x08, 0x42, 0x16, 0x40, 0x1B, 0x01, 0x03, 0x1C, 0x3B,
+ 0x07, 0x39, 0x08, 0x08, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x08, 0x17, 0x54, 0x65,
+ 0x73, 0x74, 0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x48, 0x65,
+ 0x6C, 0x70, 0x65, 0x72, 0x73, 0x08, 0x05, 0x57, 0x72, 0x6F, 0x6E, 0x67, 0x08, 0x03, 0x4B, 0x45,
+ 0x59, 0x08, 0x08, 0x57, 0x76, 0xDC, 0x93, 0x8D, 0x34, 0x59, 0x9C, 0x08, 0x48, 0x17, 0x46, 0x30,
+ 0x44, 0x02, 0x20, 0x48, 0x87, 0xFE, 0x7D, 0x43, 0x35, 0x3C, 0x55, 0xCB, 0x4A, 0x4B, 0x83, 0x50,
+ 0xC2, 0x10, 0xAD, 0x01, 0x4A, 0x99, 0xED, 0x6C, 0x29, 0x38, 0xEF, 0xE4, 0x9E, 0x10, 0x23, 0x4D,
+ 0x57, 0xC3, 0xE4, 0x02, 0x20, 0x7B, 0x35, 0xDC, 0xF2, 0x98, 0xD8, 0xFE, 0x13, 0x4D, 0x3B, 0x5D,
+ 0xE7, 0xCE, 0xFF, 0x02, 0xBD, 0x6B, 0x50, 0x30, 0x8B, 0x93, 0x91, 0x7A, 0xC9, 0xE0, 0x95, 0x21,
+ 0x5F, 0x91, 0xB6, 0xEE, 0x4E, 0x0A, 0x04, 0x10, 0x04, 0xFB, 0x38
+ };
+};
+
+struct Sha256Dataset
+{
+ const std::string name = "Sha256";
+ std::vector<uint8_t> cert = {
+
+ };
+ std::vector<uint8_t> goodData = {
+ 0x06, 0x41, 0x07, 0x14, 0x08, 0x04, 0x74, 0x65, 0x73, 0x74, 0x08, 0x04, 0x64, 0x61, 0x74, 0x61,
+ 0x08, 0x06, 0x53, 0x68, 0x61, 0x32, 0x35, 0x36, 0x14, 0x00, 0x15, 0x00, 0x16, 0x03, 0x1B, 0x01,
+ 0x00, 0x17, 0x20, 0xE2, 0xE2, 0x2F, 0x02, 0x70, 0xA7, 0xF7, 0x48, 0x70, 0x45, 0x29, 0x46, 0xBD,
+ 0xD2, 0x62, 0x24, 0xA6, 0x1E, 0x1D, 0x75, 0x2A, 0x26, 0x98, 0x04, 0xAD, 0x9C, 0x47, 0x63, 0xF8,
+ 0x98, 0x5A, 0x49
+ };
+ std::vector<uint8_t> badSigData = {
+ 0x06, 0xA5, 0x07, 0x14, 0x08, 0x04, 0x74, 0x65, 0x73, 0x74, 0x08, 0x04, 0x64, 0x61, 0x74, 0x61,
+ 0x08, 0x06, 0x53, 0x68, 0x61, 0x32, 0x35, 0x36, 0x14, 0x00, 0x15, 0x00, 0x16, 0x40, 0x1B, 0x01,
+ 0x03, 0x1C, 0x3B, 0x07, 0x39, 0x08, 0x08, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x08,
+ 0x17, 0x54, 0x65, 0x73, 0x74, 0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F,
+ 0x6E, 0x48, 0x65, 0x6C, 0x70, 0x65, 0x72, 0x73, 0x08, 0x05, 0x57, 0x72, 0x6F, 0x6E, 0x67, 0x08,
+ 0x03, 0x4B, 0x45, 0x59, 0x08, 0x08, 0x57, 0x76, 0xDC, 0x93, 0x8D, 0x34, 0x59, 0x9C, 0x17, 0x47,
+ 0x30, 0x45, 0x02, 0x20, 0x5F, 0x5E, 0x1E, 0xC0, 0xB5, 0xAC, 0x13, 0xF9, 0x51, 0x2F, 0x22, 0x33,
+ 0xFB, 0xDE, 0x57, 0xF6, 0xC8, 0xBF, 0xAE, 0x55, 0x3A, 0xDC, 0x30, 0x8A, 0x12, 0x61, 0xB3, 0x5D,
+ 0xB9, 0x31, 0x95, 0xD3, 0x02, 0x21, 0x00, 0xFA, 0xEC, 0x54, 0xEB, 0x35, 0x4D, 0xBF, 0x87, 0x4C,
+ 0xD8, 0x20, 0x3A, 0xE5, 0x05, 0x2C, 0xA1, 0x70, 0x74, 0x2E, 0xF9, 0x1E, 0xE1, 0xEF, 0xB9, 0x47,
+ 0xC4, 0x53, 0x57, 0xED, 0xB5, 0xB7, 0x60
+ };
+ std::vector<uint8_t> goodInterest = {
+ 0x05, 0x4B, 0x07, 0x43, 0x08, 0x04, 0x74, 0x65, 0x73, 0x74, 0x08, 0x08, 0x69, 0x6E, 0x74, 0x65,
+ 0x72, 0x65, 0x73, 0x74, 0x08, 0x06, 0x53, 0x68, 0x61, 0x32, 0x35, 0x36, 0x08, 0x05, 0x16, 0x03,
+ 0x1B, 0x01, 0x00, 0x08, 0x22, 0x17, 0x20, 0x38, 0x65, 0xB5, 0x37, 0x8A, 0xF4, 0xEB, 0xB9, 0xCD,
+ 0xEF, 0x18, 0x49, 0xF5, 0x79, 0x85, 0xE5, 0x76, 0x6F, 0x3C, 0x72, 0x63, 0x0E, 0x4F, 0x5D, 0xC7,
+ 0x42, 0x6B, 0xDF, 0xB0, 0xE1, 0x75, 0x2C, 0x0A, 0x04, 0xEE, 0x0A, 0x69, 0x16
+ };
+ std::vector<uint8_t> badSigInterest = {
+ 0x05, 0xD9, 0x07, 0xD1, 0x08, 0x04, 0x74, 0x65, 0x73, 0x74, 0x08, 0x08, 0x69, 0x6E, 0x74, 0x65,
+ 0x72, 0x65, 0x73, 0x74, 0x08, 0x06, 0x53, 0x68, 0x61, 0x32, 0x35, 0x36, 0x08, 0x05, 0x16, 0x03,
+ 0x1B, 0x01, 0x00, 0x08, 0x22, 0x17, 0x20, 0x38, 0x65, 0xB5, 0x37, 0x8A, 0xF4, 0xEB, 0xB9, 0xCD,
+ 0xEF, 0x18, 0x49, 0xF5, 0x79, 0x85, 0xE5, 0x76, 0x6F, 0x3C, 0x72, 0x63, 0x0E, 0x4F, 0x5D, 0xC7,
+ 0x42, 0x6B, 0xDF, 0xB0, 0xE1, 0x75, 0x2C, 0x08, 0x42, 0x16, 0x40, 0x1B, 0x01, 0x03, 0x1C, 0x3B,
+ 0x07, 0x39, 0x08, 0x08, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x08, 0x17, 0x54, 0x65,
+ 0x73, 0x74, 0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x48, 0x65,
+ 0x6C, 0x70, 0x65, 0x72, 0x73, 0x08, 0x05, 0x57, 0x72, 0x6F, 0x6E, 0x67, 0x08, 0x03, 0x4B, 0x45,
+ 0x59, 0x08, 0x08, 0x57, 0x76, 0xDC, 0x93, 0x8D, 0x34, 0x59, 0x9C, 0x08, 0x48, 0x17, 0x46, 0x30,
+ 0x44, 0x02, 0x20, 0x73, 0xB7, 0x0E, 0x17, 0x6C, 0x98, 0xB5, 0x6B, 0x25, 0x99, 0x2C, 0x6E, 0x41,
+ 0x26, 0xE6, 0x08, 0xCF, 0x81, 0xB9, 0x51, 0x53, 0x6A, 0x6B, 0x21, 0xF3, 0x2D, 0x4D, 0x62, 0x53,
+ 0x86, 0x85, 0xEE, 0x02, 0x20, 0x7D, 0x9D, 0xFF, 0xE3, 0x18, 0xF7, 0xBD, 0x7F, 0x9B, 0xC6, 0x4D,
+ 0x76, 0x09, 0x58, 0x74, 0x69, 0x67, 0x9B, 0x51, 0xBC, 0x14, 0xF0, 0x1C, 0x46, 0xA7, 0xA3, 0xA7,
+ 0xCC, 0x9A, 0xBB, 0x33, 0x07, 0x0A, 0x04, 0xEE, 0x0A, 0x69, 0x16
+ };
+};
+
+// Note about the datasets:
+// - .cert a valid certificate
+// - .goodData is a data packet that can be verified by .cert
+// - .badSigData a valid and signed data packet that cannot be verified by cert (signed by a
+// different private key)
+// - .goodInterest is an interest packet that can be verified by .cert
+// - .badSigInterest a valid and signed interest packet that cannot be verified by cert
+// (signed by a different private key)
+
+typedef boost::mpl::list<EcdsaDataset, RsaDataset> SignatureDatasets;
+
+BOOST_AUTO_TEST_CASE_TEMPLATE(VerifySignature, Dataset, SignatureDatasets)
+{
+ Dataset dataset;
+ v2::Certificate cert(Block(dataset.cert.data(), dataset.cert.size()));
+ Buffer keyRaw = cert.getPublicKey();
+ v2::PublicKey key;
+ key.loadPkcs8(keyRaw.data(), keyRaw.size());
+ Data data(Block(dataset.goodData.data(), dataset.goodData.size()));
+ Data badSigData(Block(dataset.badSigData.data(), dataset.badSigData.size()));
+ Interest interest(Block(dataset.goodInterest.data(), dataset.goodInterest.size()));
+ Interest badSigInterest(Block(dataset.badSigInterest.data(), dataset.badSigInterest.size()));
+
+ BOOST_CHECK(verifySignature(data, key));
+ BOOST_CHECK(verifySignature(data, keyRaw.data(), keyRaw.size()));
+ BOOST_CHECK(verifySignature(data, cert));
+ BOOST_CHECK(verifySignature(interest, key));
+ BOOST_CHECK(verifySignature(interest, keyRaw.data(), keyRaw.size()));
+ BOOST_CHECK(verifySignature(interest, cert));
+
+ BOOST_CHECK(!verifySignature(badSigData, key));
+ BOOST_CHECK(!verifySignature(badSigData, keyRaw.data(), keyRaw.size()));
+ BOOST_CHECK(!verifySignature(badSigData, cert));
+ BOOST_CHECK(!verifySignature(badSigInterest, key));
+ BOOST_CHECK(!verifySignature(badSigInterest, keyRaw.data(), keyRaw.size()));
+ BOOST_CHECK(!verifySignature(badSigInterest, cert));
+
+ Data unsignedData("/some/data");
+ Interest unsignedInterest1("/some/interest/with/several/name/components");
+ Interest unsignedInterest2("/interest-with-one-name-component");
+
+ BOOST_CHECK(!verifySignature(unsignedData, cert));
+ BOOST_CHECK(!verifySignature(unsignedData, key));
+ BOOST_CHECK(!verifySignature(unsignedInterest1, cert));
+ BOOST_CHECK(!verifySignature(unsignedInterest1, key));
+ BOOST_CHECK(!verifySignature(unsignedInterest2, cert));
+ BOOST_CHECK(!verifySignature(unsignedInterest2, key));
+
+ uint8_t invalidKey[] = {0x00, 0x00};
+ BOOST_CHECK(!verifySignature(unsignedData, invalidKey, sizeof(invalidKey)));
+ BOOST_CHECK(!verifySignature(unsignedInterest1, invalidKey, sizeof(invalidKey)));
+
+ // - base version of verifySignature is tested transitively
+ // - pib::Key version is tested as part of v2/key-chain.t.cpp (Security/V2/TestKeyChain)
+}
+
+typedef boost::mpl::list<Sha256Dataset> DigestDatasets;
+
+BOOST_AUTO_TEST_CASE_TEMPLATE(VerifyDigest, Dataset, DigestDatasets)
+{
+ Dataset dataset;
+ Data data(Block(dataset.goodData.data(), dataset.goodData.size()));
+ Data badSigData(Block(dataset.badSigData.data(), dataset.badSigData.size()));
+ Interest interest(Block(dataset.goodInterest.data(), dataset.goodInterest.size()));
+ Interest badSigInterest(Block(dataset.badSigInterest.data(), dataset.badSigInterest.size()));
+
+ BOOST_CHECK(verifyDigest(data, DigestAlgorithm::SHA256));
+ BOOST_CHECK(verifyDigest(interest, DigestAlgorithm::SHA256));
+
+ BOOST_CHECK(!verifyDigest(badSigData, DigestAlgorithm::SHA256));
+ BOOST_CHECK(!verifyDigest(badSigInterest, DigestAlgorithm::SHA256));
+
+ Data unsignedData("/some/data");
+ Interest unsignedInterest1("/some/interest/with/several/name/components");
+ Interest unsignedInterest2("/interest-with-one-name-component");
+
+ BOOST_CHECK(!verifyDigest(unsignedData, DigestAlgorithm::SHA256));
+ BOOST_CHECK(!verifyDigest(unsignedInterest1, DigestAlgorithm::SHA256));
+ BOOST_CHECK(!verifyDigest(unsignedInterest2, DigestAlgorithm::SHA256));
+
+ // - base version of verifyDigest is tested transitively
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestVerificationHelpers
+BOOST_AUTO_TEST_SUITE_END() // Security
+
+} // namespace tests
+} // namespace security
+} // namespace ndn
diff --git a/tests/unit/selectors.t.cpp b/tests/unit/selectors.t.cpp
new file mode 100644
index 0000000..202fd71
--- /dev/null
+++ b/tests/unit/selectors.t.cpp
@@ -0,0 +1,199 @@
+/* -*- 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 "selectors.hpp"
+
+#include "boost-test.hpp"
+
+namespace ndn {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(TestSelectors)
+
+BOOST_AUTO_TEST_CASE(DefaultConstructor)
+{
+ Selectors s;
+ BOOST_CHECK(s.empty());
+ BOOST_CHECK_EQUAL(s.getMinSuffixComponents(), -1);
+ BOOST_CHECK_EQUAL(s.getMaxSuffixComponents(), -1);
+ BOOST_CHECK(s.getPublisherPublicKeyLocator().empty());
+ BOOST_CHECK(s.getExclude().empty());
+ BOOST_CHECK_EQUAL(s.getChildSelector(), 0);
+ BOOST_CHECK_EQUAL(s.getMustBeFresh(), false);
+}
+
+BOOST_AUTO_TEST_CASE(EncodeDecodeEmpty)
+{
+ const uint8_t WIRE[] = {
+ 0x09, 0x00 // Selectors
+ };
+
+ Selectors s1;
+ Block wire1 = s1.wireEncode();
+ BOOST_CHECK_EQUAL_COLLECTIONS(wire1.begin(), wire1.end(), WIRE, WIRE + sizeof(WIRE));
+
+ Selectors s2(wire1);
+ BOOST_CHECK(s2.empty());
+ BOOST_CHECK_EQUAL(s2.getMinSuffixComponents(), -1);
+ BOOST_CHECK_EQUAL(s2.getMaxSuffixComponents(), -1);
+ BOOST_CHECK(s2.getPublisherPublicKeyLocator().empty());
+ BOOST_CHECK(s2.getExclude().empty());
+ BOOST_CHECK_EQUAL(s2.getChildSelector(), 0);
+ BOOST_CHECK_EQUAL(s2.getMustBeFresh(), false);
+
+ BOOST_CHECK(s1 == s2);
+}
+
+BOOST_AUTO_TEST_CASE(EncodeDecodeFull)
+{
+ const uint8_t WIRE[] = {
+ 0x09, 0x39, // Selectors
+ 0x0d, 0x01, 0x02, // MinSuffixComponents
+ 0x0e, 0x01, 0x06, // MaxSuffixComponent
+ 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,
+ 0x10, 0x14, // Exclude
+ 0x08, 0x04, 0x61, 0x6c, 0x65, 0x78, // GenericNameComponent
+ 0x08, 0x04, 0x78, 0x78, 0x78, 0x78, // GenericNameComponent
+ 0x13, 0x00, // Any
+ 0x08, 0x04, 0x79, 0x79, 0x79, 0x79, // GenericNameComponent
+ 0x11, 0x01, 0x01, // ChildSelector
+ 0x12, 0x00 // MustBeFresh
+ };
+
+ Selectors s1;
+ s1.setMinSuffixComponents(2);
+ s1.setMaxSuffixComponents(6);
+ s1.setPublisherPublicKeyLocator(KeyLocator("/test/key/locator"));
+ s1.setExclude(Exclude().excludeOne(name::Component("alex"))
+ .excludeRange(name::Component("xxxx"), name::Component("yyyy")));
+ s1.setChildSelector(1);
+ s1.setMustBeFresh(true);
+
+ Block wire1 = s1.wireEncode();
+ BOOST_CHECK_EQUAL_COLLECTIONS(wire1.begin(), wire1.end(), WIRE, WIRE + sizeof(WIRE));
+
+ Selectors s2(wire1);
+ BOOST_CHECK(!s2.empty());
+ BOOST_CHECK_EQUAL(s2.getMinSuffixComponents(), 2);
+ BOOST_CHECK_EQUAL(s2.getMaxSuffixComponents(), 6);
+ BOOST_CHECK_EQUAL(s2.getPublisherPublicKeyLocator().getType(), KeyLocator::KeyLocator_Name);
+ BOOST_CHECK_EQUAL(s2.getPublisherPublicKeyLocator().getName(), "ndn:/test/key/locator");
+ BOOST_CHECK_EQUAL(s2.getExclude().toUri(), "alex,xxxx,*,yyyy");
+ BOOST_CHECK_EQUAL(s2.getChildSelector(), 1);
+ BOOST_CHECK_EQUAL(s2.getMustBeFresh(), true);
+
+ BOOST_CHECK(s1 == s2);
+}
+
+BOOST_AUTO_TEST_CASE(SetChildSelector)
+{
+ Selectors s;
+ BOOST_CHECK_EQUAL(s.getChildSelector(), 0);
+ BOOST_CHECK_THROW(s.setChildSelector(-1), std::invalid_argument);
+ BOOST_CHECK_THROW(s.setChildSelector(2), std::invalid_argument);
+ s.setChildSelector(1);
+ BOOST_CHECK_EQUAL(s.getChildSelector(), 1);
+ s.setChildSelector(0);
+ BOOST_CHECK_EQUAL(s.getChildSelector(), 0);
+}
+
+BOOST_AUTO_TEST_CASE(Equality)
+{
+ // Selectors ::= SELECTORS-TYPE TLV-LENGTH
+ // MinSuffixComponents?
+ // MaxSuffixComponents?
+ // PublisherPublicKeyLocator?
+ // Exclude?
+ // ChildSelector?
+ // MustBeFresh?
+
+ Selectors a;
+ Selectors b;
+ BOOST_CHECK_EQUAL(a == b, true);
+ BOOST_CHECK_EQUAL(a != b, false);
+
+ // MinSuffixComponents
+ a.setMinSuffixComponents(1);
+ BOOST_CHECK_EQUAL(a == b, false);
+ BOOST_CHECK_EQUAL(a != b, true);
+
+ b.setMinSuffixComponents(2);
+ BOOST_CHECK_EQUAL(a == b, false);
+ BOOST_CHECK_EQUAL(a != b, true);
+
+ b.setMinSuffixComponents(1);
+ BOOST_CHECK_EQUAL(a == b, true);
+ BOOST_CHECK_EQUAL(a != b, false);
+
+ // MaxSuffixComponents
+ a.setMaxSuffixComponents(10);
+ BOOST_CHECK_EQUAL(a == b, false);
+ BOOST_CHECK_EQUAL(a != b, true);
+
+ b.setMaxSuffixComponents(10);
+ BOOST_CHECK_EQUAL(a == b, true);
+ BOOST_CHECK_EQUAL(a != b, false);
+
+ // PublisherPublicKeyLocator
+ a.setPublisherPublicKeyLocator(KeyLocator("/key/Locator/name"));
+ BOOST_CHECK_EQUAL(a == b, false);
+ BOOST_CHECK_EQUAL(a != b, true);
+
+ b.setPublisherPublicKeyLocator(KeyLocator("/key/Locator/name"));
+ BOOST_CHECK_EQUAL(a == b, true);
+ BOOST_CHECK_EQUAL(a != b, false);
+
+ // Exclude
+ a.setExclude(Exclude().excludeOne(name::Component("exclude")));
+ BOOST_CHECK_EQUAL(a == b, false);
+ BOOST_CHECK_EQUAL(a != b, true);
+
+ b.setExclude(Exclude().excludeOne(name::Component("exclude")));
+ BOOST_CHECK_EQUAL(a == b, true);
+ BOOST_CHECK_EQUAL(a != b, false);
+
+ // ChildSelector
+ a.setChildSelector(1);
+ BOOST_CHECK_EQUAL(a == b, false);
+ BOOST_CHECK_EQUAL(a != b, true);
+
+ b.setChildSelector(1);
+ BOOST_CHECK_EQUAL(a == b, true);
+ BOOST_CHECK_EQUAL(a != b, false);
+
+ // MustBeFresh
+ a.setMustBeFresh(true);
+ BOOST_CHECK_EQUAL(a == b, false);
+ BOOST_CHECK_EQUAL(a != b, true);
+
+ b.setMustBeFresh(true);
+ BOOST_CHECK_EQUAL(a == b, true);
+ BOOST_CHECK_EQUAL(a != b, false);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestSelectors
+
+} // namespace tests
+} // namespace ndn
diff --git a/tests/unit/signature-info.t.cpp b/tests/unit/signature-info.t.cpp
new file mode 100644
index 0000000..f5cf102
--- /dev/null
+++ b/tests/unit/signature-info.t.cpp
@@ -0,0 +1,264 @@
+/* -*- 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 "signature-info.hpp"
+
+#include "boost-test.hpp"
+#include <boost/lexical_cast.hpp>
+
+namespace ndn {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(TestSignatureInfo)
+
+const uint8_t sigInfoRsa[] = {
+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
+};
+
+BOOST_AUTO_TEST_CASE(Constructor)
+{
+ SignatureInfo info;
+ BOOST_CHECK_EQUAL(info.getSignatureType(), -1);
+ BOOST_CHECK_EQUAL(info.hasKeyLocator(), false);
+ BOOST_CHECK_THROW(info.getKeyLocator(), SignatureInfo::Error);
+
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(info), "Invalid SignatureInfo");
+
+ SignatureInfo sha256Info(tlv::DigestSha256);
+ BOOST_CHECK_EQUAL(sha256Info.getSignatureType(), tlv::DigestSha256);
+ BOOST_CHECK_EQUAL(sha256Info.hasKeyLocator(), false);
+ BOOST_CHECK_THROW(sha256Info.getKeyLocator(), SignatureInfo::Error);
+
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(sha256Info), "DigestSha256");
+
+ KeyLocator keyLocator("/test/key/locator");
+ SignatureInfo sha256RsaInfo(tlv::SignatureSha256WithRsa, keyLocator);
+ BOOST_CHECK_EQUAL(sha256RsaInfo.getSignatureType(), tlv::SignatureSha256WithRsa);
+ BOOST_CHECK_EQUAL(sha256RsaInfo.hasKeyLocator(), true);
+ BOOST_CHECK_NO_THROW(sha256RsaInfo.getKeyLocator());
+ BOOST_CHECK_EQUAL(sha256RsaInfo.getKeyLocator().getName(), Name("/test/key/locator"));
+
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(sha256RsaInfo), "SignatureSha256WithRsa Name=/test/key/locator");
+
+ const Block& encoded = sha256RsaInfo.wireEncode();
+ Block sigInfoBlock(sigInfoRsa, sizeof(sigInfoRsa));
+
+ BOOST_CHECK_EQUAL_COLLECTIONS(sigInfoBlock.wire(),
+ sigInfoBlock.wire() + sigInfoBlock.size(),
+ encoded.wire(),
+ encoded.wire() + encoded.size());
+
+ sha256RsaInfo = SignatureInfo(sigInfoBlock);
+ BOOST_CHECK_EQUAL(sha256RsaInfo.getSignatureType(), tlv::SignatureSha256WithRsa);
+ BOOST_CHECK_EQUAL(sha256RsaInfo.hasKeyLocator(), true);
+ BOOST_CHECK_NO_THROW(sha256RsaInfo.getKeyLocator());
+ BOOST_CHECK_EQUAL(sha256RsaInfo.getKeyLocator().getName(), Name("/test/key/locator"));
+}
+
+BOOST_AUTO_TEST_CASE(ConstructorError)
+{
+ const uint8_t error1[] = {
+ 0x15, 0x1b, // Wrong SignatureInfo (0x16, 0x1b)
+ 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
+ };
+ Block errorBlock1(error1, sizeof(error1));
+ BOOST_CHECK_THROW(SignatureInfo info(errorBlock1), tlv::Error);
+
+ const uint8_t error2[] = {
+ 0x16, 0x01, // SignatureInfo
+ 0x01 // Wrong SignatureInfo value
+ };
+ Block errorBlock2(error2, sizeof(error2));
+ BOOST_CHECK_THROW(SignatureInfo info(errorBlock2), tlv::Error);
+
+ const uint8_t error3[] = {
+ 0x16, 0x01, // SignatureInfo
+ 0x1a, 0x01, // Wrong SignatureType (0x1b, 0x1b)
+ 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
+ };
+ Block errorBlock3(error3, sizeof(error3));
+ BOOST_CHECK_THROW(SignatureInfo info(errorBlock3), tlv::Error);
+
+ const uint8_t error4[] = {
+ 0x16, 0x00 // Empty SignatureInfo
+ };
+ Block errorBlock4(error4, sizeof(error4));
+ BOOST_CHECK_THROW(SignatureInfo info(errorBlock4), tlv::Error);
+
+}
+
+BOOST_AUTO_TEST_CASE(SetterGetter)
+{
+ SignatureInfo info;
+ BOOST_CHECK_EQUAL(info.getSignatureType(), -1);
+ BOOST_CHECK_EQUAL(info.hasKeyLocator(), false);
+ BOOST_CHECK_THROW(info.getKeyLocator(), SignatureInfo::Error);
+
+ info.setSignatureType(tlv::SignatureSha256WithRsa);
+ BOOST_CHECK_EQUAL(info.getSignatureType(), tlv::SignatureSha256WithRsa);
+ BOOST_CHECK_EQUAL(info.hasKeyLocator(), false);
+
+ KeyLocator keyLocator("/test/key/locator");
+ info.setKeyLocator(keyLocator);
+ BOOST_CHECK_EQUAL(info.hasKeyLocator(), true);
+ BOOST_CHECK_NO_THROW(info.getKeyLocator());
+ BOOST_CHECK_EQUAL(info.getKeyLocator().getName(), Name("/test/key/locator"));
+
+ const Block& encoded = info.wireEncode();
+ Block sigInfoBlock(sigInfoRsa, sizeof(sigInfoRsa));
+
+ BOOST_CHECK_EQUAL_COLLECTIONS(sigInfoBlock.wire(),
+ sigInfoBlock.wire() + sigInfoBlock.size(),
+ encoded.wire(),
+ encoded.wire() + encoded.size());
+
+ info.unsetKeyLocator();
+ BOOST_CHECK_EQUAL(info.hasKeyLocator(), false);
+ BOOST_CHECK_THROW(info.getKeyLocator(), SignatureInfo::Error);
+}
+
+BOOST_AUTO_TEST_CASE(ValidityPeriodExtension)
+{
+ const uint8_t sigInfo[] = {
+ 0x16, 0x45, // 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,
+ 0xfd, 0x00, 0xfd, 0x26, // ValidityPeriod
+ 0xfd, 0x00, 0xfe, 0x0f, // NotBefore
+ 0x31, 0x39, 0x37, 0x30, 0x30, 0x31, 0x30, 0x31, // 19700101T000000
+ 0x54, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
+ 0xfd, 0x00, 0xff, 0x0f, // NotAfter
+ 0x31, 0x39, 0x37, 0x30, 0x30, 0x31, 0x30, 0x32, // 19700102T000000
+ 0x54, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30
+ };
+
+ time::system_clock::TimePoint notBefore = time::getUnixEpoch();
+ time::system_clock::TimePoint notAfter = notBefore + 1_day;
+ security::ValidityPeriod vp1(notBefore, notAfter);
+
+ // encode
+ SignatureInfo info;
+ info.setSignatureType(tlv::SignatureSha256WithRsa);
+ info.setKeyLocator(KeyLocator("/test/key/locator"));
+ info.setValidityPeriod(vp1);
+
+ BOOST_CHECK_EQUAL(info.getValidityPeriod(), vp1);
+
+ const Block& encoded = info.wireEncode();
+
+ BOOST_CHECK_EQUAL_COLLECTIONS(sigInfo, sigInfo + sizeof(sigInfo),
+ encoded.wire(), encoded.wire() + encoded.size());
+
+ // decode
+ Block block(sigInfo, sizeof(sigInfo));
+ SignatureInfo info2;
+ info2.wireDecode(block);
+ BOOST_CHECK_EQUAL(info2.getValidityPeriod(), vp1);
+
+ const security::ValidityPeriod& validityPeriod = info2.getValidityPeriod();
+ BOOST_CHECK(validityPeriod.getPeriod() == std::make_pair(notBefore, notAfter));
+}
+
+BOOST_AUTO_TEST_CASE(OtherTlvs)
+{
+ SignatureInfo info;
+ BOOST_CHECK_EQUAL(info.getSignatureType(), -1);
+ BOOST_CHECK_EQUAL(info.hasKeyLocator(), false);
+ BOOST_CHECK_THROW(info.getKeyLocator(), SignatureInfo::Error);
+
+ const uint8_t tlv1[] = {
+ 0x81, // T
+ 0x01, // L
+ 0x01, // V
+ };
+ Block block1(tlv1, sizeof(tlv1));
+
+ info.appendTypeSpecificTlv(block1);
+ BOOST_CHECK_THROW(info.getTypeSpecificTlv(0x82), SignatureInfo::Error);
+ BOOST_REQUIRE_NO_THROW(info.getTypeSpecificTlv(0x81));
+}
+
+BOOST_AUTO_TEST_CASE(OtherTlvsEncoding) // Bug #3914
+{
+ SignatureInfo info1(tlv::SignatureSha256WithRsa);
+ info1.appendTypeSpecificTlv(makeStringBlock(101, "First"));
+ info1.appendTypeSpecificTlv(makeStringBlock(102, "Second"));
+ info1.appendTypeSpecificTlv(makeStringBlock(103, "Third"));
+
+ BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(info1), "SignatureSha256WithRsa { 101 102 103 }");
+
+ SignatureInfo info2;
+ info2.wireDecode(info1.wireEncode());
+ BOOST_CHECK_EQUAL(info1, info2);
+
+ const uint8_t infoBytes[] = {
+ 0x16, 0x19, // SignatureInfo
+ 0x1b, 0x01, 0x01, // SignatureType=1
+ 0x65, 0x05, 0x46, 0x69, 0x72, 0x73, 0x74, // 101 "First"
+ 0x66, 0x06, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, // 102 "Second"
+ 0x67, 0x05, 0x54, 0x68, 0x69, 0x72, 0x64 // 103 "Third"
+ };
+
+ SignatureInfo info3(Block(infoBytes, sizeof(infoBytes)));
+ BOOST_CHECK_EQUAL(info3, info1);
+ BOOST_CHECK_EQUAL_COLLECTIONS(infoBytes, infoBytes + sizeof(infoBytes),
+ info1.wireEncode().begin(), info1.wireEncode().end());
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestSignatureInfo
+
+} // namespace tests
+} // namespace ndn
diff --git a/tests/unit/signature.t.cpp b/tests/unit/signature.t.cpp
new file mode 100644
index 0000000..e4d8eff
--- /dev/null
+++ b/tests/unit/signature.t.cpp
@@ -0,0 +1,69 @@
+/* -*- 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 "signature.hpp"
+#include "security/digest-sha256.hpp"
+#include "security/signature-sha256-with-rsa.hpp"
+
+#include "boost-test.hpp"
+
+namespace ndn {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(TestSignature)
+
+BOOST_AUTO_TEST_CASE(Equality)
+{
+ Signature a;
+ Signature b;
+
+ BOOST_CHECK_EQUAL(a == b, true);
+ BOOST_CHECK_EQUAL(a != b, false);
+
+ a = SignatureSha256WithRsa();
+ BOOST_CHECK_EQUAL(a == b, false);
+ BOOST_CHECK_EQUAL(a != b, true);
+
+ b = SignatureSha256WithRsa();
+ static const uint8_t someData[256] = {};
+ Block signatureValue = makeBinaryBlock(tlv::SignatureValue, someData, sizeof(someData));
+ b.setValue(signatureValue);
+ BOOST_CHECK_EQUAL(a == b, false);
+ BOOST_CHECK_EQUAL(a != b, true);
+
+ a.setValue(signatureValue);
+ BOOST_CHECK_EQUAL(a == b, true);
+ BOOST_CHECK_EQUAL(a != b, false);
+
+ a = DigestSha256();
+ b = SignatureSha256WithRsa();
+ BOOST_CHECK_EQUAL(a == b, false);
+ BOOST_CHECK_EQUAL(a != b, true);
+
+ b = DigestSha256();
+ BOOST_CHECK_EQUAL(a == b, true);
+ BOOST_CHECK_EQUAL(a != b, false);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestSignature
+
+} // namespace tests
+} // namespace ndn
diff --git a/tests/unit/tag-host.t.cpp b/tests/unit/tag-host.t.cpp
new file mode 100644
index 0000000..432fc74
--- /dev/null
+++ b/tests/unit/tag-host.t.cpp
@@ -0,0 +1,86 @@
+/* -*- 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 "tag-host.hpp"
+#include "data.hpp"
+#include "interest.hpp"
+
+#include "boost-test.hpp"
+
+#include <boost/mpl/vector.hpp>
+
+namespace ndn {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(TestTagHost)
+
+class TestTag : public Tag
+{
+public:
+ static constexpr int
+ getTypeId() noexcept
+ {
+ return 1;
+ }
+};
+
+class TestTag2 : public Tag
+{
+public:
+ static constexpr int
+ getTypeId() noexcept
+ {
+ return 2;
+ }
+};
+
+typedef boost::mpl::vector<TagHost, Interest, Data> Fixtures;
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE(Basic, T, Fixtures, T)
+{
+ BOOST_CHECK(this->template getTag<TestTag>() == nullptr);
+ BOOST_CHECK(this->template getTag<TestTag2>() == nullptr);
+
+ this->setTag(make_shared<TestTag>());
+
+ BOOST_CHECK(this->template getTag<TestTag>() != nullptr);
+ BOOST_CHECK(this->template getTag<TestTag2>() == nullptr);
+
+ this->setTag(make_shared<TestTag2>());
+
+ BOOST_CHECK(this->template getTag<TestTag>() != nullptr);
+ BOOST_CHECK(this->template getTag<TestTag2>() != nullptr);
+
+ this->template removeTag<TestTag2>();
+
+ BOOST_CHECK(this->template getTag<TestTag>() != nullptr);
+ BOOST_CHECK(this->template getTag<TestTag2>() == nullptr);
+
+ this->template removeTag<TestTag>();
+
+ BOOST_CHECK(this->template getTag<TestTag>() == nullptr);
+ BOOST_CHECK(this->template getTag<TestTag2>() == nullptr);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestTagHost
+
+} // namespace tests
+} // namespace ndn
diff --git a/tests/unit/tag.t.cpp b/tests/unit/tag.t.cpp
new file mode 100644
index 0000000..9e5a20d
--- /dev/null
+++ b/tests/unit/tag.t.cpp
@@ -0,0 +1,45 @@
+/* -*- 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 "tag.hpp"
+
+#include "boost-test.hpp"
+
+namespace ndn {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(TestTag)
+
+BOOST_AUTO_TEST_CASE(SimpleTag)
+{
+ typedef ndn::SimpleTag<int, 3> MyTag;
+
+ BOOST_CHECK_EQUAL(MyTag::getTypeId(), 3);
+ MyTag tag(23361); // explicitly convertible from value type
+ int value = tag; // implicitly convertible to value type
+ BOOST_CHECK_EQUAL(value, 23361);
+ BOOST_CHECK_EQUAL(tag.get(), 23361);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestTag
+
+} // namespace tests
+} // namespace ndn
diff --git a/tests/unit/test-home-env-saver.hpp b/tests/unit/test-home-env-saver.hpp
new file mode 100644
index 0000000..dc3d956
--- /dev/null
+++ b/tests/unit/test-home-env-saver.hpp
@@ -0,0 +1,56 @@
+/* -*- 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.
+ */
+
+#ifndef NDN_TESTS_UNIT_TESTS_TEST_HOME_ENV_SAVER_HPP
+#define NDN_TESTS_UNIT_TESTS_TEST_HOME_ENV_SAVER_HPP
+
+#include <string>
+#include <cstdlib>
+
+namespace ndn {
+namespace tests {
+
+class TestHomeEnvSaver
+{
+public:
+ TestHomeEnvSaver()
+ {
+ if (std::getenv("TEST_HOME") != nullptr)
+ m_HOME = std::getenv("TEST_HOME");
+ }
+
+ virtual
+ ~TestHomeEnvSaver()
+ {
+ if (!m_HOME.empty())
+ setenv("TEST_HOME", m_HOME.c_str(), 1);
+ else
+ unsetenv("TEST_HOME");
+ }
+
+private:
+ std::string m_HOME;
+};
+
+} // namespace tests
+} // namespace ndn
+
+#endif // NDN_TESTS_UNIT_TESTS_TEST_HOME_ENV_SAVER_HPP
diff --git a/tests/unit/transport/tcp-transport.t.cpp b/tests/unit/transport/tcp-transport.t.cpp
new file mode 100644
index 0000000..02bd9c3
--- /dev/null
+++ b/tests/unit/transport/tcp-transport.t.cpp
@@ -0,0 +1,90 @@
+/* -*- 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 "transport/tcp-transport.hpp"
+#include "transport-fixture.hpp"
+
+#include "boost-test.hpp"
+
+namespace ndn {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Transport)
+BOOST_FIXTURE_TEST_SUITE(TestTcpTransport, TransportFixture)
+
+using ndn::Transport;
+
+BOOST_AUTO_TEST_CASE(GetDefaultSocketNameOk)
+{
+ const auto got = TcpTransport::getSocketHostAndPortFromUri("tcp://127.0.0.1:6000");
+
+ BOOST_CHECK_EQUAL(got.first, "127.0.0.1");
+ BOOST_CHECK_EQUAL(got.second, "6000");
+}
+
+BOOST_AUTO_TEST_CASE(GetDefaultSocketHostAndPortBadMissingHost)
+{
+ BOOST_CHECK_EXCEPTION(TcpTransport::getSocketHostAndPortFromUri("tcp://:6000"),
+ Transport::Error,
+ [] (const Transport::Error& error) {
+ return error.what() == "Malformed URI: tcp://:6000"s;
+ });
+}
+
+BOOST_AUTO_TEST_CASE(GetDefaultSocketHostAndPortOkOmittedPort)
+{
+ const auto got = TcpTransport::getSocketHostAndPortFromUri("tcp://127.0.0.1");
+
+ BOOST_CHECK_EQUAL(got.first, "127.0.0.1");
+ BOOST_CHECK_EQUAL(got.second, "6363");
+}
+
+BOOST_AUTO_TEST_CASE(GetDefaultSocketHostAndPortNameOkOmittedHostOmittedPort)
+{
+ const auto got = TcpTransport::getSocketHostAndPortFromUri("tcp://");
+
+ BOOST_CHECK_EQUAL(got.first, "localhost");
+ BOOST_CHECK_EQUAL(got.second, "6363");
+}
+
+BOOST_AUTO_TEST_CASE(GetDefaultSocketHostAndPortBadWrongTransport)
+{
+ BOOST_CHECK_EXCEPTION(TcpTransport::getSocketHostAndPortFromUri("unix://"),
+ Transport::Error,
+ [] (const Transport::Error& error) {
+ return error.what() == "Cannot create TcpTransport from \"unix\" URI"s;
+ });
+}
+
+BOOST_AUTO_TEST_CASE(GetDefaultSocketHostAndPortBadMalformedUri)
+{
+ BOOST_CHECK_EXCEPTION(TcpTransport::getSocketHostAndPortFromUri("tcp"),
+ Transport::Error,
+ [] (const Transport::Error& error) {
+ return error.what() == "Malformed URI: tcp"s;
+ });
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestTcpTransport
+BOOST_AUTO_TEST_SUITE_END() // Transport
+
+} // namespace tests
+} // namespace ndn
diff --git a/tests/unit/transport/transport-fixture.hpp b/tests/unit/transport/transport-fixture.hpp
new file mode 100644
index 0000000..d25bb94
--- /dev/null
+++ b/tests/unit/transport/transport-fixture.hpp
@@ -0,0 +1,48 @@
+/* -*- 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"
+
+#ifndef NDN_TESTS_UNIT_TESTS_TRANSPORT_FIXTURE_HPP
+#define NDN_TESTS_UNIT_TESTS_TRANSPORT_FIXTURE_HPP
+
+namespace ndn {
+namespace tests {
+
+class TransportFixture : public TestHomeEnvSaver
+{
+public:
+ void
+ initializeConfig(const char* path)
+ {
+ setenv("TEST_HOME", path, 1);
+ m_config = make_unique<ConfigFile>();
+ }
+
+protected:
+ unique_ptr<ConfigFile> m_config;
+};
+
+} // namespace tests
+} // namespace ndn
+
+#endif // NDN_TESTS_UNIT_TESTS_TRANSPORT_FIXTURE_HPP
diff --git a/tests/unit/transport/unix-transport.t.cpp b/tests/unit/transport/unix-transport.t.cpp
new file mode 100644
index 0000000..f60f9d7
--- /dev/null
+++ b/tests/unit/transport/unix-transport.t.cpp
@@ -0,0 +1,67 @@
+/* -*- 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 "transport/unix-transport.hpp"
+#include "transport-fixture.hpp"
+
+#include "boost-test.hpp"
+
+namespace ndn {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Transport)
+BOOST_FIXTURE_TEST_SUITE(TestUnixTransport, TransportFixture)
+
+using ndn::Transport;
+
+BOOST_AUTO_TEST_CASE(GetDefaultSocketNameOk)
+{
+ BOOST_CHECK_EQUAL(UnixTransport::getSocketNameFromUri("unix:///tmp/test/nfd.sock"), "/tmp/test/nfd.sock");
+}
+
+BOOST_AUTO_TEST_CASE(GetDefaultSocketNameOkOmittedSocketOmittedProtocol)
+{
+ BOOST_CHECK_EQUAL(UnixTransport::getSocketNameFromUri(""), "/var/run/nfd.sock");
+}
+
+BOOST_AUTO_TEST_CASE(GetDefaultSocketNameBadWrongTransport)
+{
+ BOOST_CHECK_EXCEPTION(UnixTransport::getSocketNameFromUri("tcp://"),
+ Transport::Error,
+ [] (const Transport::Error& error) {
+ return error.what() == "Cannot create UnixTransport from \"tcp\" URI"s;
+ });
+}
+
+BOOST_AUTO_TEST_CASE(GetDefaultSocketNameBadMalformedUri)
+{
+ BOOST_CHECK_EXCEPTION(UnixTransport::getSocketNameFromUri("unix"),
+ Transport::Error,
+ [] (const Transport::Error& error) {
+ return error.what() == "Malformed URI: unix"s;
+ });
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestUnixTransport
+BOOST_AUTO_TEST_SUITE_END() // Transport
+
+} // namespace tests
+} // namespace ndn
diff --git a/tests/unit/unit-test-time-fixture.hpp b/tests/unit/unit-test-time-fixture.hpp
new file mode 100644
index 0000000..b177145
--- /dev/null
+++ b/tests/unit/unit-test-time-fixture.hpp
@@ -0,0 +1,106 @@
+/* -*- 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.
+ */
+
+#ifndef NDN_TESTS_UNIT_TESTS_UNIT_TEST_TIME_FIXTURE_HPP
+#define NDN_TESTS_UNIT_TESTS_UNIT_TEST_TIME_FIXTURE_HPP
+
+#include "util/time-unit-test-clock.hpp"
+
+#include <boost/asio/io_service.hpp>
+
+namespace ndn {
+namespace tests {
+
+/** \brief a test fixture that overrides steady clock and system clock
+ */
+class UnitTestTimeFixture
+{
+public:
+ UnitTestTimeFixture()
+ : steadyClock(make_shared<time::UnitTestSteadyClock>())
+ , systemClock(make_shared<time::UnitTestSystemClock>())
+ {
+ time::setCustomClocks(steadyClock, systemClock);
+ }
+
+ ~UnitTestTimeFixture()
+ {
+ time::setCustomClocks(nullptr, nullptr);
+ }
+
+ /** \brief advance steady and system clocks
+ *
+ * Clocks are advanced in increments of \p tick for \p nTicks ticks.
+ * After each tick, io_service is polled to process pending I/O events.
+ *
+ * Exceptions thrown during I/O events are propagated to the caller.
+ * Clock advancing would stop in case of an exception.
+ */
+ void
+ advanceClocks(const time::nanoseconds& tick, size_t nTicks = 1)
+ {
+ this->advanceClocks(tick, tick * nTicks);
+ }
+
+ /** \brief advance steady and system clocks
+ *
+ * Clocks are advanced in increments of \p tick for \p total time.
+ * The last increment might be shorter than \p tick.
+ * After each tick, io_service is polled to process pending I/O events.
+ *
+ * Exceptions thrown during I/O events are propagated to the caller.
+ * Clock advancing would stop in case of an exception.
+ */
+ void
+ advanceClocks(const time::nanoseconds& tick, const time::nanoseconds& total)
+ {
+ BOOST_ASSERT(tick > time::nanoseconds::zero());
+ BOOST_ASSERT(total >= time::nanoseconds::zero());
+
+ time::nanoseconds remaining = total;
+ while (remaining > time::nanoseconds::zero()) {
+ if (remaining >= tick) {
+ steadyClock->advance(tick);
+ systemClock->advance(tick);
+ remaining -= tick;
+ }
+ else {
+ steadyClock->advance(remaining);
+ systemClock->advance(remaining);
+ remaining = time::nanoseconds::zero();
+ }
+
+ if (io.stopped())
+ io.reset();
+ io.poll();
+ }
+ }
+
+public:
+ shared_ptr<time::UnitTestSteadyClock> steadyClock;
+ shared_ptr<time::UnitTestSystemClock> systemClock;
+ boost::asio::io_service io;
+};
+
+} // namespace tests
+} // namespace ndn
+
+#endif // NDN_TESTS_UNIT_TESTS_UNIT_TEST_TIME_FIXTURE_HPP
diff --git a/tests/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
diff --git a/tests/unit/version.t.cpp b/tests/unit/version.t.cpp
new file mode 100644
index 0000000..7fdb50d
--- /dev/null
+++ b/tests/unit/version.t.cpp
@@ -0,0 +1,60 @@
+/* -*- 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 "version.hpp"
+#include "common.hpp"
+
+#include "boost-test.hpp"
+
+#include <stdio.h>
+
+namespace ndn {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(TestVersion)
+
+BOOST_AUTO_TEST_CASE(VersionNumber)
+{
+ BOOST_TEST_MESSAGE("NDN_CXX_VERSION = " + to_string(NDN_CXX_VERSION));
+
+ BOOST_CHECK_EQUAL(NDN_CXX_VERSION, NDN_CXX_VERSION_MAJOR * 1000000 +
+ NDN_CXX_VERSION_MINOR * 1000 +
+ NDN_CXX_VERSION_PATCH);
+}
+
+BOOST_AUTO_TEST_CASE(VersionString)
+{
+ BOOST_TEST_MESSAGE("NDN_CXX_VERSION_STRING = " NDN_CXX_VERSION_STRING);
+
+ BOOST_STATIC_ASSERT(NDN_CXX_VERSION_MAJOR < 1000);
+ BOOST_STATIC_ASSERT(NDN_CXX_VERSION_MINOR < 1000);
+ BOOST_STATIC_ASSERT(NDN_CXX_VERSION_PATCH < 1000);
+ char buf[12];
+ ::snprintf(buf, sizeof(buf), "%d.%d.%d",
+ NDN_CXX_VERSION_MAJOR, NDN_CXX_VERSION_MINOR, NDN_CXX_VERSION_PATCH);
+
+ BOOST_CHECK_EQUAL(NDN_CXX_VERSION_STRING, buf);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestVersion
+
+} // namespace tests
+} // namespace ndn