Create utility method to generate file manifests

Refs: #3431

Change-Id: I1a0f06b87689e71349b4fa9346ebc29e785d86cc
diff --git a/tests/unit-tests/file-manifest.t.cpp b/tests/unit-tests/file-manifest.t.cpp
index a8181ad..d6a3e68 100644
--- a/tests/unit-tests/file-manifest.t.cpp
+++ b/tests/unit-tests/file-manifest.t.cpp
@@ -25,11 +25,21 @@
 #include <vector>
 
 #include <ndn-cxx/data.hpp>
-#include <ndn-cxx/signature.hpp>
 #include <ndn-cxx/security/key-chain.hpp>
+#include <ndn-cxx/security/signing-helpers.hpp>
+#include <ndn-cxx/signature.hpp>
+
+#include <boost/filesystem.hpp>
+#include <boost/filesystem/fstream.hpp>
+#include <boost/lexical_cast.hpp>
+
+namespace fs = boost::filesystem;
 
 BOOST_TEST_DONT_PRINT_LOG_VALUE(std::nullptr_t)
 BOOST_TEST_DONT_PRINT_LOG_VALUE(std::vector<ndn::Name>)
+BOOST_TEST_DONT_PRINT_LOG_VALUE(std::vector<ndn::ntorrent::FileManifest>)
+BOOST_TEST_DONT_PRINT_LOG_VALUE(std::vector<uint8_t>)
+BOOST_TEST_DONT_PRINT_LOG_VALUE(std::vector<ndn::Data>::iterator)
 
 namespace ndn {
 namespace ntorrent {
@@ -289,6 +299,117 @@
   BOOST_CHECK_EQUAL(m1, m2);
 }
 
+BOOST_AUTO_TEST_CASE(CheckGenerateFileManifest)
+{
+  const size_t TEST_FILE_LEN = fs::file_size("tests/testdata/foo/bar.txt");
+  const struct {
+      size_t         d_dataPacketSize;
+      size_t         d_subManifestSize;
+      const char    *d_filePath;
+      const char    *d_catalogPrefix;
+      bool           d_getData;
+      bool           d_shouldThrow;
+  } DATA [] = {
+    // Affirmative tests
+    {1            , TEST_FILE_LEN, "tests/testdata/foo/bar.txt" , "/NTORRENT/foo/", true, false },
+    {10           , 10           , "tests/testdata/foo/bar.txt" , "/NTORRENT/foo/", true, false },
+    {10           , 1            , "tests/testdata/foo/bar.txt" , "/NTORRENT/foo/", true, false },
+    {1            , 10           , "tests/testdata/foo/bar.txt" , "/NTORRENT/foo/", true, false },
+    {1            , 1            , "tests/testdata/foo/bar.txt" , "/NTORRENT/foo/", true, false },
+    {1024         , 1            , "tests/testdata/foo/bar1.txt", "/NTORRENT/foo/", true, false },
+    {1024         , 100          , "tests/testdata/foo/bar1.txt", "/NTORRENT/foo/", true, false },
+    {TEST_FILE_LEN, 1            , "tests/testdata/foo/bar.txt" , "/NTORRENT/foo/", true, false },
+      // Negative tests
+      //   non-existent file
+    {128          , 128          , "tests/testdata/foo/fake.txt", "/NTORRENT/foo/", false, true },
+      // prefix mismatch
+    {128          , 128          , "tests/testdata/foo/bar.txt", "/NTORRENT/bar/",  false, true },
+    //  scaling test
+    // {10240         , 5120 ,         "tests/testdata/foo/huge_file", "/NTORRENT/foo/", false, false },
+      // assertion failures (tests not supported on platforms)
+    // {0            , 128          , "tests/testdata/foo/bar.txt", "/NTORRENT/bar/", true },
+    // {128          , 0            , "tests/testdata/foo/bar.txt", "/NTORRENT/bar/", true },
+  };
+  enum { NUM_DATA = sizeof DATA / sizeof *DATA };
+  for (int i = 0; i < NUM_DATA; ++i) {
+    auto dataPacketSize  = DATA[i].d_dataPacketSize;
+    auto subManifestSize = DATA[i].d_subManifestSize;
+    auto filePath        = DATA[i].d_filePath;
+    auto catalogPrefix   = DATA[i].d_catalogPrefix;
+    auto getData         = DATA[i].d_getData;
+    auto shouldThrow     = DATA[i].d_shouldThrow;
+
+    std::pair<std::vector<FileManifest>, std::vector<Data>> manifestsDataPair;
+    if (shouldThrow) {
+      BOOST_CHECK_THROW(FileManifest::generate(filePath,
+                                               catalogPrefix,
+                                               subManifestSize,
+                                               dataPacketSize,
+                                               getData),
+                          FileManifest::Error);
+    }
+    else {
+      manifestsDataPair = FileManifest::generate(filePath,
+                                                 catalogPrefix,
+                                                 subManifestSize,
+                                                 dataPacketSize,
+                                                 getData);
+      auto manifests = manifestsDataPair.first;
+      auto data = manifestsDataPair.second;
+      // Verify the basic attributes of the manifest
+      size_t file_length = 0;
+      for (auto it = manifests.begin(); it != manifests.end(); ++it) {
+        // Verify that each file manifest is signed.
+        BOOST_CHECK_NO_THROW(it->getFullName());
+        BOOST_CHECK_EQUAL(it->data_packet_size(), dataPacketSize);
+        BOOST_CHECK_EQUAL(it->catalog_prefix(), catalogPrefix);
+        if (it != manifests.end() -1) {
+          BOOST_CHECK_EQUAL(it->catalog().size(), subManifestSize);
+          BOOST_CHECK_EQUAL(*(it->submanifest_ptr()), (it+1)->getFullName());
+        }
+        else {
+          BOOST_CHECK_LE(it->catalog().size(), subManifestSize);
+          BOOST_CHECK_EQUAL(it->submanifest_ptr(), nullptr);
+          file_length += it->wireEncode().value_size();
+        }
+      }
+      // confirm the manifests catalogs includes exactly the data packets
+      if (getData) {
+        auto data_it = data.begin();
+        size_t total_manifest_length = 0;
+        for (auto& m : manifests) {
+          total_manifest_length += m.wireEncode().value_size();
+          for (auto& data_name : m.catalog()) {
+            BOOST_CHECK_EQUAL(data_name, data_it->getFullName());
+            data_it++;
+          }
+        }
+        BOOST_CHECK_EQUAL(data_it, data.end());
+        std::vector<uint8_t> data_bytes;
+        data_bytes.reserve(fs::file_size(filePath));
+        for (const auto& d : data) {
+          auto content = d.getContent();
+          data_bytes.insert(data_bytes.end(), content.value_begin(), content.value_end());
+        }
+        data_bytes.shrink_to_fit();
+        // load  the contents of the file from disk
+        fs::ifstream is(filePath, fs::ifstream::binary | fs::ifstream::in);
+        is >> std::noskipws;
+        std::istream_iterator<uint8_t> start(is), end;
+        std::vector<uint8_t> file_bytes;
+        file_bytes.reserve(fs::file_size(filePath));
+        file_bytes.insert(file_bytes.begin(), start, end);
+        file_bytes.shrink_to_fit();
+        BOOST_CHECK_EQUAL(file_bytes, data_bytes);
+      }
+      else {
+        BOOST_CHECK(data.empty());
+        BOOST_CHECK(data.capacity() == 0);
+      }
+    }
+  }
+}
+
 BOOST_AUTO_TEST_SUITE_END()
 
 } // namespace tests