Created inital design for file-manifest structure

Change-Id: Ie578aea9764fb586101dbcb77e41cc6e319f390b
diff --git a/src/file-manifest.cpp b/src/file-manifest.cpp
new file mode 100644
index 0000000..7352699
--- /dev/null
+++ b/src/file-manifest.cpp
@@ -0,0 +1,244 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+* Copyright (c) 2016 Regents of the University of California.
+*
+* This file is part of the nTorrent codebase.
+*
+* nTorrent 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.
+*
+* nTorrent 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 nTorrent, e.g., in COPYING.md file. If not, see
+* <http://www.gnu.org/licenses/>.
+*
+* See AUTHORS for complete list of nTorrent authors and contributors.
+*/
+#include "file-manifest.hpp"
+
+#include <boost/assert.hpp>
+#include <boost/range/adaptors.hpp>
+#include <boost/throw_exception.hpp>
+
+using std::vector;
+
+namespace ndn {
+namespace ntorrent {
+
+BOOST_CONCEPT_ASSERT((boost::EqualityComparable<FileManifest>));
+BOOST_CONCEPT_ASSERT((WireEncodable<FileManifest>));
+BOOST_CONCEPT_ASSERT((WireDecodable<FileManifest>));
+static_assert(std::is_base_of<Data::Error, FileManifest::Error>::value,
+                "FileManifest::Error should inherit from Data::Error");
+
+void
+FileManifest::wireDecode(const Block& wire)
+{
+  Data::wireDecode(wire);
+  this->decodeContent();
+}
+
+const Block&
+FileManifest::wireEncode()
+{
+  encodeContent();
+  return Data::wireEncode();
+}
+
+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
+  //                 DataPacketSize
+  //                 FileManifestPtr?
+
+  // DataPacketName ::= NAME-TYPE TLV-LENGTH
+  //                Name
+
+  // CatalogPrefix ::= NAME-TYPE TLV-LENGTH
+  //               Name
+
+  // DataPacketSize ::= CONTENT-TYPE TLV-LENGTH
+  //                nonNegativeInteger
+
+  // FileManifestPtr ::= NAME-TYPE TLV-LENGTH
+  //                 Name
+
+  size_t totalLength = 0;
+
+  vector<Name> suffixCatalog;
+  suffixCatalog.reserve(m_catalog.size());
+  for (auto name: m_catalog) {
+    if (!m_catalogPrefix.isPrefixOf(name)) {
+      BOOST_THROW_EXCEPTION(Error(name.toUri() + "does not have the prefix"
+                                               + m_catalogPrefix.toUri()));
+    }
+    name = name.getSubName(m_catalogPrefix.size());
+    if (name.empty()) {
+      BOOST_THROW_EXCEPTION(Error("Manifest cannot include empty string"));
+    }
+    suffixCatalog.push_back(name);
+  }
+
+  for (const auto& name : suffixCatalog |  boost::adaptors::reversed) {
+    totalLength += name.wireEncode(encoder);
+  }
+
+  totalLength += m_catalogPrefix.wireEncode(encoder);
+
+  totalLength += prependNonNegativeIntegerBlock(encoder, tlv::Content, m_dataPacketSize);
+
+  if (nullptr != m_submanifestPtr) {
+    totalLength += m_submanifestPtr->wireEncode(encoder);
+  }
+
+  totalLength += encoder.prependVarNumber(totalLength);
+  totalLength += encoder.prependVarNumber(tlv::Content);
+  return totalLength;
+
+}
+
+// MANIPULATORS
+void
+FileManifest::push_back(const Name& name)
+{
+  BOOST_ASSERT(name != m_catalogPrefix);
+  BOOST_ASSERT(m_catalogPrefix.isPrefixOf(name));
+  m_catalog.push_back(name);
+}
+
+bool
+FileManifest::remove(const ndn::Name& name) {
+  auto it = std::find(m_catalog.begin(), m_catalog.end(), name);
+  if (m_catalog.end() == it) {
+    return false;
+  }
+  m_catalog.erase(it);
+  return true;
+}
+
+void FileManifest::encodeContent() {
+  // Name
+  //     <file_name>/ImplicitDigest
+  // Content
+  //     MetaData
+  //     DataPtr*
+  //     ManifestPointer?
+  //
+  // DataPtr := HashValue
+  // ManifestPtr := HashValue
+  // HashValue   := OCTET[32]
+
+  // MetaData    := Property*
+  // Property    := DataSize | Signature
+  onChanged();
+
+  EncodingEstimator estimator;
+  size_t estimatedSize = encodeContent(estimator);
+
+  EncodingBuffer buffer(estimatedSize, 0);
+  encodeContent(buffer);
+
+  setContentType(tlv::ContentType_Blob);
+  setContent(buffer.block());
+}
+
+void
+FileManifest::decodeContent() {
+  // ManifestContent ::= CONTENT-TYPE TLV-LENGTH
+  //                 DataPacketName*
+  //                 CatalogPrefix
+  //                 DataPacketSize
+  //                 FileManifestPtr?
+
+
+  // DataPacketName ::= NAME-TYPE TLV-LENGTH
+  //                Name
+
+  // CatalogPrefix ::= NAME-TYPE TLV-LENGTH
+  //               Name
+
+  // DataPacketSize ::= CONTENT-TYPE TLV-LENGTH
+  //                nonNegativeInteger
+
+  // FileManifestPtr ::= NAME-TYPE TLV-LENGTH
+  //                 Name
+
+  if (getContentType() != tlv::ContentType_Blob) {
+    BOOST_THROW_EXCEPTION(Error("Expected Content Type Blob"));
+  }
+
+  const Block& content = Data::getContent();
+  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()) {
+    m_submanifestPtr = std::make_shared<Name>(name);
+    ++element;
+  }
+
+  // DataPacketSize
+  m_dataPacketSize = readNonNegativeInteger(*element);
+  ++element;
+
+  // CatalogPrefix
+  m_catalogPrefix = Name(*element);
+  element++;
+
+  // Catalog
+  m_catalog.clear();
+  for (; element != content.elements_end(); ++element) {
+    element->parse();
+    name = m_catalogPrefix;
+    name.append(Name(*element));
+    if (name == m_catalogPrefix) {
+      BOOST_THROW_EXCEPTION(Error("Empty name included in a FileManifest"));
+    }
+    push_back(name);
+  }
+}
+
+bool operator==(const FileManifest& lhs, const FileManifest& rhs) {
+  return lhs.name()             == rhs.name()
+      && lhs.data_packet_size() == rhs.data_packet_size()
+      && (lhs.submanifest_ptr() == rhs.submanifest_ptr() /* shallow  equality */
+         || ( nullptr != lhs.submanifest_ptr()
+           && nullptr != rhs.submanifest_ptr()
+           && *rhs.submanifest_ptr() == *lhs.submanifest_ptr()
+         )
+      )
+      && lhs.catalog()          == rhs.catalog();
+}
+
+bool operator!=(const FileManifest& lhs, const FileManifest& rhs) {
+  return lhs.name()             != rhs.name()
+      || lhs.data_packet_size() != rhs.data_packet_size()
+      || (lhs.submanifest_ptr() != rhs.submanifest_ptr() /* shallow  equality */
+         && (nullptr == lhs.submanifest_ptr()
+         || nullptr == rhs.submanifest_ptr()
+         || *rhs.submanifest_ptr() != *lhs.submanifest_ptr()
+        )
+      )
+      || lhs.catalog()          != rhs.catalog();
+}
+
+}  // end ntorrent
+}  // end ndn
\ No newline at end of file
diff --git a/src/file-manifest.hpp b/src/file-manifest.hpp
new file mode 100644
index 0000000..0a46574
--- /dev/null
+++ b/src/file-manifest.hpp
@@ -0,0 +1,244 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+* Copyright (c) 2016 Regents of the University of California.
+*
+* This file is part of the nTorrent codebase.
+*
+* nTorrent 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.
+*
+* nTorrent 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 nTorrent, e.g., in COPYING.md file. If not, see
+* <http://www.gnu.org/licenses/>.
+*
+* See AUTHORS for complete list of nTorrent authors and contributors.
+*/
+#ifndef INCLUDED_FILE_MANIFEST_HPP
+#define INCLUDED_FILE_MANIFEST_HPP
+
+#include <cstring>
+#include <list>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <ndn-cxx/data.hpp>
+#include <ndn-cxx/name.hpp>
+
+namespace ndn {
+namespace ntorrent {
+
+class FileManifest : public Data {
+/**
+* \class FileManifest
+*
+* \brief A value semantic type for File manifests
+*
+*/
+ public:
+  // TYPES
+  class Error : public Data::Error
+  {
+  public:
+    explicit
+    Error(const std::string& what)
+      : Data::Error(what)
+    {
+    }
+  };
+
+ public:
+  // CREATORS
+  FileManifest() = delete;
+
+  ~FileManifest() = default;
+  /// Destroy this object
+
+  FileManifest(const Name&              name,
+               size_t                   dataPacketSize,
+               const Name&              commonPrefix,
+               const std::vector<Name>& catalog = std::vector<Name>(),
+               std::shared_ptr<Name>    subManifestPtr = nullptr);
+  /**
+  * \brief Creates a new FileManifest with the specified values
+  *
+  * @param name The Name of this FileManifest
+  * @param dataPacketSize The size (except the last) of each data packet in this FileManifest
+  * @param commonPrefix The common prefix used for all named in the catalog
+  * @param catalog The collection of Names for this FileManifest
+  * @param subManifestPtr (optional) The Name for the sub-manifest in this for this file
+  */
+
+  FileManifest(const Name&           name,
+               size_t                dataPacketSize,
+               const Name&           catalogPrefix,
+               std::vector<Name>&&   catalog,
+               std::shared_ptr<Name> subManifestPtr = nullptr);
+  /**
+  * \brief Creates a new FileManifest with the specified values
+  *
+  * @param name The Name of this FileManifest
+  * @param dataPacketSize The size (except the last) of each data packet in this FileManifest
+  * @param catalogPrefix the common prefix used for all named in the catalog
+  * @param catalog The collection of Names for this FileManifest
+  * @param subManifestPtr (optional) The Name for the sub-manifest in this for this file
+  */
+
+  explicit
+  FileManifest(const Block& block);
+
+  FileManifest(const FileManifest& other) = default;
+  /// Creates a new FileManifest with same  value as the specified 'other'
+
+  // ACCESSORS
+  const Name&
+  name() const;
+  /// Returns the 'name' of this FileManifest
+
+  const Name&
+  catalog_prefix() const;
+  /// Returns the 'catalogPrefix' of this FileManfiest.
+
+  size_t
+  data_packet_size() const;
+  /// Returns the 'data_packet_size' of this FileManifest
+
+  std::shared_ptr<Name>
+  submanifest_ptr() const;
+  /// Returns the 'submanifest_ptr' of this FileManifest, or 'nullptr' is none exists
+
+  const std::vector<Name>&
+  catalog() const;
+  /// Returns an unmodifiable reference to the 'catalog' of this FileManifest
+
+ private:
+  template<encoding::Tag TAG>
+  size_t
+  encodeContent(EncodingImpl<TAG>& encoder) const;
+  /// Encodes the contents of this object and append the contents to the 'encoder'
+
+ public:
+  // MANIPULATORS
+  FileManifest&
+  operator=(const FileManifest& rhs) = default;
+  /// Assigns the value of the specific 'rhs' object to this object.
+
+  void
+  push_back(const Name& name);
+  /// Appends a Name to the catalog
+
+  bool
+  remove(const Name& name);
+  /// If 'name' in catalog, removes first instance and returns 'true', otherwise returns 'false'.
+
+  void
+  wireDecode(const Block& wire);
+  /**
+  * \brief Decodes the wire and assign its contents to this FileManifest
+  *
+  * @param wire the write to be decoded
+  * @throws Error if decoding fails
+  */
+  const Block&
+  wireEncode();
+  /// Encodes this FileManifest into the wire format and returns a Block with the contents
+
+private:
+  void
+  decodeContent();
+  /// Decodes the contents of this Data packet, assigning its contents to this FileManifest.
+
+  void
+  encodeContent();
+  /// Encodes the contents of this FileManifest into the content section of its Data packet.
+
+// DATA
+ private:
+  size_t                 m_dataPacketSize;
+  Name                   m_catalogPrefix;
+  std::vector<Name>      m_catalog;
+  std::shared_ptr<Name>  m_submanifestPtr;
+};
+
+/// Non-member functions
+bool operator==(const FileManifest& lhs, const FileManifest& rhs);
+/// Returns 'true' if 'lhs' and 'rhs' have the same value, 'false' otherwise.
+
+bool operator!=(const FileManifest& lhs, const FileManifest& rhs);
+/// Returns 'true' if 'lhs' and 'rhs' have different values, and 'false' otherwise.
+
+inline
+FileManifest::FileManifest(
+               const Name&              name,
+               size_t                   dataPacketSize,
+               const Name&              catalogPrefix,
+               const std::vector<Name>& catalog,
+               std::shared_ptr<Name>    subManifestPtr)
+: Data(name)
+, m_dataPacketSize(dataPacketSize)
+, m_catalogPrefix(catalogPrefix)
+, m_catalog(catalog)
+, m_submanifestPtr(subManifestPtr)
+{
+}
+
+inline
+FileManifest::FileManifest(
+               const Name&           name,
+               size_t                dataPacketSize,
+               const Name&           catalogPrefix,
+               std::vector<Name>&&   catalog,
+               std::shared_ptr<Name> subManifestPtr)
+: Data(name)
+, m_dataPacketSize(dataPacketSize)
+, m_catalogPrefix(catalogPrefix)
+, m_catalog(catalog)
+, m_submanifestPtr(subManifestPtr)
+{
+}
+
+inline
+FileManifest::FileManifest(const Block& block)
+{
+  wireDecode(block);
+}
+
+inline const Name&
+FileManifest::name() const
+{
+  return getName();
+}
+
+inline size_t
+FileManifest::data_packet_size() const
+{
+  return m_dataPacketSize;
+}
+
+inline const Name&
+FileManifest::catalog_prefix() const
+{
+  return m_catalogPrefix;
+}
+
+inline const std::vector<Name>&
+FileManifest::catalog() const
+{
+  return m_catalog;
+}
+
+inline std::shared_ptr<Name>
+FileManifest::submanifest_ptr() const
+{
+  return m_submanifestPtr;
+}
+
+}  // end ntorrent
+}  // end ndn
+
+#endif // INCLUDED_FILE_MANIFEST_HPP
diff --git a/tests/unit-tests/file-manifest.t.cpp b/tests/unit-tests/file-manifest.t.cpp
new file mode 100644
index 0000000..4bc69bd
--- /dev/null
+++ b/tests/unit-tests/file-manifest.t.cpp
@@ -0,0 +1,294 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+* Copyright (c) 2016 Regents of the University of California.
+*
+* This file is part of the nTorrent codebase.
+*
+* nTorrent 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.
+*
+* nTorrent 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 nTorrent, e.g., in COPYING.md file. If not, see
+* <http://www.gnu.org/licenses/>.
+*
+* See AUTHORS for complete list of nTorrent authors and contributors.
+*/
+
+#include "file-manifest.hpp"
+#include "boost-test.hpp"
+
+#include <vector>
+
+#include <ndn-cxx/data.hpp>
+#include <ndn-cxx/signature.hpp>
+#include <ndn-cxx/security/key-chain.hpp>
+
+BOOST_TEST_DONT_PRINT_LOG_VALUE(std::nullptr_t)
+BOOST_TEST_DONT_PRINT_LOG_VALUE(std::vector<ndn::Name>)
+
+namespace ndn {
+namespace ntorrent {
+namespace tests {
+
+using std::vector;
+using ndn::Name;
+
+BOOST_AUTO_TEST_SUITE(TestFileManifest)
+
+BOOST_AUTO_TEST_CASE(CheckPrimaryAccessorsAndManipulators)
+{
+  FileManifest m1("/file0/1A2B3C4D", 256, "/foo/");
+
+  // Check the values on most simply constructed m1
+  BOOST_CHECK_EQUAL("/file0/1A2B3C4D", m1.name());
+  BOOST_CHECK_EQUAL(256, m1.data_packet_size());
+  BOOST_CHECK_EQUAL(nullptr, m1.submanifest_ptr());
+  BOOST_CHECK_EQUAL("/foo/", m1.catalog_prefix());
+  BOOST_CHECK_EQUAL(vector<Name>({}), m1.catalog());
+
+  // Append names to catalog and recheck all salient attributes
+  m1.push_back("/foo/0/ABC123");
+  m1.push_back("/foo/1/DEADBEFF");
+  m1.push_back("/foo/2/CAFEBABE");
+
+  BOOST_CHECK_EQUAL("/file0/1A2B3C4D", m1.name());
+  BOOST_CHECK_EQUAL(256, m1.data_packet_size());
+  BOOST_CHECK_EQUAL(nullptr, m1.submanifest_ptr());
+  BOOST_CHECK_EQUAL("/foo/", m1.catalog_prefix());
+  BOOST_CHECK_EQUAL(vector<Name>({"/foo/0/ABC123", "/foo/1/DEADBEFF", "/foo/2/CAFEBABE"}),
+                    m1.catalog());
+
+  // Remove a value from the catalog and recheck all salient attributes
+  BOOST_CHECK_EQUAL(true, m1.remove("/foo/0/ABC123"));
+
+  BOOST_CHECK_EQUAL("/file0/1A2B3C4D", m1.name());
+  BOOST_CHECK_EQUAL(256, m1.data_packet_size());
+  BOOST_CHECK_EQUAL(nullptr, m1.submanifest_ptr());
+  BOOST_CHECK_EQUAL("/foo/", m1.catalog_prefix());
+  BOOST_CHECK_EQUAL(vector<Name>({"/foo/1/DEADBEFF", "/foo/2/CAFEBABE"}), m1.catalog());
+
+  // Try to remove a value no longer in the catalog, and recheck that all salient attributes
+  BOOST_CHECK_EQUAL(false, m1.remove("/foo/0/ABC123"));
+
+  BOOST_CHECK_EQUAL("/file0/1A2B3C4D", m1.name());
+  BOOST_CHECK_EQUAL(256, m1.data_packet_size());
+  BOOST_CHECK_EQUAL(nullptr, m1.submanifest_ptr());
+  BOOST_CHECK_EQUAL("/foo/", m1.catalog_prefix());
+  BOOST_CHECK_EQUAL(vector<Name>({"/foo/1/DEADBEFF", "/foo/2/CAFEBABE"}), m1.catalog());
+
+  // Try to remove a value never in the catalog, and recheck that all salient attributes
+  BOOST_CHECK_EQUAL(false, m1.remove("/bar/0/ABC123"));
+  BOOST_CHECK_EQUAL("/file0/1A2B3C4D", m1.name());
+  BOOST_CHECK_EQUAL(256, m1.data_packet_size());
+  BOOST_CHECK_EQUAL(nullptr, m1.submanifest_ptr());
+  BOOST_CHECK_EQUAL("/foo/", m1.catalog_prefix());
+  BOOST_CHECK_EQUAL(vector<Name>({"/foo/1/DEADBEFF", "/foo/2/CAFEBABE"}),
+                    m1.catalog());
+
+  // Remove a value from the end of the list
+  BOOST_CHECK_EQUAL(true, m1.remove("/foo/2/CAFEBABE"));
+
+  BOOST_CHECK_EQUAL("/file0/1A2B3C4D", m1.name());
+  BOOST_CHECK_EQUAL(256, m1.data_packet_size());
+  BOOST_CHECK_EQUAL(nullptr, m1.submanifest_ptr());
+  BOOST_CHECK_EQUAL("/foo/", m1.catalog_prefix());
+  BOOST_CHECK_EQUAL(vector<Name>({"/foo/1/DEADBEFF"}),
+                    m1.catalog());
+}
+
+BOOST_AUTO_TEST_CASE(CheckValueCtors)
+{
+  FileManifest m1("/file0/1A2B3C4D",
+                  256,
+                  "/foo/",
+                  {"/foo/0/ABC123",  "/foo/1/DEADBEFF", "/foo/2/CAFEBABE"},
+                  std::make_shared<Name>("/file0/1/5E6F7G8H"));
+
+  BOOST_CHECK_EQUAL("/file0/1A2B3C4D", m1.name());
+  BOOST_CHECK_EQUAL(256, m1.data_packet_size());
+  BOOST_CHECK_EQUAL("/file0/1/5E6F7G8H", *m1.submanifest_ptr());
+  BOOST_CHECK_EQUAL("/foo/", m1.catalog_prefix());
+  BOOST_CHECK_EQUAL(vector<Name>({"/foo/0/ABC123", "/foo/1/DEADBEFF", "/foo/2/CAFEBABE"}),
+                    m1.catalog());
+
+  // Remove a value from the catalog and recheck all salient attributes
+  BOOST_CHECK_EQUAL(true, m1.remove("/foo/0/ABC123"));
+
+  BOOST_CHECK_EQUAL("/file0/1A2B3C4D", m1.name());
+  BOOST_CHECK_EQUAL(256, m1.data_packet_size());
+  BOOST_CHECK_EQUAL("/file0/1/5E6F7G8H", *m1.submanifest_ptr());
+  BOOST_CHECK_EQUAL("/foo/", m1.catalog_prefix());
+  BOOST_CHECK_EQUAL(vector<Name>({"/foo/1/DEADBEFF", "/foo/2/CAFEBABE"}), m1.catalog());
+
+  // Try to remove a value no longer in the catalog, and recheck that all salient attributes
+  BOOST_CHECK_EQUAL(false, m1.remove("/foo/0/ABC123"));
+
+  BOOST_CHECK_EQUAL("/file0/1A2B3C4D", m1.name());
+  BOOST_CHECK_EQUAL(256, m1.data_packet_size());
+  BOOST_CHECK_EQUAL("/file0/1/5E6F7G8H", *m1.submanifest_ptr());
+  BOOST_CHECK_EQUAL("/foo/", m1.catalog_prefix());
+  BOOST_CHECK_EQUAL(vector<Name>({"/foo/1/DEADBEFF", "/foo/2/CAFEBABE"}), m1.catalog());
+
+  // Try to remove a value never in the catalog, and recheck that all salient attributes
+  BOOST_CHECK_EQUAL(false, m1.remove("/bar/0/ABC123"));
+  BOOST_CHECK_EQUAL("/file0/1A2B3C4D", m1.name());
+  BOOST_CHECK_EQUAL(256, m1.data_packet_size());
+  BOOST_CHECK_EQUAL("/file0/1/5E6F7G8H", *m1.submanifest_ptr());
+  BOOST_CHECK_EQUAL("/foo/", m1.catalog_prefix());
+  BOOST_CHECK_EQUAL(vector<Name>({"/foo/1/DEADBEFF", "/foo/2/CAFEBABE"}), m1.catalog());
+
+  // Remove a value from the end of the list
+  BOOST_CHECK_EQUAL(true, m1.remove("/foo/2/CAFEBABE"));
+
+  BOOST_CHECK_EQUAL("/file0/1A2B3C4D", m1.name());
+  BOOST_CHECK_EQUAL(256, m1.data_packet_size());
+  BOOST_CHECK_EQUAL("/file0/1/5E6F7G8H", *m1.submanifest_ptr());
+  BOOST_CHECK_EQUAL("/foo/", m1.catalog_prefix());
+  BOOST_CHECK_EQUAL(vector<Name>({"/foo/1/DEADBEFF"}), m1.catalog());
+}
+
+BOOST_AUTO_TEST_CASE(CheckAssignmentOperatorEqaulityAndCopyCtor)
+{
+  // 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"));
+
+    BOOST_CHECK_EQUAL(m1, m2);
+    BOOST_CHECK(!(m1 != m2));
+
+    // Change value of one
+    m1.push_back("/foo/0/ABC123");
+    BOOST_CHECK_NE(m1, m2);
+    BOOST_CHECK(!(m1 == m2));
+
+    // Update other
+    m2.push_back("/foo/0/ABC123");
+    BOOST_CHECK_EQUAL(m1, m2);
+    BOOST_CHECK(!(m1 != m2));
+
+    // Change value again
+    m1.remove("/foo/0/ABC123");
+    BOOST_CHECK_NE(m1, m2);
+    BOOST_CHECK(!(m1 == m2));
+
+    m2.remove("/foo/0/ABC123");
+    BOOST_CHECK_EQUAL(m1, m2);
+    BOOST_CHECK(!(m1 != m2));
+  }
+
+  // Set sub-manifest pointer in one and not the other
+  {
+    FileManifest m1("/file0/1A2B3C4D",
+                    256,
+                    "/foo/",
+                    vector<Name>({}),
+                    std::make_shared<Name>("/file0/1/5E6F7G8H"));
+
+    FileManifest m2("/file0/1A2B3C4D", 256, "/foo/");
+    BOOST_CHECK_NE(m1, m2);
+    BOOST_CHECK(!(m1 == m2));
+
+    std::swap(m1, m2);
+    BOOST_CHECK_NE(m1, m2);
+    BOOST_CHECK(!(m1 == m2));
+  }
+
+  // Construct two manifests using copy ctor for one
+  {
+    FileManifest m1("/file0/1A2B3C4D", 256, "/foo/");
+    FileManifest m2(m1);
+
+    BOOST_CHECK_EQUAL(m1, m2);
+    BOOST_CHECK(!(m1 != m2));
+
+    // Change value of one
+    m1.push_back("/foo/0/ABC123");
+    BOOST_CHECK_NE(m1, m2);
+    BOOST_CHECK(!(m1 == m2));
+
+    // Update other
+    m2.push_back("/foo/0/ABC123");
+    BOOST_CHECK_EQUAL(m1, m2);
+    BOOST_CHECK(!(m1 != m2));
+
+    // Change value again
+    m1.remove("/foo/0/ABC123");
+    BOOST_CHECK_NE(m1, m2);
+    BOOST_CHECK(!(m1 == m2));
+
+    m2.remove("/foo/0/ABC123");
+    BOOST_CHECK_EQUAL(m1, m2);
+    BOOST_CHECK(!(m1 != m2));
+  }
+
+  // Use assignment operator
+  {
+    FileManifest m1("/file1/1A2B3C4D", 256, "/foo/");
+    FileManifest m2("/file1/5E6F7G8H", 256, "/foo/");
+
+    BOOST_CHECK_NE(m1, m2);
+    BOOST_CHECK(!(m1 == m2));
+
+    m2 = m1;
+    BOOST_CHECK_EQUAL(m1, m2);
+    BOOST_CHECK(!(m1 != m2));
+
+    // Change value of one
+    m1.push_back("/foo/0/ABC123");
+    BOOST_CHECK_NE(m1, m2);
+    BOOST_CHECK(!(m1 == m2));
+
+    // Update other
+    m2.push_back("/foo/0/ABC123");
+    BOOST_CHECK_EQUAL(m1, m2);
+    BOOST_CHECK(!(m1 != m2));
+
+    // Change value again
+    m1.remove("/foo/0/ABC123");
+    BOOST_CHECK_NE(m1, m2);
+    BOOST_CHECK(!(m1 == m2));
+
+    m2.remove("/foo/0/ABC123");
+    BOOST_CHECK_EQUAL(m1, m2);
+    BOOST_CHECK(!(m1 != m2));
+  }
+}
+
+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"));
+
+  KeyChain keyChain;
+  keyChain.sign(m1);
+  BOOST_CHECK_EQUAL(m1, FileManifest(m1.wireEncode()));
+
+  // Change value and be sure that wireEncoding still works
+  m1.remove("/foo/2/CAFEBABE");
+  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);
+  m2.wireDecode(m1.wireEncode());
+  BOOST_CHECK_EQUAL(m1, m2);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+} // namespace nTorrent
+} // namespace ndn