chunks: segmented file transfer

refs #3071

Change-Id: I88e4fc1a8e33a0d61a95e2291cccc7b998647489
diff --git a/tests/chunks/consumer.t.cpp b/tests/chunks/consumer.t.cpp
new file mode 100644
index 0000000..0ce7290
--- /dev/null
+++ b/tests/chunks/consumer.t.cpp
@@ -0,0 +1,142 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2016,  Regents of the University of California,
+ *                      Colorado State University,
+ *                      University Pierre & Marie Curie, Sorbonne University.
+ *
+ * This file is part of ndn-tools (Named Data Networking Essential Tools).
+ * See AUTHORS.md for complete list of ndn-tools authors and contributors.
+ *
+ * ndn-tools 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.
+ *
+ * ndn-tools 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
+ * ndn-tools, 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 Andrea Tosatto
+ */
+
+#include "tools/chunks/catchunks/consumer.hpp"
+
+#include "tests/test-common.hpp"
+#include <ndn-cxx/util/dummy-client-face.hpp>
+#include <ndn-cxx/security/validator-null.hpp>
+
+#include <boost/test/output_test_stream.hpp>
+
+namespace ndn {
+namespace chunks {
+namespace tests {
+
+using namespace ndn::tests;
+using boost::test_tools::output_test_stream;
+
+BOOST_AUTO_TEST_SUITE(Chunks)
+BOOST_AUTO_TEST_SUITE(TestConsumer)
+
+BOOST_AUTO_TEST_CASE(OutputDataSequential)
+{
+  // Test sequential segments in the right order
+  // Segment order: 0 1 2
+
+  std::string name("/ndn/chunks/test");
+
+  std::vector<std::string> testStrings {
+      "",
+
+      "a1b2c3%^&(#$&%^$$/><",
+
+      "123456789123456789123456789123456789123456789123456789123456789"
+      "123456789123456789123456789123456789123456789123456789123456789",
+
+      "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. "
+      "Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur "
+      "ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla "
+      "consequat massa Donec pede justo,"
+  };
+
+  util::DummyClientFace face;
+  ValidatorNull validator;
+  output_test_stream output("");
+  Consumer cons(face, validator, false, output);
+
+  auto interest = makeInterest(name);
+
+  for (size_t i = 0; i < testStrings.size(); ++i) {
+    output.flush();
+
+    auto data = makeData(Name(name).appendVersion(1).appendSegment(i));
+    data->setContent(reinterpret_cast<const uint8_t*>(testStrings[i].data()),
+                     testStrings[i].size());
+
+    cons.m_bufferedData[i] = data;
+    cons.writeInOrderData();
+
+    BOOST_CHECK(output.is_equal(testStrings[i]));
+  }
+}
+
+BOOST_AUTO_TEST_CASE(OutputDataUnordered)
+{
+  // Test unordered segments
+  // Segment order: 1 0 2
+
+  std::string name("/ndn/chunks/test");
+
+  std::vector<std::string> testStrings {
+      "a1b2c3%^&(#$&%^$$/><",
+
+      "123456789123456789123456789123456789123456789123456789123456789"
+      "123456789123456789123456789123456789123456789123456789123456789",
+
+      "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. "
+      "Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur "
+      "ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla "
+      "consequat massa Donec pede justo,"
+  };;
+
+  util::DummyClientFace face;
+  ValidatorNull validator;
+  output_test_stream output("");
+  Consumer cons(face, validator, false, output);
+
+  auto interest = makeInterest(name);
+  std::vector<shared_ptr<Data>> dataStore;
+
+  for (size_t i = 0; i < testStrings.size(); ++i) {
+    auto data = makeData(Name(name).appendVersion(1).appendSegment(i));
+    data->setContent(reinterpret_cast<const uint8_t*>(testStrings[i].data()),
+                     testStrings[i].size());
+
+    dataStore.push_back(data);
+  }
+
+  output.flush();
+  cons.m_bufferedData[1] = dataStore[1];
+  cons.writeInOrderData();
+  BOOST_CHECK(output.is_equal(""));
+
+  output.flush();
+  cons.m_bufferedData[0] = dataStore[0];
+  cons.writeInOrderData();
+  BOOST_CHECK(output.is_equal(testStrings[0] + testStrings[1]));
+
+  output.flush();
+  cons.m_bufferedData[2] = dataStore[2];
+  cons.writeInOrderData();
+  BOOST_CHECK(output.is_equal(testStrings[2]));
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestConsumer
+BOOST_AUTO_TEST_SUITE_END() // Chunks
+
+} // namespace tests
+} // namespace chunks
+} // namespace ndn
diff --git a/tests/chunks/discover-version-fixed.t.cpp b/tests/chunks/discover-version-fixed.t.cpp
new file mode 100644
index 0000000..a57e5c8
--- /dev/null
+++ b/tests/chunks/discover-version-fixed.t.cpp
@@ -0,0 +1,147 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2016,  Regents of the University of California,
+ *                      Colorado State University,
+ *                      University Pierre & Marie Curie, Sorbonne University.
+ *
+ * This file is part of ndn-tools (Named Data Networking Essential Tools).
+ * See AUTHORS.md for complete list of ndn-tools authors and contributors.
+ *
+ * ndn-tools 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.
+ *
+ * ndn-tools 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
+ * ndn-tools, 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 Andrea Tosatto
+ */
+
+#include "tools/chunks/catchunks/discover-version-fixed.hpp"
+
+#include "discover-version-fixture.hpp"
+
+namespace ndn {
+namespace chunks {
+namespace tests {
+
+using namespace ndn::tests;
+
+class DiscoverVersionFixedFixture : public DiscoverVersionFixture
+{
+public:
+  DiscoverVersionFixedFixture()
+    : DiscoverVersionFixture(makeOptions())
+    , version(1449227841747)
+  {
+    setDiscover(make_unique<DiscoverVersionFixed>(Name(name).appendVersion(version),
+                                                  face, makeOptions()));
+  }
+
+protected:
+  uint64_t version; //Version to find
+};
+
+BOOST_AUTO_TEST_SUITE(Chunks)
+BOOST_AUTO_TEST_SUITE(TestDiscoverVersionFixed)
+
+BOOST_FIXTURE_TEST_CASE(RequestedVersionAvailable, DiscoverVersionFixedFixture)
+{
+  discover->run();
+  advanceClocks(io, time::nanoseconds(1), 1);
+  BOOST_REQUIRE_EQUAL(face.sentInterests.size(), 1);
+
+  face.receive(*makeDataWithVersion(version));
+
+  advanceClocks(io, time::nanoseconds(1), 1);
+
+  BOOST_CHECK_EQUAL(isDiscoveryFinished, true);
+  BOOST_CHECK_EQUAL(discoveredVersion, version);
+
+  BOOST_REQUIRE_EQUAL(face.sentInterests.size(), 1);
+  auto lastInterest = face.sentInterests.back();
+  BOOST_CHECK_EQUAL(lastInterest.getExclude().size(), 0);
+  BOOST_CHECK_EQUAL(lastInterest.getMaxSuffixComponents(), 2);
+  BOOST_CHECK_EQUAL(lastInterest.getMinSuffixComponents(), 2);
+  BOOST_CHECK_EQUAL(lastInterest.getMustBeFresh(), mustBeFresh);
+}
+
+BOOST_FIXTURE_TEST_CASE(NoVersionsAvailable, DiscoverVersionFixedFixture)
+{
+  discover->run();
+  advanceClocks(io, time::nanoseconds(1), 1);
+  BOOST_REQUIRE_EQUAL(face.sentInterests.size(), 1);
+
+  for (int retries = 0; retries < maxRetriesOnTimeoutOrNack; ++retries) {
+    advanceClocks(io, interestLifetime, 1);
+    BOOST_CHECK_EQUAL(isDiscoveryFinished, false);
+    BOOST_REQUIRE_EQUAL(face.sentInterests.size(), 2 + retries);
+  }
+
+  for (const auto& lastInterest : face.sentInterests) {
+    BOOST_CHECK_EQUAL(lastInterest.getExclude().size(), 0);
+    BOOST_CHECK_EQUAL(lastInterest.getMaxSuffixComponents(), 2);
+    BOOST_CHECK_EQUAL(lastInterest.getMinSuffixComponents(), 2);
+    BOOST_CHECK_EQUAL(lastInterest.getMustBeFresh(), mustBeFresh);
+    BOOST_CHECK_EQUAL(lastInterest.getName().equals(Name(name).appendVersion(version)), true);
+  }
+
+  advanceClocks(io, interestLifetime, 1);
+  BOOST_CHECK_EQUAL(isDiscoveryFinished, true);
+  // check if discovered version is the default value
+  BOOST_CHECK_EQUAL(discoveredVersion, 0);
+  BOOST_CHECK_EQUAL(face.sentInterests.size(), maxRetriesOnTimeoutOrNack + 1);
+}
+
+BOOST_FIXTURE_TEST_CASE(DataNotSegment, DiscoverVersionFixedFixture)
+{
+  discover->run();
+  advanceClocks(io, time::nanoseconds(1), 1);
+  BOOST_REQUIRE_EQUAL(face.sentInterests.size(), 1);
+
+  std::vector<std::string> randomStrings {
+      "",
+      "abcdefg",
+      "12345",
+      "qr%67a3%4e"
+  };
+
+  Exclude expectedExclude;
+  for (size_t retries = 0; retries < randomStrings.size(); ++retries) {
+    auto data = make_shared<Data>(Name(name).appendVersion(version).append(randomStrings[retries]));
+    data->setFinalBlockId(name::Component::fromSegment(0));
+    data = signData(data);
+
+    face.receive(*data);
+    advanceClocks(io, time::nanoseconds(1), 1);
+
+    BOOST_CHECK_EQUAL(isDiscoveryFinished, false);
+
+    BOOST_REQUIRE_EQUAL(face.sentInterests.size(), 1 + retries);
+    auto lastInterest = face.sentInterests.back();
+    if (randomStrings[retries] != "")
+      expectedExclude.excludeOne(name::Component::fromEscapedString(randomStrings[retries]));
+    BOOST_CHECK_EQUAL(lastInterest.getExclude(), expectedExclude);
+    BOOST_CHECK_EQUAL(lastInterest.getMaxSuffixComponents(), 2);
+    BOOST_CHECK_EQUAL(lastInterest.getMinSuffixComponents(), 2);
+    BOOST_CHECK_EQUAL(lastInterest.getMustBeFresh(), mustBeFresh);
+  }
+
+  advanceClocks(io, interestLifetime, maxRetriesOnTimeoutOrNack + 1);
+  BOOST_CHECK_EQUAL(isDiscoveryFinished, true);
+  // check if discovered version is the default value
+  BOOST_CHECK_EQUAL(discoveredVersion, 0);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestDiscoverVersionFixed
+BOOST_AUTO_TEST_SUITE_END() // Chunks
+
+} // namespace tests
+} // namespace chunks
+} // namespace ndn
diff --git a/tests/chunks/discover-version-fixture.hpp b/tests/chunks/discover-version-fixture.hpp
new file mode 100644
index 0000000..9009301
--- /dev/null
+++ b/tests/chunks/discover-version-fixture.hpp
@@ -0,0 +1,104 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2016,  Regents of the University of California,
+ *                      Colorado State University,
+ *                      University Pierre & Marie Curie, Sorbonne University.
+ *
+ * This file is part of ndn-tools (Named Data Networking Essential Tools).
+ * See AUTHORS.md for complete list of ndn-tools authors and contributors.
+ *
+ * ndn-tools 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.
+ *
+ * ndn-tools 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
+ * ndn-tools, 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 Andrea Tosatto
+ */
+
+#ifndef NDN_TOOLS_TESTS_CHUNKS_DISCOVER_VERSION_FIXTURE_HPP
+#define NDN_TOOLS_TESTS_CHUNKS_DISCOVER_VERSION_FIXTURE_HPP
+
+#include "tools/chunks/catchunks/discover-version.hpp"
+
+#include "tests/test-common.hpp"
+#include <ndn-cxx/util/dummy-client-face.hpp>
+
+namespace ndn {
+namespace chunks {
+namespace tests {
+
+class DiscoverVersionFixture : public ndn::tests::UnitTestTimeFixture, virtual protected Options
+{
+public:
+  DiscoverVersionFixture(const Options& options)
+    : Options(options)
+    , face(io)
+    , name("/ndn/chunks/test")
+    , isDiscoveryFinished(false)
+    , discoveredVersion(0)
+  {
+  }
+
+protected:
+  void setDiscover(unique_ptr<DiscoverVersion> disc)
+  {
+    discover = std::move(disc);
+    discover->onDiscoverySuccess.connect(bind(&DiscoverVersionFixture::onSuccess, this, _1));
+    discover->onDiscoveryFailure.connect(bind(&DiscoverVersionFixture::onFailure, this, _1));
+  }
+
+  shared_ptr<Data>
+  makeDataWithVersion(uint64_t version)
+  {
+    auto data = make_shared<Data>(Name(name).appendVersion(version).appendSegment(0));
+    data->setFinalBlockId(name::Component::fromSegment(0));
+    return ndn::tests::signData(data);
+  }
+
+  static Options
+  makeOptions()
+  {
+    Options options;
+    options.isVerbose = false;
+    options.interestLifetime = time::seconds(1);
+    options.maxRetriesOnTimeoutOrNack = 3;
+    return options;
+  }
+
+  virtual void
+  onSuccess(const Data& data)
+  {
+    isDiscoveryFinished = true;
+
+    if (data.getName()[name.size()].isVersion())
+      discoveredVersion = data.getName()[name.size()].toVersion();
+  }
+
+  virtual void
+  onFailure(const std::string& reason)
+  {
+    isDiscoveryFinished = true;
+  }
+
+protected:
+  boost::asio::io_service io;
+  util::DummyClientFace face;
+  Name name;
+  unique_ptr<DiscoverVersion> discover;
+  bool isDiscoveryFinished;
+  uint64_t discoveredVersion;
+};
+
+} // namespace tests
+} // namespace chunks
+} // namespace ndn
+
+#endif // NDN_TOOLS_TESTS_CHUNKS_DISCOVER_VERSION_FIXTURE_HPP
diff --git a/tests/chunks/discover-version-iterative.t.cpp b/tests/chunks/discover-version-iterative.t.cpp
new file mode 100644
index 0000000..233f36c
--- /dev/null
+++ b/tests/chunks/discover-version-iterative.t.cpp
@@ -0,0 +1,235 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2016,  Regents of the University of California,
+ *                      Colorado State University,
+ *                      University Pierre & Marie Curie, Sorbonne University.
+ *
+ * This file is part of ndn-tools (Named Data Networking Essential Tools).
+ * See AUTHORS.md for complete list of ndn-tools authors and contributors.
+ *
+ * ndn-tools 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.
+ *
+ * ndn-tools 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
+ * ndn-tools, 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 Andrea Tosatto
+ */
+
+#include "tools/chunks/catchunks/discover-version-iterative.hpp"
+
+#include "discover-version-fixture.hpp"
+
+namespace ndn {
+namespace chunks {
+namespace tests {
+
+using namespace ndn::tests;
+
+class DiscoverVersionIterativeFixture : public DiscoverVersionFixture,
+                                        protected DiscoverVersionIterativeOptions
+{
+public:
+  typedef DiscoverVersionIterativeOptions Options;
+
+public:
+  explicit
+  DiscoverVersionIterativeFixture(const Options& opt = makeOptionsIterative())
+    : chunks::Options(opt)
+    , DiscoverVersionFixture(opt)
+    , Options(opt)
+  {
+    setDiscover(make_unique<DiscoverVersionIterative>(Name(name), face, opt));
+  }
+
+protected:
+  static Options
+  makeOptionsIterative()
+  {
+    Options options;
+    options.isVerbose = false;
+    options.maxRetriesOnTimeoutOrNack = 3;
+    options.maxRetriesAfterVersionFound = 1;
+    return options;
+  }
+};
+
+
+BOOST_AUTO_TEST_SUITE(Chunks)
+BOOST_AUTO_TEST_SUITE(TestDiscoverVersionIterative)
+
+BOOST_FIXTURE_TEST_CASE(SingleVersionAvailable, DiscoverVersionIterativeFixture)
+{
+  discover->run();
+  advanceClocks(io, time::nanoseconds(1), 1);
+
+  uint64_t version = 1449241767037;
+
+  BOOST_REQUIRE_EQUAL(face.sentInterests.size(), 1);
+  auto lastInterest = face.sentInterests.back();
+  BOOST_CHECK_EQUAL(lastInterest.getExclude().size(), 0);
+  BOOST_CHECK_EQUAL(lastInterest.getChildSelector(), 1);
+  BOOST_CHECK_EQUAL(lastInterest.getMustBeFresh(), mustBeFresh);
+  BOOST_CHECK_EQUAL(lastInterest.getName().equals(name), true);
+
+  // Send first segment for the right version
+  face.receive(*makeDataWithVersion(version));
+  advanceClocks(io, time::nanoseconds(1), 1);
+
+  BOOST_REQUIRE_EQUAL(face.sentInterests.size(), 2);
+  lastInterest = face.sentInterests.back();
+  Exclude expectedExclude;
+  expectedExclude.excludeBefore(name::Component::fromVersion(version));
+  BOOST_CHECK_EQUAL(lastInterest.getExclude(), expectedExclude);
+  BOOST_CHECK_EQUAL(lastInterest.getChildSelector(), 1);
+  BOOST_CHECK_EQUAL(lastInterest.getMustBeFresh(), mustBeFresh);
+  BOOST_CHECK_EQUAL(lastInterest.getName().equals(name), true);
+
+  // Generate the timeout
+  for (int retries = 0; retries < maxRetriesAfterVersionFound; ++retries) {
+    advanceClocks(io, interestLifetime, 1);
+
+    BOOST_CHECK_EQUAL(isDiscoveryFinished, false);
+
+    BOOST_REQUIRE_EQUAL(face.sentInterests.size(), retries + 3);
+    lastInterest = face.sentInterests.back();
+    Exclude expectedExclude;
+    expectedExclude.excludeBefore(name::Component::fromVersion(version));
+    BOOST_CHECK_EQUAL(lastInterest.getExclude(), expectedExclude);
+    BOOST_CHECK_EQUAL(lastInterest.getChildSelector(), 1);
+    BOOST_CHECK_EQUAL(lastInterest.getMustBeFresh(), mustBeFresh);
+    BOOST_CHECK_EQUAL(lastInterest.getName().equals(name), true);
+  }
+
+  advanceClocks(io, interestLifetime, 1);
+  BOOST_CHECK_EQUAL(isDiscoveryFinished, true);
+  BOOST_CHECK_EQUAL(discoveredVersion, version);
+  BOOST_CHECK_EQUAL(face.sentInterests.size(), maxRetriesAfterVersionFound + 2);
+}
+
+
+BOOST_FIXTURE_TEST_CASE(NoVersionsAvailable, DiscoverVersionIterativeFixture)
+{
+  discover->run();
+  advanceClocks(io, time::nanoseconds(1), 1);
+  BOOST_REQUIRE_EQUAL(face.sentInterests.size(), 1);
+
+  for (int retries = 0; retries < maxRetriesOnTimeoutOrNack; ++retries) {
+    advanceClocks(io, interestLifetime, 1);
+    BOOST_CHECK_EQUAL(isDiscoveryFinished, false);
+    BOOST_REQUIRE_EQUAL(face.sentInterests.size(), 2 + retries);
+  }
+
+  for (auto& lastInterest : face.sentInterests) {
+    BOOST_CHECK_EQUAL(lastInterest.getExclude().size(), 0);
+    BOOST_CHECK_EQUAL(lastInterest.getChildSelector(), 1);
+    BOOST_CHECK_EQUAL(lastInterest.getMustBeFresh(), mustBeFresh);
+    BOOST_CHECK_EQUAL(lastInterest.getName().equals(name), true);
+  }
+
+  advanceClocks(io, interestLifetime, 1);
+  BOOST_CHECK_EQUAL(isDiscoveryFinished, true);
+  BOOST_CHECK_EQUAL(face.sentInterests.size(), maxRetriesOnTimeoutOrNack + 1);
+}
+
+BOOST_FIXTURE_TEST_CASE(MultipleVersionsAvailable, DiscoverVersionIterativeFixture)
+{
+  // nVersions must be positive
+  const uint64_t nVersions = 5;
+
+  discover->run();
+  advanceClocks(io, time::nanoseconds(1), 1);
+
+  BOOST_REQUIRE_EQUAL(face.sentInterests.size(), 1);
+  auto lastInterest = face.sentInterests.back();
+  BOOST_CHECK_EQUAL(lastInterest.getExclude().size(), 0);
+  BOOST_CHECK_EQUAL(lastInterest.getChildSelector(), 1);
+  BOOST_CHECK_EQUAL(lastInterest.getMustBeFresh(), mustBeFresh);
+  BOOST_CHECK_EQUAL(lastInterest.getName().equals(name), true);
+
+  for (uint64_t nSentVersions = 0; nSentVersions < nVersions; ++nSentVersions) {
+    face.receive(*makeDataWithVersion(nSentVersions));
+    advanceClocks(io, time::nanoseconds(1), 1);
+
+    BOOST_REQUIRE_EQUAL(face.sentInterests.size(), 2 + nSentVersions);
+    lastInterest = face.sentInterests.back();
+    Exclude expectedExclude;
+    expectedExclude.excludeBefore(name::Component::fromVersion(nSentVersions));
+    BOOST_CHECK_EQUAL(lastInterest.getExclude(), expectedExclude);
+    BOOST_CHECK_EQUAL(lastInterest.getChildSelector(), 1);
+    BOOST_CHECK_EQUAL(lastInterest.getMustBeFresh(), mustBeFresh);
+    BOOST_CHECK_EQUAL(lastInterest.getName().equals(name), true);
+  }
+
+  for (int retries = 0; retries < maxRetriesAfterVersionFound; ++retries) {
+    advanceClocks(io, interestLifetime, 1);
+
+    BOOST_CHECK_EQUAL(isDiscoveryFinished, false);
+
+    BOOST_REQUIRE_EQUAL(face.sentInterests.size(), 2 + retries + nVersions);
+    lastInterest = face.sentInterests.back();
+    Exclude expectedExclude;
+    expectedExclude.excludeBefore(name::Component::fromVersion(nVersions - 1));
+    BOOST_CHECK_EQUAL(lastInterest.getExclude(), expectedExclude);
+    BOOST_CHECK_EQUAL(lastInterest.getChildSelector(), 1);
+    BOOST_CHECK_EQUAL(lastInterest.getMustBeFresh(), mustBeFresh);
+    BOOST_CHECK_EQUAL(lastInterest.getName().equals(name), true);
+  }
+
+  advanceClocks(io, interestLifetime, 1);
+  BOOST_CHECK_EQUAL(isDiscoveryFinished, true);
+  BOOST_CHECK_EQUAL(discoveredVersion, nVersions - 1);
+  BOOST_CHECK_EQUAL(face.sentInterests.size(), nVersions + maxRetriesAfterVersionFound + 1);
+}
+
+BOOST_FIXTURE_TEST_CASE(MultipleVersionsAvailableDescendent, DiscoverVersionIterativeFixture)
+{
+  // nVersions must be positive
+  const uint64_t nVersions = 5;
+
+  discover->run();
+  advanceClocks(io, time::nanoseconds(1), 1);
+
+  BOOST_REQUIRE_EQUAL(face.sentInterests.size(), 1);
+  auto lastInterest = face.sentInterests.back();
+  BOOST_CHECK_EQUAL(isDiscoveryFinished, false);
+  BOOST_CHECK_EQUAL(lastInterest.getExclude().size(), 0);
+  BOOST_CHECK_EQUAL(lastInterest.getChildSelector(), 1);
+  BOOST_CHECK_EQUAL(lastInterest.getMustBeFresh(), mustBeFresh);
+  BOOST_CHECK_EQUAL(lastInterest.getName().equals(name), true);
+
+  for (uint64_t nVersionsToSend = nVersions; nVersionsToSend > 0; --nVersionsToSend) {
+    face.receive(*makeDataWithVersion(nVersionsToSend - 1));
+    advanceClocks(io, time::nanoseconds(1), 1);
+
+    BOOST_CHECK_EQUAL(isDiscoveryFinished, false);
+
+    BOOST_REQUIRE_EQUAL(face.sentInterests.size(), 2);
+    lastInterest = face.sentInterests.back();
+    Exclude expectedExclude;
+    expectedExclude.excludeBefore(name::Component::fromVersion(nVersions - 1));
+    BOOST_CHECK_EQUAL(lastInterest.getExclude(), expectedExclude);
+    BOOST_CHECK_EQUAL(lastInterest.getChildSelector(), 1);
+    BOOST_CHECK_EQUAL(lastInterest.getMustBeFresh(), mustBeFresh);
+    BOOST_CHECK_EQUAL(lastInterest.getName().equals(name), true);
+  }
+
+  advanceClocks(io, interestLifetime, maxRetriesAfterVersionFound + 1);
+  BOOST_CHECK_EQUAL(isDiscoveryFinished, true);
+  BOOST_CHECK_EQUAL(discoveredVersion, nVersions - 1);
+  BOOST_CHECK_EQUAL(face.sentInterests.size(), maxRetriesAfterVersionFound + 2);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestDiscoverVersionIterative
+BOOST_AUTO_TEST_SUITE_END() // Chunks
+
+} // namespace tests
+} // namespace chunks
+} // namespace ndn
diff --git a/tests/chunks/pipeline-interests.t.cpp b/tests/chunks/pipeline-interests.t.cpp
new file mode 100644
index 0000000..98abfa4
--- /dev/null
+++ b/tests/chunks/pipeline-interests.t.cpp
@@ -0,0 +1,378 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2016,  Regents of the University of California,
+ *                      Colorado State University,
+ *                      University Pierre & Marie Curie, Sorbonne University.
+ *
+ * This file is part of ndn-tools (Named Data Networking Essential Tools).
+ * See AUTHORS.md for complete list of ndn-tools authors and contributors.
+ *
+ * ndn-tools 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.
+ *
+ * ndn-tools 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
+ * ndn-tools, 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 Andrea Tosatto
+ */
+
+#include "tools/chunks/catchunks/pipeline-interests.hpp"
+#include "tools/chunks/catchunks/data-fetcher.hpp"
+
+#include "tests/test-common.hpp"
+#include <ndn-cxx/util/dummy-client-face.hpp>
+
+namespace ndn {
+namespace chunks {
+namespace tests {
+
+using namespace ndn::tests;
+
+class PipelineInterestsFixture : public UnitTestTimeFixture
+{
+public:
+  typedef PipelineInterestsOptions Options;
+
+public:
+  PipelineInterestsFixture()
+    : face(io)
+    , opt(makeOptions())
+    , name("/ndn/chunks/test")
+    , pipeline(face, opt)
+    , nDataSegments(0)
+    , nReceivedSegments(0)
+    , hasFailed(false)
+  {
+  }
+
+protected:
+  shared_ptr<Data>
+  makeDataWithSegment(uint64_t segmentNo, bool setFinalBlockId = true)
+  {
+    auto data = make_shared<Data>(Name(name).appendVersion(0).appendSegment(segmentNo));
+    if (setFinalBlockId)
+      data->setFinalBlockId(name::Component::fromSegment(nDataSegments - 1));
+    return signData(data);
+  }
+
+  void
+  runWithData(const Data& data)
+  {
+    pipeline.runWithExcludedSegment(data,
+                                    bind(&PipelineInterestsFixture::onData, this, _1, _2),
+                                    bind(&PipelineInterestsFixture::onFailure, this, _1));
+  }
+
+private:
+  void
+  onData(const Interest& interest, const Data& data)
+  {
+    nReceivedSegments++;
+  }
+
+  void
+  onFailure(const std::string& reason)
+  {
+    hasFailed = true;
+  }
+
+  static Options
+  makeOptions()
+  {
+    Options options;
+    options.isVerbose = false;
+    options.interestLifetime = time::seconds(1);
+    options.maxRetriesOnTimeoutOrNack = 3;
+    options.maxPipelineSize = 5;
+    return options;
+  }
+
+protected:
+  boost::asio::io_service io;
+  util::DummyClientFace face;
+  Options opt;
+  Name name;
+  PipelineInterests pipeline;
+  uint64_t nDataSegments;
+  uint64_t nReceivedSegments;
+  bool hasFailed;
+};
+
+BOOST_AUTO_TEST_SUITE(Chunks)
+BOOST_AUTO_TEST_SUITE(TestPipelineInterests)
+
+BOOST_FIXTURE_TEST_CASE(FewerSegmentsThanPipelineCapacity, PipelineInterestsFixture)
+{
+  nDataSegments = 3;
+  BOOST_ASSERT(nDataSegments <= opt.maxPipelineSize);
+
+  runWithData(*makeDataWithSegment(0));
+  advanceClocks(io, time::nanoseconds(1), 1);
+  BOOST_REQUIRE_EQUAL(face.sentInterests.size(), nDataSegments - 1);
+
+  for (uint64_t i = 0; i < nDataSegments - 1; ++i) {
+    face.receive(*makeDataWithSegment(i));
+    advanceClocks(io, time::nanoseconds(1), 1);
+
+    BOOST_CHECK_EQUAL(nReceivedSegments, i);
+    BOOST_REQUIRE_EQUAL(face.sentInterests.size(), nDataSegments - 1);
+    // check if the interest for the segment i+1 is well formed
+    auto sentInterest = face.sentInterests[i];
+    BOOST_CHECK_EQUAL(sentInterest.getExclude().size(), 0);
+    BOOST_CHECK_EQUAL(sentInterest.getMaxSuffixComponents(), 1);
+    BOOST_CHECK_EQUAL(sentInterest.getMustBeFresh(), opt.mustBeFresh);
+    BOOST_CHECK_EQUAL(Name(name).isPrefixOf(sentInterest.getName()), true);
+    BOOST_CHECK_EQUAL(sentInterest.getName()[-1].toSegment(), i + 1);
+  }
+
+  BOOST_CHECK_EQUAL(hasFailed, false);
+
+  advanceClocks(io, ndn::DEFAULT_INTEREST_LIFETIME, opt.maxRetriesOnTimeoutOrNack + 1);
+  BOOST_CHECK_EQUAL(hasFailed, true);
+}
+
+BOOST_FIXTURE_TEST_CASE(FullPipeline, PipelineInterestsFixture)
+{
+  nDataSegments = 13;
+  BOOST_ASSERT(nDataSegments > opt.maxPipelineSize);
+
+  runWithData(*makeDataWithSegment(nDataSegments - 1));
+  advanceClocks(io, time::nanoseconds(1), 1);
+  BOOST_REQUIRE_EQUAL(face.sentInterests.size(), opt.maxPipelineSize);
+
+  for (uint64_t i = 0; i < nDataSegments - 1; ++i) {
+    face.receive(*makeDataWithSegment(i));
+    advanceClocks(io, time::nanoseconds(1), 1);
+    BOOST_CHECK_EQUAL(nReceivedSegments, i + 1);
+
+    if (i < nDataSegments - opt.maxPipelineSize - 1) {
+      BOOST_REQUIRE_EQUAL(face.sentInterests.size(), opt.maxPipelineSize + i + 1);
+      // check if the interest for the segment i+1 is well formed
+      auto sentInterest = face.sentInterests[i];
+      BOOST_CHECK_EQUAL(sentInterest.getExclude().size(), 0);
+      BOOST_CHECK_EQUAL(sentInterest.getMaxSuffixComponents(), 1);
+      BOOST_CHECK_EQUAL(sentInterest.getMustBeFresh(), opt.mustBeFresh);
+      BOOST_CHECK_EQUAL(Name(name).isPrefixOf(sentInterest.getName()), true);
+      BOOST_CHECK_EQUAL(sentInterest.getName()[-1].toSegment(), i);
+    }
+    else {
+      // all the interests have been sent for all the segments
+      BOOST_CHECK_EQUAL(face.sentInterests.size(), nDataSegments - 1);
+    }
+  }
+
+  BOOST_CHECK_EQUAL(hasFailed, false);
+}
+
+BOOST_FIXTURE_TEST_CASE(TimeoutAllSegments, PipelineInterestsFixture)
+{
+  nDataSegments = 13;
+  BOOST_ASSERT(nDataSegments > opt.maxPipelineSize);
+
+  runWithData(*makeDataWithSegment(nDataSegments - 1));
+  advanceClocks(io, time::nanoseconds(1), 1);
+  BOOST_REQUIRE_EQUAL(face.sentInterests.size(), opt.maxPipelineSize);
+
+  for (int i = 0; i < opt.maxRetriesOnTimeoutOrNack; ++i) {
+    advanceClocks(io, opt.interestLifetime, 1);
+    BOOST_REQUIRE_EQUAL(face.sentInterests.size(), opt.maxPipelineSize * (i + 2));
+    BOOST_CHECK_EQUAL(nReceivedSegments, 0);
+
+    // A single retry for every pipeline element
+    for (size_t j = 0; j < opt.maxPipelineSize; ++j) {
+      auto interest = face.sentInterests[(opt.maxPipelineSize * (i + 1)) + j];
+      BOOST_CHECK_EQUAL(static_cast<size_t>(interest.getName()[-1].toSegment()), j);
+    }
+  }
+
+  advanceClocks(io, opt.interestLifetime, 1);
+  BOOST_CHECK_EQUAL(hasFailed, true);
+}
+
+BOOST_FIXTURE_TEST_CASE(TimeoutAfterFinalBlockIdReceived, PipelineInterestsFixture)
+{
+  // the FinalBlockId is sent with the first segment, after the first segment failure the pipeline
+  // should fail
+
+  nDataSegments = 18;
+  BOOST_ASSERT(nDataSegments > opt.maxPipelineSize);
+
+  runWithData(*makeDataWithSegment(nDataSegments - 1));
+  advanceClocks(io, time::nanoseconds(1), 1);
+  BOOST_REQUIRE_EQUAL(face.sentInterests.size(), opt.maxPipelineSize);
+
+  // send a single segment for each pipeline element but not the first element
+  advanceClocks(io, opt.interestLifetime, 1);
+  for (uint64_t i = 1; i < opt.maxPipelineSize; ++i) {
+    face.receive(*makeDataWithSegment(i));
+    advanceClocks(io, time::nanoseconds(1), 1);
+  }
+
+  // send a single data packet for each pipeline element
+  advanceClocks(io, opt.interestLifetime, opt.maxRetriesOnTimeoutOrNack - 1);
+  for (uint64_t i = 0; i < opt.maxPipelineSize - 1; ++i) {
+    face.receive(*makeDataWithSegment(opt.maxPipelineSize + i));
+    advanceClocks(io, time::nanoseconds(1), 1);
+  }
+  advanceClocks(io, opt.interestLifetime, 1);
+
+  size_t interestAfterFailure = face.sentInterests.size();
+
+  BOOST_CHECK_EQUAL(face.getNPendingInterests(), 0);
+  BOOST_CHECK_EQUAL(hasFailed, true);
+
+  // these new segments should not generate new interests
+  advanceClocks(io, opt.interestLifetime, 1);
+  for (uint64_t i = 0; i < opt.maxPipelineSize - 1; ++i) {
+    face.receive(*makeDataWithSegment(opt.maxPipelineSize * 2 + i - 1));
+    advanceClocks(io, time::nanoseconds(1), 1);
+  }
+
+  // no more interests after a failure
+  advanceClocks(io, opt.interestLifetime, opt.maxRetriesOnTimeoutOrNack);
+  BOOST_CHECK_EQUAL(interestAfterFailure, face.sentInterests.size());
+  BOOST_CHECK_EQUAL(face.getNPendingInterests(), 0);
+}
+
+BOOST_FIXTURE_TEST_CASE(TimeoutBeforeFinalBlockIdReceived, PipelineInterestsFixture)
+{
+  // the FinalBlockId is sent only with the last segment, all segments are sent except for the
+  // second one (segment #1); all segments are received correctly until the FinalBlockId is received
+
+  nDataSegments = 22;
+  BOOST_ASSERT(nDataSegments > opt.maxPipelineSize);
+
+  runWithData(*makeDataWithSegment(nDataSegments - 1, false));
+  advanceClocks(io, time::nanoseconds(1), 1);
+  BOOST_REQUIRE_EQUAL(face.sentInterests.size(), opt.maxPipelineSize);
+
+  advanceClocks(io, opt.interestLifetime, 1);
+  for (uint64_t i = 2; i < opt.maxPipelineSize; ++i) {
+    face.receive(*makeDataWithSegment(i, false));
+    advanceClocks(io, time::nanoseconds(1), 1);
+
+    auto lastInterest = face.sentInterests.back();
+    BOOST_CHECK_EQUAL(lastInterest.getName()[-1].toSegment(), opt.maxPipelineSize + i - 2);
+  }
+  BOOST_REQUIRE_EQUAL(face.sentInterests.size(), opt.maxPipelineSize * 3 - 2);
+
+  // nack for the first pipeline element (segment #0)
+  auto nack = make_shared<lp::Nack>(face.sentInterests[opt.maxPipelineSize]);
+  nack->setReason(lp::NackReason::DUPLICATE);
+  face.receive(*nack);
+
+  // all the pipeline elements are two retries near the timeout error, but not the
+  // second (segment #1) that is only one retry near the timeout
+  advanceClocks(io, opt.interestLifetime, opt.maxRetriesOnTimeoutOrNack - 1);
+  BOOST_CHECK_EQUAL(hasFailed, false);
+
+  // data for the first pipeline element (segment #0)
+  face.receive(*makeDataWithSegment(0, false));
+  BOOST_CHECK_EQUAL(hasFailed, false);
+
+  // data for all the pipeline element, but not the second (segment #1)
+  for (uint64_t i = opt.maxPipelineSize; i < nDataSegments - 1; ++i) {
+    if (i == nDataSegments - 2) {
+      face.receive(*makeDataWithSegment(i, true));
+    }
+    else {
+      face.receive(*makeDataWithSegment(i, false));
+    }
+    advanceClocks(io, time::nanoseconds(1), 1);
+  }
+  // timeout for the second pipeline element (segment #1), this should trigger an error
+  advanceClocks(io, opt.interestLifetime, 1);
+
+  BOOST_CHECK_EQUAL(nReceivedSegments, nDataSegments - 2);
+  BOOST_CHECK_EQUAL(hasFailed, true);
+}
+
+BOOST_FIXTURE_TEST_CASE(SegmentReceivedAfterTimeout, PipelineInterestsFixture)
+{
+  // the FinalBlockId is never sent, all the pipeline elements with a segment number greater than
+  // segment #0 will fail, after this failure also segment #0 fail and this should trigger an error
+
+  nDataSegments = 22;
+  BOOST_ASSERT(nDataSegments > opt.maxPipelineSize);
+
+  runWithData(*makeDataWithSegment(nDataSegments - 1, false));
+  advanceClocks(io, time::nanoseconds(1), 1);
+  BOOST_REQUIRE_EQUAL(face.sentInterests.size(), opt.maxPipelineSize);
+
+  advanceClocks(io, opt.interestLifetime, 1);
+
+  // nack for the first pipeline element (segment #0)
+  auto nack = make_shared<lp::Nack>(face.sentInterests[opt.maxPipelineSize]);
+  nack->setReason(lp::NackReason::DUPLICATE);
+  face.receive(*nack);
+  BOOST_CHECK_EQUAL(hasFailed, false);
+
+  // timeout for all the pipeline elements, but not the first (segment #0)
+  advanceClocks(io, opt.interestLifetime, opt.maxRetriesOnTimeoutOrNack);
+  BOOST_CHECK_EQUAL(hasFailed, false);
+
+  // data for the first pipeline element (segment #0), this should trigger an error because the
+  // others pipeline elements failed
+  face.receive(*makeDataWithSegment(0, false));
+  advanceClocks(io, time::nanoseconds(1), 1);
+
+  BOOST_CHECK_EQUAL(nReceivedSegments, 1);
+  BOOST_CHECK_EQUAL(hasFailed, true);
+}
+
+BOOST_FIXTURE_TEST_CASE(CongestionAllSegments, PipelineInterestsFixture)
+{
+  nDataSegments = 13;
+  BOOST_ASSERT(nDataSegments > opt.maxPipelineSize);
+
+  runWithData(*makeDataWithSegment(nDataSegments - 1));
+  advanceClocks(io, time::nanoseconds(1), 1);
+  BOOST_REQUIRE_EQUAL(face.sentInterests.size(), opt.maxPipelineSize);
+
+  // send nack for all the pipeline elements first interest
+  for (size_t j = 0; j < opt.maxPipelineSize; j++) {
+    auto nack = make_shared<lp::Nack>(face.sentInterests[j]);
+    nack->setReason(lp::NackReason::CONGESTION);
+    face.receive(*nack);
+    advanceClocks(io, time::nanoseconds(1), 1);
+  }
+
+  // send nack for all the pipeline elements interests after the first
+  for (int i = 1; i <= opt.maxRetriesOnTimeoutOrNack; ++i) {
+    time::milliseconds backoffTime(static_cast<uint64_t>(std::pow(2, i)));
+    if (backoffTime > DataFetcher::MAX_CONGESTION_BACKOFF_TIME)
+      backoffTime = DataFetcher::MAX_CONGESTION_BACKOFF_TIME;
+
+    advanceClocks(io, backoffTime, 1);
+    BOOST_REQUIRE_EQUAL(face.sentInterests.size(), opt.maxPipelineSize * (i +1));
+
+    // A single retry for every pipeline element
+    for (size_t j = 0; j < opt.maxPipelineSize; ++j) {
+      auto interest = face.sentInterests[(opt.maxPipelineSize * i) + j];
+      BOOST_CHECK_LT(static_cast<size_t>(interest.getName()[-1].toSegment()), opt.maxPipelineSize);
+    }
+
+    for (size_t j = 0; j < opt.maxPipelineSize; j++) {
+      auto nack = make_shared<lp::Nack>(face.sentInterests[(opt.maxPipelineSize * i) + j]);
+      nack->setReason(lp::NackReason::CONGESTION);
+      face.receive(*nack);
+      advanceClocks(io, time::nanoseconds(1), 1);
+    }
+  }
+
+  BOOST_CHECK_EQUAL(hasFailed, true);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestPipelineInterests
+BOOST_AUTO_TEST_SUITE_END() // Chunks
+
+} // namespace tests
+} // namespace chunks
+} // namespace ndn
diff --git a/tests/chunks/producer.t.cpp b/tests/chunks/producer.t.cpp
new file mode 100644
index 0000000..dc5346f
--- /dev/null
+++ b/tests/chunks/producer.t.cpp
@@ -0,0 +1,233 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2016,  Regents of the University of California,
+ *                      Colorado State University,
+ *                      University Pierre & Marie Curie, Sorbonne University.
+ *
+ * This file is part of ndn-tools (Named Data Networking Essential Tools).
+ * See AUTHORS.md for complete list of ndn-tools authors and contributors.
+ *
+ * ndn-tools 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.
+ *
+ * ndn-tools 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
+ * ndn-tools, 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 Andrea Tosatto
+ */
+
+#include "tools/chunks/putchunks/producer.hpp"
+
+#include "tests/test-common.hpp"
+#include <ndn-cxx/util/dummy-client-face.hpp>
+#include <ndn-cxx/security/validator-null.hpp>
+
+#include <cmath>
+
+namespace ndn {
+namespace chunks {
+namespace tests {
+
+using namespace ndn::tests;
+
+BOOST_AUTO_TEST_SUITE(Chunks)
+BOOST_AUTO_TEST_SUITE(TestProducer)
+
+BOOST_AUTO_TEST_CASE(InputData)
+{
+  util::DummyClientFace face;
+  KeyChain keyChain;
+  security::SigningInfo signingInfo;
+  Name prefix("/ndn/chunks/test");
+  int maxSegmentSize = 40;
+  std::vector<std::string> testStrings {
+      "",
+
+      "a1b2c3%^&(#$&%^$$/><",
+
+      "123456789123456789123456789123456789123456789123456789123456789"
+      "123456789123456789123456789123456789123456789123456789123456789",
+
+      "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. "
+      "Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur "
+      "ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla "
+      "consequat massa Donec pede justo,"
+  };
+
+  for (size_t i = 0; i <  testStrings.size(); ++i) {
+    std::istringstream str(testStrings[i]);
+    Producer prod(prefix, face, keyChain, signingInfo, time::seconds(4), maxSegmentSize, false,
+                  false, str);
+
+    size_t expectedSize = std::ceil(static_cast<double>(testStrings[i].size()) / maxSegmentSize);
+    if (testStrings[i].size() == 0)
+      expectedSize = 1;
+
+    BOOST_CHECK_EQUAL(prod.m_store.size(), expectedSize);
+  }
+}
+
+BOOST_AUTO_TEST_CASE(RequestSegmentUnspecifiedVersion)
+{
+  boost::asio::io_service io;
+  util::DummyClientFace face(io, {true, true});
+  KeyChain keyChain;
+  security::SigningInfo signingInfo;
+  Name prefix("/ndn/chunks/test");
+  time::milliseconds freshnessPeriod(time::seconds(10));
+  size_t maxSegmentSize(40);
+  std::istringstream testString(std::string(
+          "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget "
+          "dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, "
+          "nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, "
+          "sem. Nulla consequat massa Donec pede justo,"));
+
+  Name keyLocatorName = keyChain.getDefaultCertificateName().getPrefix(-1);
+
+  Producer producer(prefix, face, keyChain, signingInfo, freshnessPeriod, maxSegmentSize,
+                    false, false, testString);
+  io.poll();
+
+  size_t nSegments = std::ceil(static_cast<double>(testString.str().size()) / maxSegmentSize);
+
+  // version request
+  face.receive(*makeInterest(prefix));
+  face.processEvents();
+
+  BOOST_REQUIRE_EQUAL(face.sentData.size(), 1);
+  auto lastData = face.sentData.back();
+  BOOST_REQUIRE_EQUAL(lastData.getName().size(), prefix.size() + 2);
+  BOOST_CHECK_EQUAL(lastData.getName()[-1].toSegment(), 0);
+  BOOST_REQUIRE(!lastData.getFinalBlockId().empty());
+  BOOST_CHECK_EQUAL(lastData.getFinalBlockId().toSegment(), nSegments - 1);
+  BOOST_CHECK_EQUAL(lastData.getSignature().getKeyLocator().getName(), keyLocatorName);
+
+  // segment request
+  Name nameWithVersion(prefix);
+  nameWithVersion.append(lastData.getName()[-2]);
+  size_t requestSegmentNo = 1;
+
+  face.receive(*makeInterest(nameWithVersion.appendSegment(requestSegmentNo)));
+  face.processEvents();
+
+  BOOST_REQUIRE_EQUAL(face.sentData.size(), 2);
+  lastData = face.sentData.back();
+  BOOST_REQUIRE_EQUAL(lastData.getName().size(), prefix.size() + 2);
+  BOOST_CHECK_EQUAL(lastData.getName()[-1].toSegment(), requestSegmentNo);
+  BOOST_REQUIRE(!lastData.getFinalBlockId().empty());
+  BOOST_CHECK_EQUAL(lastData.getFinalBlockId().toSegment(), nSegments - 1);
+  BOOST_CHECK_EQUAL(lastData.getSignature().getKeyLocator().getName(), keyLocatorName);
+}
+
+BOOST_AUTO_TEST_CASE(RequestSegmentSpecifiedVersion)
+{
+  boost::asio::io_service io;
+  util::DummyClientFace face(io, {true, true});
+  KeyChain keyChain;
+  security::SigningInfo signingInfo;
+  Name prefix("/ndn/chunks/test");
+  time::milliseconds freshnessPeriod(time::seconds(10));
+  size_t maxSegmentSize(40);
+  std::istringstream testString(std::string(
+          "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget "
+          "dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, "
+          "nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, "
+          "sem. Nulla consequat massa Donec pede justo,"));
+
+  uint64_t version = 1449227841747;
+  Name keyLocatorName = keyChain.getDefaultCertificateName().getPrefix(-1);
+
+  Producer producer(prefix.appendVersion(version), face, keyChain, signingInfo, freshnessPeriod,
+                    maxSegmentSize, false, false, testString);
+  io.poll();
+
+  size_t nSegments = std::ceil(static_cast<double>(testString.str().size()) / maxSegmentSize);
+
+  // version request
+  face.receive(*makeInterest(prefix));
+  face.processEvents();
+
+  BOOST_REQUIRE_EQUAL(face.sentData.size(), 1);
+  auto lastData = face.sentData.back();
+  BOOST_REQUIRE_EQUAL(lastData.getName().size(), prefix.size() + 1);
+  BOOST_CHECK_EQUAL(lastData.getName()[-2].toVersion(), version);
+  BOOST_CHECK_EQUAL(lastData.getName()[-1].toSegment(), 0);
+  BOOST_REQUIRE(!lastData.getFinalBlockId().empty());
+  BOOST_CHECK_EQUAL(lastData.getFinalBlockId().toSegment(), nSegments - 1);
+  BOOST_CHECK_EQUAL(lastData.getSignature().getKeyLocator().getName(), keyLocatorName);
+
+  // segment request
+  Name nameWithVersion(prefix);
+  size_t requestSegmentNo = 1;
+
+  face.receive(*makeInterest(nameWithVersion.appendSegment(requestSegmentNo)));
+  face.processEvents();
+
+  BOOST_REQUIRE_EQUAL(face.sentData.size(), 2);
+  lastData = face.sentData.back();
+  BOOST_REQUIRE_EQUAL(lastData.getName().size(), prefix.size() + 1);
+  BOOST_CHECK_EQUAL(lastData.getName()[-2].toVersion(), version);
+  BOOST_CHECK_EQUAL(lastData.getName()[-1].toSegment(), requestSegmentNo);
+  BOOST_REQUIRE(!lastData.getFinalBlockId().empty());
+  BOOST_CHECK_EQUAL(lastData.getFinalBlockId().toSegment(), nSegments - 1);
+  BOOST_CHECK_EQUAL(lastData.getSignature().getKeyLocator().getName(), keyLocatorName);
+}
+
+BOOST_AUTO_TEST_CASE(RequestNotExistingSegment)
+{
+  boost::asio::io_service io;
+  util::DummyClientFace face(io, {true, true});
+  KeyChain keyChain;
+  security::SigningInfo signingInfo;
+  Name prefix("/ndn/chunks/test");
+  time::milliseconds freshnessPeriod(time::seconds(10));
+  size_t maxSegmentSize(40);
+  std::istringstream testString(std::string(
+          "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget "
+          "dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, "
+          "nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, "
+          "sem. Nulla consequat massa Donec pede justo,"));
+
+  Name keyLocatorName = keyChain.getDefaultCertificateName().getPrefix(-1);
+
+  Producer producer(prefix, face, keyChain, signingInfo, freshnessPeriod, maxSegmentSize,
+                    false, false, testString);
+  io.poll();
+
+  size_t nSegments = std::ceil(static_cast<double>(testString.str().size()) / maxSegmentSize);
+
+  // version request
+  face.receive(*makeInterest(prefix));
+  face.processEvents();
+
+  BOOST_REQUIRE_EQUAL(face.sentData.size(), 1);
+  auto lastData = face.sentData.back();
+  BOOST_REQUIRE_EQUAL(lastData.getName().size(), prefix.size() + 2);
+  BOOST_CHECK_EQUAL(lastData.getName()[-1].toSegment(), 0);
+  BOOST_REQUIRE(!lastData.getFinalBlockId().empty());
+  BOOST_CHECK_EQUAL(lastData.getFinalBlockId().toSegment(), nSegments - 1);
+  BOOST_CHECK_EQUAL(lastData.getSignature().getKeyLocator().getName(), keyLocatorName);
+
+  // segment request
+  Name nameWithVersion(prefix);
+  nameWithVersion.append(lastData.getName()[-2]);
+  face.receive(*makeInterest(nameWithVersion.appendSegment(nSegments)));
+  face.processEvents();
+
+  // no new data
+  BOOST_REQUIRE_EQUAL(face.sentData.size(), 1);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestProducer
+BOOST_AUTO_TEST_SUITE_END() // Chunks
+
+} // namespace tests
+} // namespace chunks
+} // namespace ndn