o Bug fix in FileManifest::wireDecode
o Add I/O test for FileManifest

Change-Id: I7307605a71cc0cdc76585421f73346dccbca6a6a
diff --git a/src/file-manifest.cpp b/src/file-manifest.cpp
index 71ba3a6..40726e6 100644
--- a/src/file-manifest.cpp
+++ b/src/file-manifest.cpp
@@ -32,6 +32,8 @@
 #include <ndn-cxx/security/key-chain.hpp>
 #include <ndn-cxx/security/signing-helpers.hpp>
 
+#include <ndn-cxx/encoding/tlv.hpp>
+
 using std::vector;
 using std::streamsize;
 using boost::irange;
@@ -219,9 +221,6 @@
 template<ndn::encoding::Tag TAG>
 size_t
 FileManifest::encodeContent(ndn::EncodingImpl<TAG>& encoder) const {
-  // FileManifestName ::= NAME-TYPE TLV-LENGTH
-  //                  Name
-
   // ManifestContent ::= CONTENT-TYPE TLV-LENGTH
   //                 DataPacketName*
   //                 CatalogPrefix
@@ -234,7 +233,7 @@
   // CatalogPrefix ::= NAME-TYPE TLV-LENGTH
   //               Name
 
-  // DataPacketSize ::= CONTENT-TYPE TLV-LENGTH
+    // DataPacketSize ::= CONTENT-TYPE TLV-LENGTH
   //                nonNegativeInteger
 
   // FileManifestPtr ::= NAME-TYPE TLV-LENGTH
@@ -242,6 +241,7 @@
 
   size_t totalLength = 0;
 
+  // build suffix catalog
   vector<Name> suffixCatalog;
   suffixCatalog.reserve(m_catalog.size());
   for (auto name: m_catalog) {
@@ -355,16 +355,11 @@
   content.parse();
 
   auto element = content.elements_begin();
-
   if (content.elements_end() == element) {
     BOOST_THROW_EXCEPTION(Error("FileManifest with empty content"));
   }
-
-  element->parse();
-  Name name(*element);
-
-  // Submanifest ptr
-  if (!name.empty()) {
+  if (element->type() == tlv::Name) {
+    Name name(*element);
     m_submanifestPtr = std::make_shared<Name>(name);
     ++element;
   }
@@ -372,16 +367,14 @@
   // DataPacketSize
   m_dataPacketSize = readNonNegativeInteger(*element);
   ++element;
-
   // CatalogPrefix
   m_catalogPrefix = Name(*element);
-  element++;
-
+  ++element;
   // Catalog
   m_catalog.clear();
   for (; element != content.elements_end(); ++element) {
     element->parse();
-    name = m_catalogPrefix;
+    Name name = m_catalogPrefix;
     name.append(Name(*element));
     if (name == m_catalogPrefix) {
       BOOST_THROW_EXCEPTION(Error("Empty name included in a FileManifest"));
diff --git a/src/file-manifest.hpp b/src/file-manifest.hpp
index b3cf3ff..8c4680a 100644
--- a/src/file-manifest.hpp
+++ b/src/file-manifest.hpp
@@ -87,7 +87,7 @@
    */
 
   // CREATORS
-  FileManifest() = delete;
+  FileManifest() = default;
 
   ~FileManifest() = default;
   /// Destroy this object
diff --git a/tests/unit-tests/file-manifest.t.cpp b/tests/unit-tests/file-manifest.t.cpp
index d6a3e68..226ae2b 100644
--- a/tests/unit-tests/file-manifest.t.cpp
+++ b/tests/unit-tests/file-manifest.t.cpp
@@ -28,6 +28,7 @@
 #include <ndn-cxx/security/key-chain.hpp>
 #include <ndn-cxx/security/signing-helpers.hpp>
 #include <ndn-cxx/signature.hpp>
+#include <ndn-cxx/util/io.hpp>
 
 #include <boost/filesystem.hpp>
 #include <boost/filesystem/fstream.hpp>
@@ -38,6 +39,7 @@
 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<ndn::ntorrent::FileManifest>::iterator)
 BOOST_TEST_DONT_PRINT_LOG_VALUE(std::vector<uint8_t>)
 BOOST_TEST_DONT_PRINT_LOG_VALUE(std::vector<ndn::Data>::iterator)
 
@@ -166,8 +168,17 @@
 {
   // Construct two manifests with the same attributes, and check that they related equal
   {
-    FileManifest m1("/file0/1A2B3C4D", 256, "/foo/", vector<Name>({}), std::make_shared<Name>("/file0/1/5E6F7G8H"));
-    FileManifest m2("/file0/1A2B3C4D", 256, "/foo/", vector<Name>({}), std::make_shared<Name>("/file0/1/5E6F7G8H"));
+    FileManifest m1("/file0/1A2B3C4D",
+                    256,
+                    "/foo/",
+                    vector<Name>({}),
+                    std::make_shared<Name>("/file0/1/5E6F7G8H"));
+
+    FileManifest m2("/file0/1A2B3C4D",
+                    256,
+                    "/foo/",
+                    vector<Name>({}),
+                    std::make_shared<Name>("/file0/1/5E6F7G8H"));
 
     BOOST_CHECK_EQUAL(m1, m2);
     BOOST_CHECK(!(m1 != m2));
@@ -273,30 +284,42 @@
 BOOST_AUTO_TEST_CASE(CheckEncodeDecode)
 {
   // Construct new FileManifest from  wire encoding of another FileManifest
-  FileManifest m1("/file0/1A2B3C4D",
-                  256,
-                  "/foo/",
-                  {"/foo/0/ABC123",  "/foo/1/DEADBEFF", "/foo/2/CAFEBABE"},
-                  std::make_shared<Name>("/file0/1/5E6F7G8H"));
+  {
+    FileManifest m1("/file0/1A2B3C4D",
+                    256,
+                    "/foo/",
+                    {"/foo/0/ABC123",  "/foo/1/DEADBEFF", "/foo/2/CAFEBABE"},
+                    std::make_shared<Name>("/file0/1/5E6F7G8H"));
 
-  KeyChain keyChain;
-  m1.finalize();
-  keyChain.sign(m1);
-  BOOST_CHECK_EQUAL(m1, FileManifest(m1.wireEncode()));
+    KeyChain keyChain;
+    m1.finalize();
+    keyChain.sign(m1);
+    BOOST_CHECK_EQUAL(m1, FileManifest(m1.wireEncode()));
 
-  // Change value and be sure that wireEncoding still works
-  m1.remove("/foo/2/CAFEBABE");
-  m1.finalize();
-  keyChain.sign(m1);
-  BOOST_CHECK_EQUAL(m1, FileManifest(m1.wireEncode()));
+    // Change value and be sure that wireEncoding still works
+    m1.remove("/foo/2/CAFEBABE");
+    m1.finalize();
+    keyChain.sign(m1);
+    BOOST_CHECK_EQUAL(m1, FileManifest(m1.wireEncode()));
 
-  // Explicitly call wireEncode and ensure the value works
-  FileManifest m2 = m1;
-  m1.remove("/foo/0/ABC123");
-  keyChain.sign(m1);
-  m1.finalize();
-  m2.wireDecode(m1.wireEncode());
-  BOOST_CHECK_EQUAL(m1, m2);
+    // Explicitly call wireEncode and ensure the value works
+    FileManifest m2 = m1;
+    m1.remove("/foo/0/ABC123");
+    keyChain.sign(m1);
+    m1.finalize();
+    m2.wireDecode(m1.wireEncode());
+    BOOST_CHECK_EQUAL(m1, m2);
+  }
+  {
+    FileManifest m1("/file0/1A2B3C4D",
+                    256,
+                    "/foo/",
+                    {"/foo/0/ABC123",  "/foo/1/DEADBEFF", "/foo/2/CAFEBABE"});
+    KeyChain keyChain;
+    m1.finalize();
+    keyChain.sign(m1);
+    BOOST_CHECK_EQUAL(m1, FileManifest(m1.wireEncode()));
+  }
 }
 
 BOOST_AUTO_TEST_CASE(CheckGenerateFileManifest)
@@ -319,10 +342,10 @@
     {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
+    // Negative tests
+    //   non-existent file
     {128          , 128          , "tests/testdata/foo/fake.txt", "/NTORRENT/foo/", false, true },
-      // prefix mismatch
+    //   // 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 },
@@ -354,15 +377,22 @@
                                                  subManifestSize,
                                                  dataPacketSize,
                                                  getData);
+      auto fileSize = fs::file_size(filePath);
+      auto EXP_NUM_DATA_PACKETS   = fileSize / dataPacketSize + !!(fileSize % dataPacketSize);
+      auto EXP_NUM_FILE_MANIFESTS = EXP_NUM_DATA_PACKETS / subManifestSize +
+                                    !!(EXP_NUM_DATA_PACKETS % subManifestSize);
       auto manifests = manifestsDataPair.first;
       auto data = manifestsDataPair.second;
+
       // Verify the basic attributes of the manifest
-      size_t file_length = 0;
+      BOOST_CHECK_EQUAL(manifests.size(), EXP_NUM_FILE_MANIFESTS);
       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);
+        auto block = it->wireEncode();
+        BOOST_CHECK_EQUAL(*it, FileManifest(block));
         if (it != manifests.end() -1) {
           BOOST_CHECK_EQUAL(it->catalog().size(), subManifestSize);
           BOOST_CHECK_EQUAL(*(it->submanifest_ptr()), (it+1)->getFullName());
@@ -370,34 +400,55 @@
         else {
           BOOST_CHECK_LE(it->catalog().size(), subManifestSize);
           BOOST_CHECK_EQUAL(it->submanifest_ptr(), nullptr);
-          file_length += it->wireEncode().value_size();
         }
       }
+      // test that we can write the manifest to the disk and read it out again
+      {
+        std::string dirPath = "tests/testdata/temp/";
+        boost::filesystem::create_directory(dirPath);
+        auto fileNum = 0;
+        for (auto &m : manifests) {
+          fileNum++;
+          auto filename = dirPath + to_string(fileNum);
+          io::save(m, filename);
+        }
+        // read them back out
+        fileNum = 0;
+        for (auto &m : manifests) {
+          fileNum++;
+          auto filename = dirPath + to_string(fileNum);
+          auto manifest_ptr = io::load<FileManifest>(filename);
+          BOOST_CHECK_NE(manifest_ptr, nullptr);
+          BOOST_CHECK_EQUAL(m, *manifest_ptr);
+        }
+        fs::remove_all(dirPath);
+      }
       // confirm the manifests catalogs includes exactly the data packets
       if (getData) {
+        BOOST_CHECK_EQUAL(EXP_NUM_DATA_PACKETS, data.size());
         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++;
+            ++data_it;
           }
         }
         BOOST_CHECK_EQUAL(data_it, data.end());
         std::vector<uint8_t> data_bytes;
-        data_bytes.reserve(fs::file_size(filePath));
+        data_bytes.reserve(fileSize);
         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
+        // 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.reserve(fileSize);
         file_bytes.insert(file_bytes.begin(), start, end);
         file_bytes.shrink_to_fit();
         BOOST_CHECK_EQUAL(file_bytes, data_bytes);