Enable torrent manager to check whether we already have a requested torrent file segment, manifest file segment, data packet before downloading

Refs: #3505
Change-Id: I97472459a774b1a858462f82a5b7d9606bd0843a
diff --git a/src/torrent-manager.cpp b/src/torrent-manager.cpp
index c06db40..4a0eaff 100644
--- a/src/torrent-manager.cpp
+++ b/src/torrent-manager.cpp
@@ -436,18 +436,44 @@
 
 void
 TorrentManager::downloadTorrentFile(const std::string& path,
-                                    DataReceivedCallback onSuccess,
+                                    TorrentFileReceivedCallback onSuccess,
                                     FailedCallback onFailed)
 {
+  shared_ptr<Name> searchRes = this->findTorrentFileSegmentToDownload();
   auto manifestNames = make_shared<std::vector<Name>>();
-  this->downloadTorrentFileSegment(m_torrentFileName, path, manifestNames,
+  if (searchRes == nullptr) {
+    this->findFileManifestsToDownload(*manifestNames);
+    if (manifestNames->empty()) {
+      auto packetNames = make_shared<std::vector<Name>>();
+      this->findAllMissingDataPackets(*packetNames);
+      onSuccess(*packetNames);
+      return;
+    }
+    else {
+      onSuccess(*manifestNames);
+      return;
+    }
+  }
+  this->downloadTorrentFileSegment(*searchRes, path, manifestNames,
                                    true, onSuccess, onFailed);
 }
 
 std::vector<Name>
 TorrentManager::downloadTorrentFile(const std::string& path)
 {
+  shared_ptr<Name> searchRes = this->findTorrentFileSegmentToDownload();
   auto manifestNames = make_shared<std::vector<Name>>();
+  if (searchRes == nullptr) {
+    this->findFileManifestsToDownload(*manifestNames);
+    if (manifestNames->empty()) {
+      auto packetNames = make_shared<std::vector<Name>>();
+      this->findAllMissingDataPackets(*packetNames);
+      return *packetNames;
+    }
+    else {
+      return *manifestNames;
+    }
+  }
   this->downloadTorrentFileSegment(m_torrentFileName, path, manifestNames,
                                   false, {}, {});
   return *manifestNames;
@@ -458,7 +484,7 @@
                                            const std::string& path,
                                            std::shared_ptr<std::vector<Name>> manifestNames,
                                            bool async,
-                                           DataReceivedCallback onSuccess,
+                                           TorrentFileReceivedCallback onSuccess,
                                            FailedCallback onFailed)
 {
   shared_ptr<Interest> interest = createInterest(name);
@@ -469,6 +495,10 @@
       m_stats_table_iter->incrementReceivedData();
       m_retries = 0;
 
+      if (async) {
+        manifestNames->clear();
+      }
+
       TorrentFile file(data.wireEncode());
 
       // Write the torrent file segment to disk...
@@ -480,7 +510,7 @@
       shared_ptr<Name> nextSegmentPtr = file.getTorrentFilePtr();
 
       if (async) {
-        onSuccess(file.getName());
+        onSuccess(*manifestNames);
       }
       if (nextSegmentPtr != nullptr) {
         this->downloadTorrentFileSegment(*nextSegmentPtr, path, manifestNames,
@@ -517,8 +547,14 @@
                                        TorrentManager::ManifestReceivedCallback onSuccess,
                                        TorrentManager::FailedCallback           onFailed)
 {
+  shared_ptr<Name> searchRes = findManifestSegmentToDownload(manifestName);
   auto packetNames = make_shared<std::vector<Name>>();
-  this->downloadFileManifestSegment(manifestName, path, packetNames, onSuccess, onFailed);
+  if (searchRes == nullptr) {
+    this->findDataPacketsToDownload(manifestName, *packetNames);
+    onSuccess(*packetNames);
+    return;
+  }
+  this->downloadFileManifestSegment(*searchRes, path, packetNames, onSuccess, onFailed);
 }
 
 void
@@ -570,6 +606,11 @@
                                      DataReceivedCallback onSuccess,
                                      FailedCallback onFailed)
 {
+  if (this->dataAlreadyDownloaded(packetName)) {
+    onSuccess(packetName);
+    return;
+  }
+
   shared_ptr<Interest> interest = this->createInterest(packetName);
 
   auto dataReceived = [onSuccess, onFailed, this]
@@ -624,5 +665,121 @@
   return interest;
 }
 
+shared_ptr<Name>
+TorrentManager::findTorrentFileSegmentToDownload()
+{
+  // if we have no segments
+  if (m_torrentSegments.empty()) {
+    return make_shared<Name>(m_torrentFileName);
+  }
+  // otherwise just return the next segment ptr of the last segment we have
+  return m_torrentSegments.back().getTorrentFilePtr();
+}
+
+shared_ptr<Name>
+TorrentManager::findManifestSegmentToDownload(const Name& manifestName)
+{
+  //sequentially find whether we have downloaded any segments of this manifest file
+  Name manifestPrefix = manifestName.getSubName(0, manifestName.size() - 2);
+  auto it = std::find_if(m_fileManifests.rbegin(), m_fileManifests.rend(),
+                      [&manifestPrefix] (const FileManifest& f) {
+                        return manifestPrefix.isPrefixOf(f.getName());
+                      });
+
+  // if we do not have any segments of the file manifest
+  if (it == m_fileManifests.rend()) {
+    return make_shared<Name>(manifestName);
+  }
+
+  // if we already have the requested segment of the file manifest
+  if (it->submanifest_number() >= manifestName.get(manifestName.size() - 2).toSequenceNumber()) {
+    return it->submanifest_ptr();
+  }
+  // if we do not have the requested segment
+  else {
+    return make_shared<Name>(manifestName);
+  }
+}
+
+bool
+TorrentManager::dataAlreadyDownloaded(const Name& dataName)
+{
+
+  auto manifest_it = std::find_if(m_fileManifests.begin(), m_fileManifests.end(),
+                                 [&dataName](const FileManifest& m) {
+                                   return m.getName().isPrefixOf(dataName);
+                                 });
+
+  // if we do not have the file manifest, just return false
+  if (manifest_it == m_fileManifests.end()) {
+    return false;
+  }
+
+  // find the pair of (std::shared_ptr<fs::fstream>, std::vector<bool>)
+  // that corresponds to the specific submanifest
+  auto& fileState = m_fileStates[manifest_it->getFullName()];
+
+  auto dataNum = dataName.get(dataName.size() - 2).toSequenceNumber();
+
+  // find whether we have the requested packet from the bitmap
+  return fileState.second[dataNum];
+}
+
+void
+TorrentManager::findFileManifestsToDownload(std::vector<Name>& manifestNames)
+{
+  std::vector<Name> manifests;
+  // insert the first segment name of all the file manifests to the vector
+  for (auto i = m_torrentSegments.begin(); i != m_torrentSegments.end(); i++) {
+    manifests.insert(manifests.end(), i->getCatalog().begin(), i->getCatalog().end());
+  }
+  // for each file
+  for (const auto& manifestName : manifests) {
+    // find the first (if any) segment we are missing
+    shared_ptr<Name> manifestSegmentName = findManifestSegmentToDownload(manifestName);
+    if (nullptr != manifestSegmentName) {
+      manifestNames.push_back(*manifestSegmentName);
+    }
+  }
+}
+
+void
+TorrentManager::findDataPacketsToDownload(const Name& manifestName, std::vector<Name>& packetNames)
+{
+  auto manifest_it = std::find_if(m_fileManifests.begin(), m_fileManifests.end(),
+                                 [&manifestName](const FileManifest& m) {
+                                   return m.name().getSubName(0, m.name().size()
+                                                              - 1).isPrefixOf(manifestName);
+                                 });
+
+  for (auto j = manifest_it; j != m_fileManifests.end(); j++) {
+    auto& fileState = m_fileStates[j->getFullName()];
+    for (size_t dataNum = 0; dataNum < j->catalog().size(); ++dataNum) {
+      if (!fileState.second[dataNum]) {
+        packetNames.push_back(j->catalog()[dataNum]);
+      }
+    }
+
+    // check that the next manifest in the vector refers to the next segment of the same file
+    if ((j + 1) != m_fileManifests.end() && (j+1)->file_name() != manifest_it->file_name()) {
+      break;
+    }
+  }
+}
+
+void
+TorrentManager::findAllMissingDataPackets(std::vector<Name>& packetNames)
+{
+  for (auto j = m_fileManifests.begin(); j != m_fileManifests.end(); j++) {
+    auto& fileState = m_fileStates[j->getFullName()];
+    for (auto i = j->catalog().begin(); i != j->catalog().end(); i++) {
+      auto dataNum = i->get(i->size() - 2).toSequenceNumber();
+      if (!fileState.second[dataNum]) {
+        packetNames.push_back(*i);
+      }
+    }
+  }
+}
+
 }  // end ntorrent
 }  // end ndn
diff --git a/src/torrent-manager.hpp b/src/torrent-manager.hpp
index a96f21b..28053f2 100644
--- a/src/torrent-manager.hpp
+++ b/src/torrent-manager.hpp
@@ -54,6 +54,7 @@
  public:
    typedef std::function<void(const ndn::Name&)>                     DataReceivedCallback;
    typedef std::function<void(const std::vector<ndn::Name>&)>        ManifestReceivedCallback;
+   typedef std::function<void(const std::vector<ndn::Name>&)>        TorrentFileReceivedCallback;
    typedef std::function<void(const ndn::Name&, const std::string&)> FailedCallback;
 
    /*
@@ -114,7 +115,7 @@
    */
   void
   downloadTorrentFile(const std::string& path,
-                      DataReceivedCallback onSuccess,
+                      TorrentFileReceivedCallback onSuccess,
                       FailedCallback onFailed);
 
   /*
@@ -131,10 +132,11 @@
    * This method provides non-blocking downloading of all the file manifest segments
    *
    */
-  void download_file_manifest(const Name&              manifestName,
-                              const std::string&       path,
-                              ManifestReceivedCallback onSuccess,
-                              FailedCallback           onFailed);
+  void
+  download_file_manifest(const Name&              manifestName,
+                         const std::string&       path,
+                         ManifestReceivedCallback onSuccess,
+                         FailedCallback           onFailed);
 
   /*
    * @brief Download a data packet
@@ -147,12 +149,14 @@
    * This method writes the downloaded data packet to m_dataPath on disk
    *
    */
-  void download_data_packet(const Name&          packetName,
-                            DataReceivedCallback onSuccess,
-                            FailedCallback       onFailed);
+  void
+  download_data_packet(const Name&          packetName,
+                       DataReceivedCallback onSuccess,
+                       FailedCallback       onFailed);
 
   // Seed the specified 'data' to the network.
-  void seed(const Data& data) const;
+  void
+  seed(const Data& data) const;
 
  protected:
   /*
@@ -162,7 +166,8 @@
    * otherwise. Behavior is undefined unless the corresponding file manifest has already been
    * downloaded.
    */
-  bool writeData(const Data& packet);
+  bool
+  writeData(const Data& packet);
 
   /*
    * \brief Write the @p segment torrent segment to disk at the specified path.
@@ -172,7 +177,8 @@
    * otherwise. Behavior is undefined unless @segment is a correct segment for the torrent file of
    * this manager and @p path is the directory used for all segments of this torrent file.
    */
-  bool writeTorrentSegment(const TorrentFile& segment, const std::string& path);
+  bool
+  writeTorrentSegment(const TorrentFile& segment, const std::string& path);
 
   /*
    * \brief Write the @p manifest file manifest to disk at the specified @p path.
@@ -183,7 +189,8 @@
    * torrent file of this manager and @p path is the directory used for all file manifests of this
    * torrent file.
    */
-  bool writeFileManifest(const FileManifest& manifest, const std::string& path);
+  bool
+  writeFileManifest(const FileManifest& manifest, const std::string& path);
 
   /*
    * \brief Download the segments of the torrent file
@@ -201,12 +208,13 @@
    *                 specified when async is false
    *
    */
-  void downloadTorrentFileSegment(const ndn::Name& name,
-                                  const std::string& path,
-                                  std::shared_ptr<std::vector<Name>> manifestNames,
-                                  bool async,
-                                  DataReceivedCallback onSuccess,
-                                  FailedCallback onFailed);
+  void
+  downloadTorrentFileSegment(const ndn::Name& name,
+                             const std::string& path,
+                             std::shared_ptr<std::vector<Name>> manifestNames,
+                             bool async,
+                             TorrentFileReceivedCallback onSuccess,
+                             FailedCallback onFailed);
 
   /*
    * \brief Download the segments of a file manifest
@@ -218,11 +226,12 @@
    * @param onFailed Callback to be called when we fail to download a file manifest segment
    *
    */
-  void downloadFileManifestSegment(const Name& manifestName,
-                                   const std::string& path,
-                                   std::shared_ptr<std::vector<Name>> packetNames,
-                                   TorrentManager::ManifestReceivedCallback onSuccess,
-                                   TorrentManager::FailedCallback onFailed);
+  void
+  downloadFileManifestSegment(const Name& manifestName,
+                              const std::string& path,
+                              std::shared_ptr<std::vector<Name>> packetNames,
+                              ManifestReceivedCallback onSuccess,
+                              FailedCallback onFailed);
 
   enum {
     // Number of times to retry if a routable prefix fails to retrieve data
@@ -245,6 +254,67 @@
   // The path to the location on disk of the Data packet for this manager
   std::string                                                         m_dataPath;
 
+protected:
+  /*
+   * \brief Find the torrent file segment that we should download
+   *        (either we have nothing or we have them all)
+   * @return A shared_ptr to the name of the segment to download or
+   *         nullptr if we have all the segments
+   *
+   */
+  shared_ptr<Name>
+  findTorrentFileSegmentToDownload();
+
+  /*
+   * \brief Given a file manifest segment name, find the next file manifest segment
+   *        that we should download
+   * @param manifestName The name of the file manifest segment that we want to download
+   * @return A shared_ptr to the name of the segment to download or
+   *         nullptr if we have all the segments
+   *
+   */
+  shared_ptr<Name>
+  findManifestSegmentToDownload(const Name& manifestName);
+
+  /*
+   * \brief Given a data packet name, find whether we have already downloaded this packet
+   * @param dataName The name of the data packet to download
+   * @return True if we have already downloaded this packet, false otherwise
+   *
+   */
+  bool
+  dataAlreadyDownloaded(const Name& dataName);
+
+  /*
+   * \brief Find the segments of all the file manifests that we are missing
+   * @param manifestNames The name of the manifest file segments to download (currently missing)
+   *                      This parameter is used as an output vector of names
+   */
+  void
+  findFileManifestsToDownload(std::vector<Name>& manifestNames);
+
+  /*
+   * \brief Find the names of the data packets of a file manifest that we are currently missing
+   * @param manifestName The name of the manifest
+   * @param packetNames The name of the data packets to be downloaded
+   *                    (used as an output vector of names)
+   *
+   * No matter what segment of a manifest file the manifestName parameter might refer to, the
+   * missing data packets starting from the first segment of this manifest file would be returned
+   *
+   */
+  void
+  findDataPacketsToDownload(const Name& manifestName, std::vector<Name>& packetNames);
+
+  /*
+   * \brief Find all the data packets that we are currently missing
+   * @param packetNames The name of the data packets to be downloaded
+   *                    (used as an output vector of names)
+   *
+   */
+  void
+  findAllMissingDataPackets(std::vector<Name>& packetNames);
+
 private:
   shared_ptr<Interest>
   createInterest(Name name);
diff --git a/tests/unit-tests/torrent-manager.t.cpp b/tests/unit-tests/torrent-manager.t.cpp
index bb16344..29938eb 100644
--- a/tests/unit-tests/torrent-manager.t.cpp
+++ b/tests/unit-tests/torrent-manager.t.cpp
@@ -65,6 +65,26 @@
     return m_fileManifests;
   }
 
+  void pushTorrentSegment(const TorrentFile& t) {
+    m_torrentSegments.push_back(t);
+  }
+
+  void pushFileManifestSegment(const FileManifest& m) {
+    m_fileManifests.push_back(m);
+  }
+
+  shared_ptr<Name> findTorrentFileSegmentToDownload() {
+    return TorrentManager::findTorrentFileSegmentToDownload();
+  }
+
+  shared_ptr<Name> findManifestSegmentToDownload(const Name& manifestName) {
+    return TorrentManager::findManifestSegmentToDownload(manifestName);
+  }
+
+  bool dataAlreadyDownloaded(const Name& name) {
+    return TorrentManager::dataAlreadyDownloaded(name);
+  }
+
   std::vector<bool> fileState(const ndn::Name& manifestName) {
     auto fout = m_fileStates[manifestName].first;
     if (nullptr != fout) {
@@ -73,6 +93,13 @@
     return m_fileStates[manifestName].second;
   }
 
+  void setFileState(const ndn::Name manifestName,
+                    std::shared_ptr<fs::fstream> f,
+                    const std::vector<bool>& stateVec) {
+
+    m_fileStates.insert({ manifestName, std::make_pair(f, stateVec) });
+  }
+
   bool writeData(const Data& data) {
     return TorrentManager::writeData(data);
   }
@@ -80,6 +107,7 @@
   bool writeTorrentSegment(const TorrentFile& segment, const std::string& path) {
     return TorrentManager::writeTorrentSegment(segment, path);
   }
+
   bool writeFileManifest(const FileManifest& manifest, const std::string& path) {
     return TorrentManager::writeFileManifest(manifest, path);
   }
@@ -293,13 +321,19 @@
 
   // Test download torrent file segments
   uint32_t counter = 0;
-  manager.downloadTorrentFile(filePath + "torrent_files", [&counter, &torrentSegments] (const ndn::Name& name) {
-                                          BOOST_CHECK_EQUAL(torrentSegments[counter].getName(), name);
-                                          counter++;
-                                        },
-                                        bind([] {
-                                          BOOST_FAIL("Unexpected failure");
-                                        }));
+  manager.downloadTorrentFile(filePath + "torrent_files", [&counter, &torrentSegments]
+                                (const std::vector<ndn::Name>& vec) {
+                                  uint32_t manifestNum = 0;
+                                  for (auto i = vec.begin(); i != vec.end(); i++) {
+                                    Name n = torrentSegments[counter].getCatalog()[manifestNum];
+                                    BOOST_CHECK_EQUAL(n, *i);
+                                    manifestNum++;
+                                  }
+                                  counter++;
+                                },
+                                bind([] {
+                                  BOOST_FAIL("Unexpected failure");
+                                }));
 
   for (auto i = torrentSegments.begin(); i != torrentSegments.end(); i++) {
     advanceClocks(time::milliseconds(1), 40);
@@ -307,6 +341,7 @@
   }
 
   fs::remove_all(filePath);
+  fs::remove_all(".appdata");
 }
 
 BOOST_AUTO_TEST_CASE(TestDownloadingFileManifests)
@@ -380,6 +415,7 @@
   }
 
   fs::remove_all(filePath);
+  fs::remove_all(".appdata");
 }
 
 BOOST_AUTO_TEST_CASE(TestDownloadingDataPackets)
@@ -411,7 +447,7 @@
 
   // Fail to download data
   manager.download_data_packet(dataName,
-                              [] (const ndn::Name& name) {
+                              [](const ndn::Name& name) {
                                 BOOST_FAIL("Unexpected failure");
                               },
                               [&dataName](const ndn::Name& name, const std::string& reason) {
@@ -421,6 +457,389 @@
   advanceClocks(time::milliseconds(1), 2100);
 
   fs::remove_all(filePath);
+  fs::remove_all(".appdata");
+}
+
+// we already have downloaded the torrent file
+BOOST_AUTO_TEST_CASE(TestFindTorrentFileSegmentToDownload1)
+{
+  std::string filePath = ".appdata/foo/";
+  TestTorrentManager manager("NTORRENT/test/torrent-file/sha256digest",
+                             filePath, face);
+  manager.Initialize();
+
+  TorrentFile t1(Name("NTORRENT/test/torrent-file/sha256digest"),
+                 Name("NTORRENT/test/torrent-file/1/sha256digest"), Name("/test"),
+                 { Name("/manifest1") });
+  manager.pushTorrentSegment(t1);
+
+  TorrentFile t2(Name("NTORRENT/test/torrent-file/1/sha256digest"),
+                 Name("NTORRENT/test/torrent-file/2/sha256digest"), Name("/test"),
+                 { Name("/manifest2"), Name("/manifest3") });
+  manager.pushTorrentSegment(t2);
+
+  TorrentFile t3(Name("NTORRENT/test/torrent-file/3/sha256digest"),
+                 Name("NTORRENT/test/torrent-file/4/sha256digest"), Name("/test"),
+                 { Name("/manifest4"), Name("/manifest5") });
+  manager.pushTorrentSegment(t3);
+
+  TorrentFile t4(Name("NTORRENT/test/torrent-file/4/sha256digest"), Name("/test"), {});
+  manager.pushTorrentSegment(t4);
+
+  BOOST_CHECK(!(manager.findTorrentFileSegmentToDownload()));
+
+  std::vector<Name> manifests;
+  manifests = manager.downloadTorrentFile("/test");
+
+  BOOST_CHECK_EQUAL(manifests[0].toUri(), "/manifest1");
+  BOOST_CHECK_EQUAL(manifests[1].toUri(), "/manifest2");
+  BOOST_CHECK_EQUAL(manifests[2].toUri(), "/manifest3");
+  BOOST_CHECK_EQUAL(manifests[3].toUri(), "/manifest4");
+  BOOST_CHECK_EQUAL(manifests[4].toUri(), "/manifest5");
+
+  fs::remove_all(filePath);
+  fs::remove_all(".appdata");
+}
+
+// we do not have the torrent file
+BOOST_AUTO_TEST_CASE(TestFindTorrentFileSegmentToDownload2)
+{
+  std::string filePath = ".appdata/foo/";
+  TestTorrentManager manager("/test/0/sha256digest",
+                             filePath, face);
+  manager.Initialize();
+
+  BOOST_CHECK_EQUAL(manager.findTorrentFileSegmentToDownload()->toUri(), "/test/0/sha256digest");
+
+  fs::remove_all(filePath);
+  fs::remove_all(".appdata");
+}
+
+// we have the torrent file and the manifests
+BOOST_AUTO_TEST_CASE(TestFindTorrentFileSegmentToDownload3)
+{
+  vector<FileManifest> manifests;
+  vector<TorrentFile>  torrentSegments;
+  // for each file, the data packets
+  std::vector<vector<Data>> fileData;
+  std::string filePath = "tests/testdata/temp";
+  // get torrent files and manifests
+  {
+    auto temp = TorrentFile::generate("tests/testdata/foo",
+                                      1024,
+                                      2048,
+                                      8192,
+                                      true);
+    torrentSegments = temp.first;
+    auto temp1      = temp.second;
+    for (const auto& ms : temp1) {
+      manifests.insert(manifests.end(), ms.first.begin(), ms.first.end());
+      fileData.push_back(ms.second);
+    }
+  }
+  // write the torrent segments and manifests to disk
+  std::string dirPath = ".appdata/foo/";
+  boost::filesystem::create_directories(dirPath);
+  std::string torrentPath = dirPath + "torrent_files/";
+  boost::filesystem::create_directories(torrentPath);
+  auto fileNum = 0;
+  for (const auto& t : torrentSegments) {
+    fileNum++;
+    auto filename = torrentPath + to_string(fileNum);
+    io::save(t, filename);
+  }
+
+  auto manifestPath = dirPath + "manifests/";
+  boost::filesystem::create_directory(manifestPath);
+  for (const auto& m : manifests) {
+    fs::path filename = manifestPath + m.file_name() + to_string(m.submanifest_number());
+    boost::filesystem::create_directory(filename.parent_path());
+    io::save(m, filename.string());
+  }
+  // Initialize manager
+  TestTorrentManager manager("/NTORRENT/foo/torrent-file/sha256digest=a8a2e98cd943d895b8c4b12a208343bcf9344ce85a6376dc6f5754fe8f4a573e",
+                             filePath);
+  manager.Initialize();
+
+  // Set the file state
+  std::vector<bool> v1 = {true};
+  manager.setFileState(manifests[0].getFullName(), make_shared<fs::fstream>(), v1);
+
+  std::vector<bool> v2 = {false, true, true, false, false, false};
+  manager.setFileState(manifests[1].getFullName(), make_shared<fs::fstream>(), v2);
+
+  std::vector<bool> v3 = {true, false, false, false, false, false};
+  manager.setFileState(manifests[2].getFullName(), make_shared<fs::fstream>(), v3);
+
+  manager.downloadTorrentFile(filePath + "torrent_files/",
+                                 [&manifests](const std::vector<ndn::Name>& vec) {
+                                   BOOST_CHECK_EQUAL(vec[0].toUri(),
+                                                     manifests[1].catalog()[0].toUri());
+                                   BOOST_CHECK_EQUAL(vec[1].toUri(),
+                                                     manifests[1].catalog()[3].toUri());
+                                   BOOST_CHECK_EQUAL(vec[2].toUri(),
+                                                     manifests[1].catalog()[4].toUri());
+                                   BOOST_CHECK_EQUAL(vec[3].toUri(),
+                                                     manifests[1].catalog()[5].toUri());
+                                   BOOST_CHECK_EQUAL(vec[4].toUri(),
+                                                     manifests[2].catalog()[1].toUri());
+                                   BOOST_CHECK_EQUAL(vec[5].toUri(),
+                                                     manifests[2].catalog()[2].toUri());
+                                   BOOST_CHECK_EQUAL(vec[6].toUri(),
+                                                     manifests[2].catalog()[3].toUri());
+                                   BOOST_CHECK_EQUAL(vec[7].toUri(),
+                                                     manifests[2].catalog()[4].toUri());
+                                   BOOST_CHECK_EQUAL(vec[8].toUri(),
+                                                     manifests[2].catalog()[5].toUri());
+                                 },
+                                 [](const ndn::Name& name, const std::string& reason) {
+                                   BOOST_FAIL("Unexpected failure");
+                                 });
+
+  fs::remove_all(filePath);
+  fs::remove_all(".appdata");
+}
+
+
+BOOST_AUTO_TEST_CASE(TestFindManifestSegmentToDownload1)
+{
+  std::string filePath = ".appdata/foo/";
+  TestTorrentManager manager("NTORRENT/test/sha256digest",
+                             filePath, face);
+  manager.Initialize();
+
+  Name n1(Name("NTORRENT/test/file0"));
+  n1.appendSequenceNumber(0);
+
+  Name n2(Name("NTORRENT/test/file0"));
+  n2.appendSequenceNumber(1);
+
+  Name n3(Name("NTORRENT/test/file0"));
+  n3.appendSequenceNumber(2);
+
+  Name n4(Name("NTORRENT/test/file0"));
+  n4.appendSequenceNumber(3);
+
+  Name n5(Name("NTORRENT/test/file0"));
+  n5.appendSequenceNumber(4);
+
+  Name n6(Name("NTORRENT/test1/file0"));
+  n6.appendSequenceNumber(0);
+
+  Name n7(Name("NTORRENT/test1/file0"));
+  n7.appendSequenceNumber(1);
+
+  // In theory, this may not be correct, but here let's suck it up for the sake
+  // of testing the function correctness
+  Name n8(Name("NTORRENT/test1/file2"));
+  n8.appendSequenceNumber(0);
+
+  Name n9(Name("NTORRENT/test1/file2"));
+  n9.appendSequenceNumber(1);
+
+  FileManifest f1(n1, 50, Name("NTORRENT/test"), {}, make_shared<Name>(n2));
+  manager.pushFileManifestSegment(f1);
+
+  FileManifest f2(n2, 50, Name("NTORRENT/test"), {}, make_shared<Name>(n3));
+  manager.pushFileManifestSegment(f2);
+
+  FileManifest f3(n3, 50, Name("NTORRENT/test"), {}, make_shared<Name>(n4));
+  manager.pushFileManifestSegment(f3);
+
+  FileManifest f4(n4, 50, Name("NTORRENT/test"), {}, make_shared<Name>(n5));
+  manager.pushFileManifestSegment(f4);
+
+  FileManifest f5(n6, 50, Name("NTORRENT/test2"), {}, make_shared<Name>(n7));
+  manager.pushFileManifestSegment(f5);
+
+  FileManifest f6(n7, 50, Name("NTORRENT/test2"), {}, {});
+  manager.pushFileManifestSegment(f6);
+
+  FileManifest f7(n8, 50, Name("NTORRENT/test3"), {}, make_shared<Name>(n9));
+  manager.pushFileManifestSegment(f7);
+
+  BOOST_CHECK_EQUAL(manager.findManifestSegmentToDownload(Name(n2.toUri() + "/sha256digest"))->toUri(), n5.toUri());
+  BOOST_CHECK_EQUAL(manager.findManifestSegmentToDownload(Name(n8.toUri() + "/sha256digest"))->toUri(), n9.toUri());
+  BOOST_CHECK_EQUAL(manager.findManifestSegmentToDownload(Name(n5.toUri() + "/sha256digest"))->toUri(),
+                                                               Name(n5.toUri() + "/sha256digest").toUri());
+  BOOST_CHECK(!(manager.findManifestSegmentToDownload(Name(n7.toUri() + "/sha256digest"))));
+
+  Name n10(Name("NTORRENT/test1/file1"));
+  n10.appendSequenceNumber(1);
+  n10 = Name(n10.toUri() + "/sha256digest");
+
+  BOOST_CHECK_EQUAL(manager.findManifestSegmentToDownload(n10)->toUri(), n10.toUri());
+
+  fs::remove_all(filePath);
+  fs::remove_all(".appdata");
+}
+
+BOOST_AUTO_TEST_CASE(TestFindManifestSegmentToDownload2)
+{
+  vector<FileManifest> manifests;
+  vector<TorrentFile>  torrentSegments;
+  // for each file, the data packets
+  std::vector<vector<Data>> fileData;
+  std::string filePath = "tests/testdata/temp";
+  // get torrent files and manifests
+  {
+    auto temp = TorrentFile::generate("tests/testdata/foo",
+                                      1024,
+                                      2048,
+                                      8192,
+                                      true);
+    torrentSegments = temp.first;
+    auto temp1      = temp.second;
+    for (const auto& ms : temp1) {
+      manifests.insert(manifests.end(), ms.first.begin(), ms.first.end());
+      fileData.push_back(ms.second);
+    }
+  }
+  // write the torrent segments and manifests to disk
+  std::string dirPath = ".appdata/foo/";
+  boost::filesystem::create_directories(dirPath);
+  std::string torrentPath = dirPath + "torrent_files/";
+  boost::filesystem::create_directories(torrentPath);
+  auto fileNum = 0;
+  for (const auto& t : torrentSegments) {
+    fileNum++;
+    auto filename = torrentPath + to_string(fileNum);
+    io::save(t, filename);
+  }
+
+  auto manifestPath = dirPath + "manifests/";
+  boost::filesystem::create_directory(manifestPath);
+  for (const auto& m : manifests) {
+    fs::path filename = manifestPath + m.file_name() + to_string(m.submanifest_number());
+    boost::filesystem::create_directory(filename.parent_path());
+    io::save(m, filename.string());
+  }
+  // Initialize manager
+  TestTorrentManager manager("/NTORRENT/foo/torrent-file/sha256digest=a8a2e98cd943d895b8c4b12a208343bcf9344ce85a6376dc6f5754fe8f4a573e",
+                             filePath);
+  manager.Initialize();
+
+  // Set the file state
+  std::vector<bool> v1 = {true};
+  manager.setFileState(manifests[0].getFullName(), make_shared<fs::fstream>(), v1);
+
+  std::vector<bool> v2 = {false, true, true, false, false, false};
+  manager.setFileState(manifests[1].getFullName(), make_shared<fs::fstream>(), v2);
+
+  std::vector<bool> v3 = {true, false, false, false, false, false};
+  manager.setFileState(manifests[2].getFullName(), make_shared<fs::fstream>(), v3);
+
+  manager.download_file_manifest(manifests[0].getFullName(), filePath + "manifests",
+                                [&manifests](const std::vector<ndn::Name>& vec) {
+                                      BOOST_CHECK_EQUAL(vec.size(), 0);
+                                },
+                                [](const ndn::Name& name, const std::string& reason) {
+                                  BOOST_FAIL("Unexpected failure");
+                                });
+
+  manager.download_file_manifest(manifests[1].getFullName(), filePath + "manifests",
+                                 [&manifests](const std::vector<ndn::Name>& vec) {
+                                   BOOST_CHECK_EQUAL(vec[0].toUri(),
+                                                     manifests[1].catalog()[0].toUri());
+                                   BOOST_CHECK_EQUAL(vec[1].toUri(),
+                                                     manifests[1].catalog()[3].toUri());
+                                   BOOST_CHECK_EQUAL(vec[2].toUri(),
+                                                     manifests[1].catalog()[4].toUri());
+                                   BOOST_CHECK_EQUAL(vec[3].toUri(),
+                                                     manifests[1].catalog()[5].toUri());
+                                 },
+                                 [](const ndn::Name& name, const std::string& reason) {
+                                   BOOST_FAIL("Unexpected failure");
+                                 });
+
+  manager.download_file_manifest(manifests[2].getFullName(), filePath + "manifests",
+                                 [&manifests](const std::vector<ndn::Name>& vec) {
+                                   BOOST_CHECK_EQUAL(vec[0].toUri(),
+                                                     manifests[2].catalog()[1].toUri());
+                                   BOOST_CHECK_EQUAL(vec[1].toUri(),
+                                                     manifests[2].catalog()[2].toUri());
+                                   BOOST_CHECK_EQUAL(vec[2].toUri(),
+                                                     manifests[2].catalog()[3].toUri());
+                                   BOOST_CHECK_EQUAL(vec[3].toUri(),
+                                                     manifests[2].catalog()[4].toUri());
+                                   BOOST_CHECK_EQUAL(vec[4].toUri(),
+                                                     manifests[2].catalog()[5].toUri());
+                                 },
+                                 [](const ndn::Name& name, const std::string& reason) {
+                                   BOOST_FAIL("Unexpected failure");
+                                 });
+  fs::remove_all(filePath);
+  fs::remove_all(".appdata");
+}
+
+BOOST_AUTO_TEST_CASE(TestDataAlreadyDownloaded)
+{
+  vector<FileManifest> manifests;
+  vector<TorrentFile>  torrentSegments;
+  // for each file, the data packets
+  std::vector<vector<Data>> fileData;
+  std::string filePath = "tests/testdata/temp";
+  // get torrent files and manifests
+  {
+    auto temp = TorrentFile::generate("tests/testdata/foo",
+                                      1024,
+                                      2048,
+                                      8192,
+                                      true);
+    torrentSegments = temp.first;
+    auto temp1      = temp.second;
+    for (const auto& ms : temp1) {
+      manifests.insert(manifests.end(), ms.first.begin(), ms.first.end());
+      fileData.push_back(ms.second);
+    }
+  }
+  // write the torrent segments and manifests to disk
+  std::string dirPath = ".appdata/foo/";
+  boost::filesystem::create_directories(dirPath);
+  std::string torrentPath = dirPath + "torrent_files/";
+  boost::filesystem::create_directories(torrentPath);
+  auto fileNum = 0;
+  for (const auto& t : torrentSegments) {
+    fileNum++;
+    auto filename = torrentPath + to_string(fileNum);
+    io::save(t, filename);
+  }
+
+  auto manifestPath = dirPath + "manifests/";
+  boost::filesystem::create_directory(manifestPath);
+  for (const auto& m : manifests) {
+    fs::path filename = manifestPath + m.file_name() + to_string(m.submanifest_number());
+    boost::filesystem::create_directory(filename.parent_path());
+    io::save(m, filename.string());
+  }
+  // Initialize manager
+  TestTorrentManager manager("/NTORRENT/foo/torrent-file/sha256digest=a8a2e98cd943d895b8c4b12a208343bcf9344ce85a6376dc6f5754fe8f4a573e",
+                             filePath);
+  manager.Initialize();
+
+  // Set the file state
+  std::vector<bool> v1 = {true};
+  manager.setFileState(manifests[0].getFullName(), make_shared<fs::fstream>(), v1);
+
+  std::vector<bool> v2 = {false, true, true, false, false, false};
+  manager.setFileState(manifests[1].getFullName(), make_shared<fs::fstream>(), v2);
+
+  std::vector<bool> v3 = {true, false, false, false, false, false};
+  manager.setFileState(manifests[2].getFullName(), make_shared<fs::fstream>(), v3);
+
+  Name p1("NTORRENT/foo/bar1.txt");
+  p1.appendSequenceNumber(0);
+  p1.appendSequenceNumber(0);
+  p1 = Name(p1.toUri() + "/sha256digest");
+
+  BOOST_CHECK(!(manager.dataAlreadyDownloaded(p1)));
+
+  Name p2("NTORRENT/foo/bar.txt");
+  p2.appendSequenceNumber(0);
+  p2.appendSequenceNumber(0);
+  p2 = Name(p2.toUri() + "/sha256digest");
+
+  BOOST_CHECK(manager.dataAlreadyDownloaded(p2));
 }
 
 BOOST_AUTO_TEST_SUITE_END()
@@ -482,7 +901,7 @@
     for (auto& d : data) {
       BOOST_CHECK(manager.writeData(d));
     }
-    // check that the  state is updated appropriately
+    // check that the state is updated appropriately
     auto fileState = manager.fileState(manifest_it->getFullName());
     for (auto s : fileState) {
       BOOST_CHECK(s);