publisher: add LSDB dataset publishers

refs #2280

Change-Id: Ifb1920ef9b807610890b0d0a04f24141d39f011d
diff --git a/tests/publisher/publisher-fixture.hpp b/tests/publisher/publisher-fixture.hpp
new file mode 100644
index 0000000..07da594
--- /dev/null
+++ b/tests/publisher/publisher-fixture.hpp
@@ -0,0 +1,144 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014-2015,  The University of Memphis,
+ *                           Regents of the University of California,
+ *                           Arizona Board of Regents.
+ *
+ * This file is part of NLSR (Named-data Link State Routing).
+ * See AUTHORS.md for complete list of NLSR authors and contributors.
+ *
+ * NLSR is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ *
+ * NLSR is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE.  See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * NLSR, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+#include "nlsr.hpp"
+
+#include "../boost-test.hpp"
+#include "../test-common.hpp"
+
+#include <ndn-cxx/util/dummy-client-face.hpp>
+
+namespace nlsr {
+namespace test {
+
+class PublisherFixture : public BaseFixture
+{
+public:
+  PublisherFixture()
+    : face(ndn::util::makeDummyClientFace())
+    , nlsr(g_ioService, g_scheduler, ndn::ref(*face))
+    , lsdb(nlsr, g_scheduler, nlsr.getSyncLogicHandler())
+  {
+  }
+
+  void
+  addAdjacency(AdjLsa& lsa, const std::string& name, const std::string& faceUri, double cost)
+  {
+    Adjacent adjacency(name, faceUri, cost, Adjacent::STATUS_ACTIVE, 0, 0);
+    lsa.addAdjacent(adjacency);
+  }
+
+  void
+  checkTlvLsaInfo(const tlv::LsaInfo& info, Lsa& lsa)
+  {
+    BOOST_CHECK_EQUAL(info.getOriginRouter(), lsa.getOrigRouter());
+    BOOST_CHECK_EQUAL(info.getSequenceNumber(), lsa.getLsSeqNo());
+    BOOST_CHECK_LE(info.getExpirationPeriod(), ndn::time::milliseconds(0));
+  }
+
+  void
+  checkTlvAdjLsa(const ndn::Block& block, AdjLsa& lsa)
+  {
+    BOOST_CHECK_EQUAL(block.type(), ndn::tlv::nlsr::AdjacencyLsa);
+
+    tlv::AdjacencyLsa tlvLsa;
+    BOOST_REQUIRE_NO_THROW(tlvLsa.wireDecode(block));
+
+    checkTlvAdjLsa(tlvLsa, lsa);
+  }
+
+  void
+  checkTlvAdjLsa(const tlv::AdjacencyLsa& tlvLsa, AdjLsa& lsa)
+  {
+    checkTlvLsaInfo(tlvLsa.getLsaInfo(), lsa);
+
+    std::list<tlv::Adjacency>::const_iterator it = tlvLsa.getAdjacencies().begin();
+
+    for (const Adjacent& adjacency : lsa.getAdl().getAdjList()) {
+      BOOST_CHECK_EQUAL(it->getName(), adjacency.getName());
+      BOOST_CHECK_EQUAL(it->getUri(), adjacency.getConnectingFaceUri());
+      BOOST_CHECK_EQUAL(it->getCost(), adjacency.getLinkCost());
+      ++it;
+    }
+  }
+
+  CoordinateLsa
+  createCoordinateLsa(const std::string& origin, double radius, double angle)
+  {
+    CoordinateLsa lsa(origin, CoordinateLsa::TYPE_STRING, 1, ndn::time::system_clock::now(),
+                      radius, angle);
+
+    return std::move(lsa);
+  }
+
+  void
+  checkTlvCoordinateLsa(const ndn::Block& block, CoordinateLsa& lsa)
+  {
+    BOOST_CHECK_EQUAL(block.type(), ndn::tlv::nlsr::CoordinateLsa);
+
+    tlv::CoordinateLsa tlvLsa;
+    BOOST_REQUIRE_NO_THROW(tlvLsa.wireDecode(block));
+
+    checkTlvCoordinateLsa(tlvLsa, lsa);
+  }
+
+  void
+  checkTlvCoordinateLsa(const tlv::CoordinateLsa& tlvLsa, CoordinateLsa& lsa)
+  {
+    checkTlvLsaInfo(tlvLsa.getLsaInfo(), lsa);
+
+    BOOST_CHECK_EQUAL(tlvLsa.getHyperbolicRadius(), lsa.getCorRadius());
+    BOOST_CHECK_EQUAL(tlvLsa.getHyperbolicAngle(), lsa.getCorTheta());
+  }
+
+  void
+  checkTlvNameLsa(const ndn::Block& block, NameLsa& lsa)
+  {
+    BOOST_CHECK_EQUAL(block.type(), ndn::tlv::nlsr::NameLsa);
+
+    tlv::NameLsa tlvLsa;
+    BOOST_REQUIRE_NO_THROW(tlvLsa.wireDecode(block));
+
+    checkTlvNameLsa(tlvLsa, lsa);
+  }
+
+  void
+  checkTlvNameLsa(const tlv::NameLsa& tlvLsa, NameLsa& lsa)
+  {
+    checkTlvLsaInfo(tlvLsa.getLsaInfo(), lsa);
+
+    std::list<ndn::Name>::const_iterator it = tlvLsa.getNames().begin();
+
+    for (const ndn::Name& name : lsa.getNpl().getNameList()) {
+      BOOST_CHECK_EQUAL(*it, name);
+      ++it;
+    }
+  }
+
+public:
+  shared_ptr<ndn::util::DummyClientFace> face;
+  Nlsr nlsr;
+  Lsdb lsdb;
+  ndn::KeyChain keyChain;
+};
+
+} // namespace test
+} // namespace nlsr
diff --git a/tests/publisher/test-lsa-publisher.cpp b/tests/publisher/test-lsa-publisher.cpp
new file mode 100644
index 0000000..aa7922c
--- /dev/null
+++ b/tests/publisher/test-lsa-publisher.cpp
@@ -0,0 +1,140 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014-2015,  The University of Memphis,
+ *                           Regents of the University of California,
+ *                           Arizona Board of Regents.
+ *
+ * This file is part of NLSR (Named-data Link State Routing).
+ * See AUTHORS.md for complete list of NLSR authors and contributors.
+ *
+ * NLSR is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ *
+ * NLSR is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE.  See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * NLSR, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+#include "publisher/lsa-publisher.hpp"
+#include "tlv/adjacency.hpp"
+#include "tlv/adjacency-lsa.hpp"
+#include "tlv/tlv-nlsr.hpp"
+
+#include "publisher-fixture.hpp"
+#include "../boost-test.hpp"
+
+namespace nlsr {
+namespace test {
+
+BOOST_FIXTURE_TEST_SUITE(PublisherTestLsaPublisher, PublisherFixture)
+
+BOOST_AUTO_TEST_CASE(AdjacencyLsaPublisherBasic)
+{
+  // Adjacency LSA for RouterA
+  AdjLsa routerALsa;
+  routerALsa.setOrigRouter("/RouterA");
+  addAdjacency(routerALsa, "/RouterA/adjacency1", "udp://face-1", 10);
+  lsdb.installAdjLsa(routerALsa);
+
+  // Adjacency LSA for RouterB
+  AdjLsa routerBLsa;
+  routerBLsa.setOrigRouter("/RouterB");
+  routerBLsa.setLsSeqNo(5);
+  addAdjacency(routerBLsa, "/RouterB/adjacency1", "udp://face-1", 10);
+  addAdjacency(routerBLsa, "/RouterB/adjacency2", "udp://face-2", 20);
+  addAdjacency(routerBLsa, "/RouterB/adjacency3", "udp://face-3", 30);
+  lsdb.installAdjLsa(routerBLsa);
+
+  AdjacencyLsaPublisher publisher(lsdb, *face, "/RouterA", keyChain);
+
+  publisher.publish();
+  face->processEvents(ndn::time::milliseconds(1));
+
+  BOOST_REQUIRE_EQUAL(face->sentDatas.size(), 1);
+  ndn::Block parser = face->sentDatas[0].getContent();
+  parser.parse();
+
+  // Check RouterB LSA
+  ndn::Block::element_const_iterator it = parser.elements_begin();
+  checkTlvAdjLsa(*it, routerBLsa);
+
+  // Check RouterA LSA
+  it++;
+  checkTlvAdjLsa(*it, routerALsa);
+}
+
+BOOST_AUTO_TEST_CASE(CoordinateLsaBasic)
+{
+  CoordinateLsa routerALsa = createCoordinateLsa("/RouterA", 10.0, 20.0);
+  lsdb.installCoordinateLsa(routerALsa);
+
+  CoordinateLsa routerBLsa = createCoordinateLsa("/RouterB", 123.45, 543.21);
+  lsdb.installCoordinateLsa(routerBLsa);
+
+  CoordinateLsa routerCLsa = createCoordinateLsa("/RouterC", 0.01, 0.02);
+  lsdb.installCoordinateLsa(routerCLsa);
+
+  CoordinateLsaPublisher publisher(lsdb, *face, "/RouterA", keyChain);
+
+  publisher.publish();
+  face->processEvents(ndn::time::milliseconds(1));
+
+  BOOST_REQUIRE_EQUAL(face->sentDatas.size(), 1);
+  ndn::Block parser = face->sentDatas[0].getContent();
+  parser.parse();
+
+  // Check RouterC LSA
+  ndn::Block::element_const_iterator it = parser.elements_begin();
+  checkTlvCoordinateLsa(*it, routerCLsa);
+
+  // Check RouterB LSA
+  ++it;
+  checkTlvCoordinateLsa(*it, routerBLsa);
+
+  // Check RouterA LSA
+  ++it;
+  checkTlvCoordinateLsa(*it, routerALsa);
+}
+
+BOOST_AUTO_TEST_CASE(NameLsaBasic)
+{
+  // Name LSA for RouterA
+  NameLsa routerALsa;
+  routerALsa.setOrigRouter("/RouterA");
+  routerALsa.addName("/RouterA/name1");
+  lsdb.installNameLsa(routerALsa);
+
+  // Name LSA for RouterB
+  NameLsa routerBLsa;
+  routerBLsa.setOrigRouter("/RouterB");
+  routerBLsa.addName("/RouterB/name1");
+  routerBLsa.addName("/RouterB/name2");
+  routerBLsa.addName("/RouterB/name3");
+  lsdb.installNameLsa(routerBLsa);
+
+  NameLsaPublisher publisher(lsdb, *face, "/RouterA", keyChain);
+
+  publisher.publish();
+  face->processEvents(ndn::time::milliseconds(1));
+
+  BOOST_REQUIRE_EQUAL(face->sentDatas.size(), 1);
+  ndn::Block parser = face->sentDatas[0].getContent();
+  parser.parse();
+
+  // Check RouterB LSA
+  ndn::Block::element_const_iterator it = parser.elements_begin();
+  checkTlvNameLsa(*it, routerBLsa);
+
+  // Check RouterA LSA
+  it++;
+  checkTlvNameLsa(*it, routerALsa);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace test
+} // namespace nlsr
diff --git a/tests/publisher/test-lsdb-dataset-interest-handler.cpp b/tests/publisher/test-lsdb-dataset-interest-handler.cpp
new file mode 100644
index 0000000..22b2819
--- /dev/null
+++ b/tests/publisher/test-lsdb-dataset-interest-handler.cpp
@@ -0,0 +1,145 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014-2015,  The University of Memphis,
+ *                           Regents of the University of California,
+ *                           Arizona Board of Regents.
+ *
+ * This file is part of NLSR (Named-data Link State Routing).
+ * See AUTHORS.md for complete list of NLSR authors and contributors.
+ *
+ * NLSR is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ *
+ * NLSR is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE.  See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * NLSR, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+#include "publisher/lsdb-dataset-interest-handler.hpp"
+#include "tlv/tlv-nlsr.hpp"
+
+#include "publisher-fixture.hpp"
+#include "../boost-test.hpp"
+
+#include <ndn-cxx/management/nfd-control-response.hpp>
+
+namespace nlsr {
+namespace test {
+
+void
+processDatasetInterest(shared_ptr<ndn::util::DummyClientFace> face,
+                       std::function<bool(const ndn::Block&)> isSameType)
+{
+  face->processEvents(ndn::time::milliseconds(1));
+
+  BOOST_REQUIRE_EQUAL(face->sentDatas.size(), 1);
+  ndn::Block parser(face->sentDatas[0].getContent());
+  parser.parse();
+
+  ndn::Block::element_const_iterator it = parser.elements_begin();
+  BOOST_CHECK_EQUAL(isSameType(*it), true);
+  ++it;
+
+  BOOST_CHECK(it == parser.elements_end());
+
+  face->sentDatas.clear();
+}
+
+void
+checkErrorResponse(shared_ptr<ndn::util::DummyClientFace> face, uint64_t expectedCode)
+{
+  BOOST_REQUIRE_EQUAL(face->sentDatas.size(), 1);
+
+  ndn::nfd::ControlResponse response(face->sentDatas[0].getContent().blockFromValue());
+  BOOST_CHECK_EQUAL(response.getCode(), expectedCode);
+
+  face->sentDatas.clear();
+}
+
+BOOST_FIXTURE_TEST_SUITE(PublisherTestLsdbDatasetInterestHandler, PublisherFixture)
+
+BOOST_AUTO_TEST_CASE(Basic)
+{
+  // Install adjacency LSA
+  AdjLsa adjLsa;
+  adjLsa.setOrigRouter("/RouterA");
+  addAdjacency(adjLsa, "/RouterA/adjacency1", "udp://face-1", 10);
+  lsdb.installAdjLsa(adjLsa);
+
+  // Install coordinate LSA
+  CoordinateLsa coordinateLsa = createCoordinateLsa("/RouterA", 10.0, 20.0);
+  lsdb.installCoordinateLsa(coordinateLsa);
+
+  // Install Name LSA
+  NameLsa nameLsa;
+  nameLsa.setOrigRouter("/RouterA");
+  nameLsa.addName("/RouterA/name1");
+  lsdb.installNameLsa(nameLsa);
+
+  ndn::Name thisRouter("/This/Router");
+  LsdbDatasetInterestHandler publisher(lsdb, *face, thisRouter, keyChain);
+
+  face->processEvents(ndn::time::milliseconds(10));
+
+  ndn::Name commandPrefix(thisRouter);
+  commandPrefix.append("lsdb");
+
+  // Request adjacency LSAs
+  face->receive(ndn::Interest(ndn::Name(commandPrefix).append("adjacencies")));
+
+  processDatasetInterest(face,
+    [] (const ndn::Block& block) { return block.type() == ndn::tlv::nlsr::AdjacencyLsa; });
+
+  // Request coordinate LSAs
+  face->receive(ndn::Interest(ndn::Name(commandPrefix).append("coordinates")));
+  processDatasetInterest(face,
+    [] (const ndn::Block& block) { return block.type() == ndn::tlv::nlsr::CoordinateLsa; });
+
+  // Request Name LSAs
+  face->receive(ndn::Interest(ndn::Name(commandPrefix).append("names")));
+  processDatasetInterest(face,
+    [] (const ndn::Block& block) { return block.type() == ndn::tlv::nlsr::NameLsa; });
+
+  // Request LSDB Status
+  face->receive(ndn::Interest(ndn::Name(commandPrefix).append("list")));
+  processDatasetInterest(face,
+    [] (const ndn::Block& block) { return block.type() == ndn::tlv::nlsr::LsdbStatus; });
+}
+
+BOOST_AUTO_TEST_CASE(InvalidCommand)
+{
+  ndn::Name thisRouter("/This/Router");
+  LsdbDatasetInterestHandler publisher(lsdb, *face, thisRouter, keyChain);
+
+  face->processEvents(ndn::time::milliseconds(10));
+
+  ndn::Name commandPrefix(thisRouter);
+  commandPrefix.append("lsdb");
+
+  // Unsupported command
+  face->receive(ndn::Interest(ndn::Name(commandPrefix).append("unsupported")));
+  face->processEvents(ndn::time::milliseconds(1));
+
+  checkErrorResponse(face, 501);
+
+  // Long malformed command
+  face->receive(ndn::Interest(ndn::Name(commandPrefix).append("extra").append("malformed")));
+  face->processEvents(ndn::time::milliseconds(1));
+
+  checkErrorResponse(face, 400);
+
+  // Short malformed command
+  face->receive(ndn::Interest(ndn::Name(thisRouter).append("malformed")));
+  face->processEvents(ndn::time::milliseconds(1));
+
+  BOOST_CHECK_EQUAL(face->sentDatas.size(), 0);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace test
+} // namespace nlsr
diff --git a/tests/publisher/test-lsdb-status-publisher.cpp b/tests/publisher/test-lsdb-status-publisher.cpp
new file mode 100644
index 0000000..50d48da
--- /dev/null
+++ b/tests/publisher/test-lsdb-status-publisher.cpp
@@ -0,0 +1,133 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014-2015,  The University of Memphis,
+ *                           Regents of the University of California,
+ *                           Arizona Board of Regents.
+ *
+ * This file is part of NLSR (Named-data Link State Routing).
+ * See AUTHORS.md for complete list of NLSR authors and contributors.
+ *
+ * NLSR is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ *
+ * NLSR is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE.  See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * NLSR, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+#include "publisher/lsdb-status-publisher.hpp"
+#include "tlv/lsdb-status.hpp"
+#include "tlv/tlv-nlsr.hpp"
+
+#include "publisher-fixture.hpp"
+#include "../boost-test.hpp"
+
+namespace nlsr {
+namespace test {
+
+BOOST_FIXTURE_TEST_SUITE(PublisherTestLsdbStatusPublisher, PublisherFixture)
+
+BOOST_AUTO_TEST_CASE(Basic)
+{
+  // Install adjacency LSAs
+  // Adjacency LSA for RouterA
+  AdjLsa routerAAdjLsa;
+  routerAAdjLsa.setOrigRouter("/RouterA");
+  addAdjacency(routerAAdjLsa, "/RouterA/adjacency1", "udp://face-1", 10);
+  lsdb.installAdjLsa(routerAAdjLsa);
+
+  // Adjacency LSA for RouterB
+  AdjLsa routerBAdjLsa;
+  routerBAdjLsa.setOrigRouter("/RouterB");
+  routerBAdjLsa.setLsSeqNo(5);
+  addAdjacency(routerBAdjLsa, "/RouterB/adjacency1", "udp://face-1", 10);
+  addAdjacency(routerBAdjLsa, "/RouterB/adjacency2", "udp://face-2", 20);
+  addAdjacency(routerBAdjLsa, "/RouterB/adjacency3", "udp://face-3", 30);
+  lsdb.installAdjLsa(routerBAdjLsa);
+
+  // Install coordinate LSAs
+  CoordinateLsa routerACorLsa = createCoordinateLsa("/RouterA", 10.0, 20.0);
+  lsdb.installCoordinateLsa(routerACorLsa);
+
+  CoordinateLsa routerBCorLsa = createCoordinateLsa("/RouterB", 123.45, 543.21);
+  lsdb.installCoordinateLsa(routerBCorLsa);
+
+  CoordinateLsa routerCCorLsa = createCoordinateLsa("/RouterC", 0.01, 0.02);
+  lsdb.installCoordinateLsa(routerCCorLsa);
+
+  // Install Name LSAs
+  // Name LSA for RouterA
+  NameLsa routerANameLsa;
+  routerANameLsa.setOrigRouter("/RouterA");
+  routerANameLsa.addName("/RouterA/name1");
+  lsdb.installNameLsa(routerANameLsa);
+
+  // Name LSA for RouterB
+  NameLsa routerBNameLsa;
+  routerBNameLsa.setOrigRouter("/RouterB");
+  routerBNameLsa.addName("/RouterB/name1");
+  routerBNameLsa.addName("/RouterB/name2");
+  routerBNameLsa.addName("/RouterB/name3");
+  lsdb.installNameLsa(routerBNameLsa);
+
+  ndn::Name thisRouter("/This/Router");
+  AdjacencyLsaPublisher adjacencyLsaPublisher(lsdb, *face, thisRouter, keyChain);
+  CoordinateLsaPublisher coordinateLsaPublisher(lsdb, *face, thisRouter, keyChain);
+  NameLsaPublisher nameLsaPublisher(lsdb, *face, thisRouter, keyChain);
+
+  LsdbStatusPublisher publisher(lsdb, *face, thisRouter, keyChain,
+                                adjacencyLsaPublisher,
+                                coordinateLsaPublisher,
+                                nameLsaPublisher);
+
+  publisher.publish();
+  face->processEvents(ndn::time::milliseconds(1));
+
+  BOOST_REQUIRE_EQUAL(face->sentDatas.size(), 1);
+
+  ndn::Block parser = face->sentDatas[0].getContent();
+  parser.parse();
+
+  ndn::Block::element_const_iterator it = parser.elements_begin();
+
+  BOOST_CHECK_EQUAL(it->type(), ndn::tlv::nlsr::LsdbStatus);
+
+  tlv::LsdbStatus lsdbStatusTlv;
+  BOOST_REQUIRE_NO_THROW(lsdbStatusTlv.wireDecode(*it));
+
+  BOOST_CHECK_EQUAL(lsdbStatusTlv.hasAdjacencyLsas(), true);
+
+  // Check adjacency LSAs
+  std::list<tlv::AdjacencyLsa>::const_iterator adjLsaIt = lsdbStatusTlv.getAdjacencyLsas().begin();
+  checkTlvAdjLsa(*adjLsaIt, routerAAdjLsa);
+
+  ++adjLsaIt;
+  checkTlvAdjLsa(*adjLsaIt, routerBAdjLsa);
+
+  // Check coordinate LSAs
+  std::list<tlv::CoordinateLsa>::const_iterator corLsaIt =
+    lsdbStatusTlv.getCoordinateLsas().begin();
+  checkTlvCoordinateLsa(*corLsaIt, routerACorLsa);
+
+  ++corLsaIt;
+  checkTlvCoordinateLsa(*corLsaIt, routerBCorLsa);
+
+  ++corLsaIt;
+  checkTlvCoordinateLsa(*corLsaIt, routerCCorLsa);
+
+  // Check Name LSAs
+  std::list<tlv::NameLsa>::const_iterator nameLsaIt = lsdbStatusTlv.getNameLsas().begin();
+  checkTlvNameLsa(*nameLsaIt, routerANameLsa);
+
+  ++nameLsaIt;
+  checkTlvNameLsa(*nameLsaIt, routerBNameLsa);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace test
+} // namespace nlsr
diff --git a/tests/publisher/test-segment-publisher.cpp b/tests/publisher/test-segment-publisher.cpp
new file mode 100644
index 0000000..8cc8dde
--- /dev/null
+++ b/tests/publisher/test-segment-publisher.cpp
@@ -0,0 +1,170 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014-2015,  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 NFD (Named Data Networking Forwarding Daemon).
+ * See AUTHORS.md for complete list of NFD authors and contributors.
+ *
+ * NFD is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ *
+ * NFD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE.  See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * NFD, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "publisher/segment-publisher.hpp"
+
+#include "../boost-test.hpp"
+
+#include <ndn-cxx/encoding/tlv.hpp>
+#include <ndn-cxx/util/dummy-client-face.hpp>
+
+#include <boost/mpl/int.hpp>
+#include <boost/mpl/vector.hpp>
+
+namespace nlsr {
+namespace tests {
+
+template<int64_t N=10000>
+class TestSegmentPublisher : public SegmentPublisher<ndn::util::DummyClientFace>
+{
+public:
+  TestSegmentPublisher(ndn::util::DummyClientFace& face,
+                       const ndn::Name& prefix,
+                       ndn::KeyChain& keyChain,
+                       const ndn::time::milliseconds freshnessPeriod)
+    : SegmentPublisher(face, prefix, keyChain, freshnessPeriod)
+    , m_totalPayloadLength(0)
+  {
+
+  }
+
+  virtual
+  ~TestSegmentPublisher()
+  {
+  }
+
+  uint16_t
+  getLimit() const
+  {
+    return N;
+  }
+
+  size_t
+  getTotalPayloadLength() const
+  {
+    return m_totalPayloadLength;
+  }
+
+protected:
+
+  virtual size_t
+  generate(ndn::EncodingBuffer& outBuffer)
+  {
+    size_t totalLength = 0;
+    for (int64_t i = 0; i < N; i++)
+      {
+        totalLength += ndn::prependNonNegativeIntegerBlock(outBuffer, ndn::tlv::Content, i);
+      }
+    m_totalPayloadLength += totalLength;
+    return totalLength;
+  }
+
+protected:
+  size_t m_totalPayloadLength;
+};
+
+template<int64_t N>
+class SegmentPublisherFixture
+{
+public:
+  SegmentPublisherFixture()
+    : m_face(ndn::util::makeDummyClientFace())
+    , m_expectedFreshnessPeriod(ndn::time::milliseconds(111))
+    , m_publisher(*m_face, "/localhost/nfd/SegmentPublisherFixture",
+                  m_keyChain, m_expectedFreshnessPeriod)
+  {
+  }
+
+  void
+  validate(const ndn::Data& data)
+  {
+    BOOST_CHECK_EQUAL(data.getFreshnessPeriod(), m_expectedFreshnessPeriod);
+
+    ndn::Block payload = data.getContent();
+
+    m_buffer.appendByteArray(payload.value(), payload.value_size());
+
+    // uint64_t segmentNo = data.getName()[-1].toSegment();
+    if (data.getFinalBlockId() != data.getName()[-1])
+      {
+        return;
+      }
+
+    // wrap data in a single Content TLV for easy parsing
+    m_buffer.prependVarNumber(m_buffer.size());
+    m_buffer.prependVarNumber(ndn::tlv::Content);
+
+    BOOST_TEST_CHECKPOINT("creating parser");
+    ndn::Block parser(m_buffer.buf(), m_buffer.size());
+    BOOST_TEST_CHECKPOINT("parsing aggregated response");
+    parser.parse();
+
+    BOOST_REQUIRE_EQUAL(parser.elements_size(), m_publisher.getLimit());
+
+    uint64_t expectedNo = m_publisher.getLimit() - 1;
+    for (ndn::Block::element_const_iterator i = parser.elements_begin();
+         i != parser.elements_end();
+         ++i)
+      {
+        uint64_t number = readNonNegativeInteger(*i);
+        BOOST_REQUIRE_EQUAL(number, expectedNo);
+        --expectedNo;
+      }
+  }
+
+protected:
+  ndn::shared_ptr<ndn::util::DummyClientFace> m_face;
+  const ndn::time::milliseconds m_expectedFreshnessPeriod;
+  TestSegmentPublisher<N> m_publisher;
+  ndn::EncodingBuffer m_buffer;
+  ndn::KeyChain m_keyChain;
+};
+
+using boost::mpl::int_;
+typedef boost::mpl::vector<int_<10000>, int_<100>, int_<10>, int_<0> > DatasetSizes;
+
+BOOST_AUTO_TEST_SUITE(PublisherTestSegmentPublisher)
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE(Generate, T, DatasetSizes, SegmentPublisherFixture<T::value>)
+{
+  this->m_publisher.publish();
+  this->m_face->processEvents();
+
+  size_t nSegments = this->m_publisher.getTotalPayloadLength() /
+                     this->m_publisher.getMaxSegmentSize();
+  if (this->m_publisher.getTotalPayloadLength() % this->m_publisher.getMaxSegmentSize() != 0 ||
+      nSegments == 0)
+    ++nSegments;
+
+  BOOST_CHECK_EQUAL(this->m_face->sentDatas.size(), nSegments);
+  for (const ndn::Data& data : this->m_face->sentDatas) {
+    this->validate(data);
+  }
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+} // namespace nlsr
diff --git a/tests/tlv/test-lsa-info.cpp b/tests/tlv/test-lsa-info.cpp
index dff4458..0e6d906 100644
--- a/tests/tlv/test-lsa-info.cpp
+++ b/tests/tlv/test-lsa-info.cpp
@@ -123,6 +123,19 @@
                               "ExpirationPeriod: 10000 milliseconds)");
 }
 
+BOOST_AUTO_TEST_CASE(LsaInfoMake)
+{
+  Lsa lsa;
+  lsa.setOrigRouter("/test/lsa/info/tlv");
+  lsa.setLsSeqNo(128);
+  lsa.setExpirationTimePoint(ndn::time::system_clock::now());
+
+  std::shared_ptr<LsaInfo> lsaInfo = makeLsaInfo(lsa);
+  BOOST_CHECK_EQUAL(lsaInfo->getOriginRouter(), lsa.getOrigRouter());
+  BOOST_CHECK_EQUAL(lsaInfo->getSequenceNumber(), lsa.getLsSeqNo());
+  BOOST_CHECK_LE(lsaInfo->getExpirationPeriod(), ndn::time::milliseconds(0));
+}
+
 BOOST_AUTO_TEST_SUITE_END()
 
 } // namespace test
diff --git a/tests/wscript b/tests/wscript
index 4ef346f..9d8973b 100644
--- a/tests/wscript
+++ b/tests/wscript
@@ -28,7 +28,7 @@
             target='unit-tests-main',
             name='unit-tests-main',
             features='cxx',
-            source=bld.path.ant_glob(['*.cpp', 'utility/*.cpp', 'tlv/*.cpp']),
+            source=bld.path.ant_glob(['*.cpp', 'utility/*.cpp', 'tlv/*.cpp', 'publisher/*.cpp']),
             use='nlsr-objects',
           )