Add ArchiveReaderIterator
This commit is contained in:
@@ -59,7 +59,7 @@ namespace OpenVulkano::AR::Playback
|
|||||||
|
|
||||||
int GetNextFrameId()
|
int GetNextFrameId()
|
||||||
{
|
{
|
||||||
std::string name = m_archiveMetadata.GetNextDescription().path;
|
std::string name = m_archiveMetadata.GetDescription().path;
|
||||||
return std::stoi(name.substr(0, name.length() - 5));
|
return std::stoi(name.substr(0, name.length() - 5));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -205,16 +205,6 @@ namespace OpenVulkano
|
|||||||
return archive_read_support_format_all;
|
return archive_read_support_format_all;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t ArchiveReader::ExtractRemaining(std::string_view targetDir)
|
|
||||||
{
|
|
||||||
size_t count = 0;
|
|
||||||
while (ExtractNext(targetDir))
|
|
||||||
{
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t ArchiveReader::ExtractRemaining(const std::filesystem::path &targetDir)
|
size_t ArchiveReader::ExtractRemaining(const std::filesystem::path &targetDir)
|
||||||
{
|
{
|
||||||
size_t count = 0;
|
size_t count = 0;
|
||||||
@@ -258,7 +248,7 @@ namespace OpenVulkano
|
|||||||
return HasNext();
|
return HasNext();
|
||||||
}
|
}
|
||||||
|
|
||||||
FileDescription ArchiveReader::GetNextDescription() const
|
FileDescription ArchiveReader::GetDescription() const
|
||||||
{
|
{
|
||||||
return {
|
return {
|
||||||
LibArchiveHelper::GetFileType(archive_entry_filetype(m_archiveEntry)),
|
LibArchiveHelper::GetFileType(archive_entry_filetype(m_archiveEntry)),
|
||||||
@@ -270,11 +260,21 @@ namespace OpenVulkano
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::pair<FileDescription, Array<char>> ArchiveReader::GetNext()
|
||||||
|
{
|
||||||
|
if (!HasNext()) throw std::runtime_error("Reached end of file!");
|
||||||
|
std::pair<FileDescription, Array<char>> file = {GetDescription(), Array<char>() };
|
||||||
|
file.second = Array<char>(file.first.size);
|
||||||
|
ChkErr(archive_read_data(m_archive, file.second.Data(), file.second.Size()));
|
||||||
|
ReadNextHeader();
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
|
||||||
std::optional<FileDescription> ArchiveReader::GetNextDirectory()
|
std::optional<FileDescription> ArchiveReader::GetNextDirectory()
|
||||||
{
|
{
|
||||||
if (SkipTill(std::filesystem::file_type::directory))
|
if (SkipTill(std::filesystem::file_type::directory))
|
||||||
{
|
{
|
||||||
return GetNextDescription();
|
return GetDescription();
|
||||||
}
|
}
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
@@ -283,7 +283,7 @@ namespace OpenVulkano
|
|||||||
{
|
{
|
||||||
if (SkipTill(std::filesystem::file_type::regular))
|
if (SkipTill(std::filesystem::file_type::regular))
|
||||||
{
|
{
|
||||||
FileDescription fileDescription = GetNextDescription();
|
FileDescription fileDescription = GetDescription();
|
||||||
std::filesystem::path outputFilePath;
|
std::filesystem::path outputFilePath;
|
||||||
if (m_filePathRewrite)
|
if (m_filePathRewrite)
|
||||||
outputFilePath = targetDir / m_filePathRewrite(fileDescription.path);
|
outputFilePath = targetDir / m_filePathRewrite(fileDescription.path);
|
||||||
@@ -317,7 +317,7 @@ namespace OpenVulkano
|
|||||||
{
|
{
|
||||||
if (SkipTill(std::filesystem::file_type::regular))
|
if (SkipTill(std::filesystem::file_type::regular))
|
||||||
{
|
{
|
||||||
std::pair<FileDescription, Array<char>> file = { GetNextDescription(), Array<char>() };
|
std::pair<FileDescription, Array<char>> file = {GetDescription(), Array<char>() };
|
||||||
file.second = Array<char>(file.first.size);
|
file.second = Array<char>(file.first.size);
|
||||||
ChkErr(archive_read_data(m_archive, file.second.Data(), file.second.Size()));
|
ChkErr(archive_read_data(m_archive, file.second.Data(), file.second.Size()));
|
||||||
ReadNextHeader();
|
ReadNextHeader();
|
||||||
@@ -330,7 +330,7 @@ namespace OpenVulkano
|
|||||||
{
|
{
|
||||||
if (SkipTill(std::filesystem::file_type::regular))
|
if (SkipTill(std::filesystem::file_type::regular))
|
||||||
{
|
{
|
||||||
std::pair<FileDescription, std::vector<char>> file = { GetNextDescription(), std::vector<char>() };
|
std::pair<FileDescription, std::vector<char>> file = {GetDescription(), std::vector<char>() };
|
||||||
file.second.resize(file.first.size);
|
file.second.resize(file.first.size);
|
||||||
ChkErr(archive_read_data(m_archive, file.second.data(), file.second.size()));
|
ChkErr(archive_read_data(m_archive, file.second.data(), file.second.size()));
|
||||||
ReadNextHeader();
|
ReadNextHeader();
|
||||||
@@ -359,7 +359,7 @@ namespace OpenVulkano
|
|||||||
{
|
{
|
||||||
if (SkipTill(std::filesystem::file_type::regular))
|
if (SkipTill(std::filesystem::file_type::regular))
|
||||||
{
|
{
|
||||||
FileDescription fileDescription = GetNextDescription();
|
FileDescription fileDescription = GetDescription();
|
||||||
LibArchiveEntryStreamBuffer streamBuffer(m_archive);
|
LibArchiveEntryStreamBuffer streamBuffer(m_archive);
|
||||||
std::istream stream(&streamBuffer);
|
std::istream stream(&streamBuffer);
|
||||||
streamReader(fileDescription, stream);
|
streamReader(fileDescription, stream);
|
||||||
@@ -381,7 +381,7 @@ namespace OpenVulkano
|
|||||||
ChkErr(r);
|
ChkErr(r);
|
||||||
stream.write(static_cast<const char*>(buffer), size);
|
stream.write(static_cast<const char*>(buffer), size);
|
||||||
}
|
}
|
||||||
FileDescription fileDescription = GetNextDescription();
|
FileDescription fileDescription = GetDescription();
|
||||||
ReadNextHeader();
|
ReadNextHeader();
|
||||||
return fileDescription;
|
return fileDescription;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "ArchiveBase.hpp"
|
#include "ArchiveBase.hpp"
|
||||||
|
#include "ArchiveReaderIterator.hpp"
|
||||||
#include "Data/Containers/Array.hpp"
|
#include "Data/Containers/Array.hpp"
|
||||||
#include "ArchiveType.hpp"
|
#include "ArchiveType.hpp"
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
@@ -54,8 +55,6 @@ namespace OpenVulkano
|
|||||||
|
|
||||||
[[nodiscard]] bool IsOpen() const { return m_open; }
|
[[nodiscard]] bool IsOpen() const { return m_open; }
|
||||||
|
|
||||||
[[deprecated]] size_t ExtractRemaining(std::string_view targetDir);
|
|
||||||
|
|
||||||
size_t ExtractRemaining(const std::filesystem::path& targetDir);
|
size_t ExtractRemaining(const std::filesystem::path& targetDir);
|
||||||
|
|
||||||
size_t ExtractRemaining(const std::filesystem::path& targetDir, const std::function<void(const FileDescription&)>& extractionCallback);
|
size_t ExtractRemaining(const std::filesystem::path& targetDir, const std::function<void(const FileDescription&)>& extractionCallback);
|
||||||
@@ -67,29 +66,42 @@ namespace OpenVulkano
|
|||||||
|
|
||||||
bool SkipTill(std::filesystem::file_type type = std::filesystem::file_type::regular);
|
bool SkipTill(std::filesystem::file_type type = std::filesystem::file_type::regular);
|
||||||
|
|
||||||
[[nodiscard]] FileDescription GetNextDescription() const;
|
[[nodiscard]] FileDescription GetDescription() const;
|
||||||
|
|
||||||
std::optional<FileDescription> GetNextDirectory();
|
[[nodiscard]] std::pair<FileDescription, Array<char>> GetNext();
|
||||||
|
|
||||||
|
[[nodiscard]] std::optional<FileDescription> GetNextDirectory();
|
||||||
|
|
||||||
[[deprecated]] std::optional<FileDescription> ExtractNext(std::string_view targetDir) { return ExtractNext(std::filesystem::path(targetDir)); }
|
[[deprecated]] std::optional<FileDescription> ExtractNext(std::string_view targetDir) { return ExtractNext(std::filesystem::path(targetDir)); }
|
||||||
|
|
||||||
std::optional<FileDescription> ExtractNext(const std::filesystem::path& targetDir);
|
[[nodiscard]] std::optional<FileDescription> ExtractNext(const std::filesystem::path& targetDir);
|
||||||
|
|
||||||
std::optional<std::pair<FileDescription, Array<char>>> GetNextFile();
|
[[nodiscard]] std::optional<std::pair<FileDescription, Array<char>>> GetNextFile();
|
||||||
|
|
||||||
std::optional<std::pair<FileDescription, std::vector<char>>> GetNextFileAsVector();
|
[[nodiscard]] std::optional<std::pair<FileDescription, std::vector<char>>> GetNextFileAsVector();
|
||||||
|
|
||||||
std::optional<std::pair<FileDescription, Array<char>>> GetFile(const std::string& path);
|
[[nodiscard]] std::optional<std::pair<FileDescription, Array<char>>> GetFile(const std::string& path);
|
||||||
|
|
||||||
std::optional<FileDescription> StreamNextFile(std::ostream& stream);
|
[[nodiscard]] std::optional<FileDescription> StreamNextFile(std::ostream& stream);
|
||||||
|
|
||||||
bool GetNextFileAsStream(const std::function<void(const FileDescription&, std::istream&)>& streamReader);
|
bool GetNextFileAsStream(const std::function<void(const FileDescription&, std::istream&)>& streamReader);
|
||||||
|
|
||||||
const decltype(m_passwordCallback)& GetPasswordCallback() const { return m_passwordCallback; }
|
[[nodiscard]] const decltype(m_passwordCallback)& GetPasswordCallback() const { return m_passwordCallback; }
|
||||||
|
|
||||||
void SetPathRewriteFunction(const decltype(m_filePathRewrite)& rewriteFunc) { m_filePathRewrite = rewriteFunc; }
|
void SetPathRewriteFunction(const decltype(m_filePathRewrite)& rewriteFunc) { m_filePathRewrite = rewriteFunc; }
|
||||||
|
|
||||||
const decltype(m_filePathRewrite)& GetPathRewriteFunction() const { return m_filePathRewrite; }
|
[[nodiscard]] const decltype(m_filePathRewrite)& GetPathRewriteFunction() const { return m_filePathRewrite; }
|
||||||
|
|
||||||
|
public:
|
||||||
|
using iterator = ArchiveReaderIterator;
|
||||||
|
using const_iterator = ArchiveReaderIterator;
|
||||||
|
|
||||||
|
[[nodiscard]] iterator begin() { return ArchiveReaderIterator(this); }
|
||||||
|
[[nodiscard]] iterator end() { return ArchiveReaderIterator(nullptr); }
|
||||||
|
[[nodiscard]] const_iterator begin() const { return ArchiveReaderIterator(const_cast<ArchiveReader*>(this)); }
|
||||||
|
[[nodiscard]] const_iterator end() const { return ArchiveReaderIterator(nullptr); }
|
||||||
|
[[nodiscard]] const_iterator cbegin() const { return begin(); }
|
||||||
|
[[nodiscard]] const_iterator cend() const { return end(); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void ReadNextHeader();
|
void ReadNextHeader();
|
||||||
|
|||||||
38
openVulkanoCpp/IO/Archive/ArchiveReaderIterator.cpp
Normal file
38
openVulkanoCpp/IO/Archive/ArchiveReaderIterator.cpp
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "ArchiveReaderIterator.hpp"
|
||||||
|
#include "ArchiveReader.hpp"
|
||||||
|
|
||||||
|
namespace OpenVulkano
|
||||||
|
{
|
||||||
|
ArchiveReaderIterator::ArchiveReaderIterator(ArchiveReader* reader) : m_reader(reader)
|
||||||
|
{
|
||||||
|
if (m_reader)
|
||||||
|
{
|
||||||
|
if (m_reader->HasNext()) m_current = m_reader->GetNext();
|
||||||
|
else m_reader = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ArchiveReaderIterator& ArchiveReaderIterator::operator++()
|
||||||
|
{
|
||||||
|
if (m_reader)
|
||||||
|
{
|
||||||
|
if (m_reader->HasNext())
|
||||||
|
{
|
||||||
|
m_current = m_reader->GetNext();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_current.first.Reset();
|
||||||
|
m_current.second.Reset();
|
||||||
|
m_reader = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
}
|
||||||
63
openVulkanoCpp/IO/Archive/ArchiveReaderIterator.hpp
Normal file
63
openVulkanoCpp/IO/Archive/ArchiveReaderIterator.hpp
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
/*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "IO/FileDescription.hpp"
|
||||||
|
#include "Data/Containers/Array.hpp"
|
||||||
|
#include <iterator>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace OpenVulkano
|
||||||
|
{
|
||||||
|
class ArchiveReader;
|
||||||
|
|
||||||
|
class ArchiveReaderIterator
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using iterator_category = std::input_iterator_tag;
|
||||||
|
using value_type = std::pair<FileDescription, Array<char>>;
|
||||||
|
using difference_type = std::ptrdiff_t;
|
||||||
|
using pointer = const value_type*;
|
||||||
|
using reference = const value_type&;
|
||||||
|
|
||||||
|
private:
|
||||||
|
ArchiveReader* m_reader;
|
||||||
|
std::pair<FileDescription, Array<char>> m_current;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit ArchiveReaderIterator(ArchiveReader* reader = nullptr);
|
||||||
|
|
||||||
|
reference operator*() const
|
||||||
|
{
|
||||||
|
return m_current;
|
||||||
|
}
|
||||||
|
|
||||||
|
pointer operator->() const
|
||||||
|
{
|
||||||
|
return &m_current;
|
||||||
|
}
|
||||||
|
|
||||||
|
ArchiveReaderIterator& operator++();
|
||||||
|
|
||||||
|
ArchiveReaderIterator operator++(int)
|
||||||
|
{
|
||||||
|
ArchiveReaderIterator tmp = *this;
|
||||||
|
++(*this);
|
||||||
|
return tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const ArchiveReaderIterator& other) const
|
||||||
|
{
|
||||||
|
return m_reader == other.m_reader;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator!=(const ArchiveReaderIterator& other) const
|
||||||
|
{
|
||||||
|
return !(*this == other);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
253
tests/IO/Archive/ArchiveReaderIterator.cpp
Normal file
253
tests/IO/Archive/ArchiveReaderIterator.cpp
Normal file
@@ -0,0 +1,253 @@
|
|||||||
|
/*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <catch2/catch_all.hpp>
|
||||||
|
|
||||||
|
#include "IO/Archive/ArchiveReader.hpp"
|
||||||
|
#include "IO/Archive/ArchiveReaderIterator.hpp"
|
||||||
|
#include "IO/Archive/ArchiveWriter.hpp"
|
||||||
|
#include "IO/AppFolders.hpp"
|
||||||
|
|
||||||
|
#include <filesystem>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
using namespace OpenVulkano;
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
std::filesystem::path iteratorTestArchiveDir()
|
||||||
|
{
|
||||||
|
return AppFolders::GetAppTempDir();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::filesystem::path iteratorTestArchivePath()
|
||||||
|
{
|
||||||
|
return iteratorTestArchiveDir() / "test_iterator_archive.zip";
|
||||||
|
}
|
||||||
|
|
||||||
|
void makeIteratorTestArchive()
|
||||||
|
{
|
||||||
|
ArchiveWriter writer(iteratorTestArchivePath());
|
||||||
|
|
||||||
|
// Add multiple files for iteration testing
|
||||||
|
std::vector<std::pair<std::string, std::string>> files = {
|
||||||
|
{"file1.txt", "Content of file 1"},
|
||||||
|
{"file2.txt", "Content of file 2"},
|
||||||
|
{"subdir/file3.txt", "Content of file 3"},
|
||||||
|
{"subdir/file4.txt", "Content of file 4"},
|
||||||
|
{"readme.md", "# Test Archive"}
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const auto& [path, content]: files)
|
||||||
|
{
|
||||||
|
FileDescription desc = FileDescription::MkFile(path, content.size());
|
||||||
|
writer.AddFile(desc, content.data());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Iterator Basic Operations", "[ArchiveReaderIterator]")
|
||||||
|
{
|
||||||
|
makeIteratorTestArchive();
|
||||||
|
|
||||||
|
SECTION("Begin and End")
|
||||||
|
{
|
||||||
|
ArchiveReader reader(iteratorTestArchivePath());
|
||||||
|
auto begin_it = reader.begin();
|
||||||
|
auto end_it = reader.end();
|
||||||
|
|
||||||
|
REQUIRE(begin_it != end_it);
|
||||||
|
REQUIRE(end_it == reader.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Dereference Operations")
|
||||||
|
{
|
||||||
|
ArchiveReader reader(iteratorTestArchivePath());
|
||||||
|
auto it = reader.begin();
|
||||||
|
REQUIRE(it != reader.end());
|
||||||
|
|
||||||
|
// Test operator*
|
||||||
|
const auto& desc = *it;
|
||||||
|
REQUIRE(!desc.first.path.empty());
|
||||||
|
REQUIRE(desc.first.size > 0);
|
||||||
|
REQUIRE(desc.first.type == std::filesystem::file_type::regular);
|
||||||
|
|
||||||
|
// Test operator->
|
||||||
|
REQUIRE(it->first.path == desc.first.path);
|
||||||
|
REQUIRE(it->first.size == desc.first.size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Iterator Traversal", "[ArchiveReaderIterator]")
|
||||||
|
{
|
||||||
|
makeIteratorTestArchive();
|
||||||
|
|
||||||
|
SECTION("Pre-increment")
|
||||||
|
{
|
||||||
|
ArchiveReader reader(iteratorTestArchivePath());
|
||||||
|
auto it = reader.begin();
|
||||||
|
auto first_path = it->first.path;
|
||||||
|
|
||||||
|
++it;
|
||||||
|
REQUIRE(it != reader.end());
|
||||||
|
REQUIRE(it->first.path != first_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Post-increment")
|
||||||
|
{
|
||||||
|
ArchiveReader reader(iteratorTestArchivePath());
|
||||||
|
auto it = reader.begin();
|
||||||
|
auto first_path = it->first.path;
|
||||||
|
|
||||||
|
auto old_it = it++;
|
||||||
|
REQUIRE(old_it->first.path == first_path);
|
||||||
|
REQUIRE(it->first.path != first_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Complete Traversal")
|
||||||
|
{
|
||||||
|
ArchiveReader reader(iteratorTestArchivePath());
|
||||||
|
int count = 0;
|
||||||
|
for (auto it = reader.begin(); it != reader.end(); ++it)
|
||||||
|
{
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
REQUIRE(count == 5); // We added 5 files in makeIteratorTestArchive
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Range-based For Loop", "[ArchiveReaderIterator]")
|
||||||
|
{
|
||||||
|
makeIteratorTestArchive();
|
||||||
|
|
||||||
|
SECTION("Basic Range Loop")
|
||||||
|
{
|
||||||
|
ArchiveReader reader(iteratorTestArchivePath());
|
||||||
|
std::vector<std::string> paths;
|
||||||
|
for (const auto& entry: reader)
|
||||||
|
{
|
||||||
|
paths.push_back(entry.first.path);
|
||||||
|
}
|
||||||
|
|
||||||
|
REQUIRE(paths.size() == 5);
|
||||||
|
REQUIRE(std::find(paths.begin(), paths.end(), "file1.txt") != paths.end());
|
||||||
|
REQUIRE(std::find(paths.begin(), paths.end(), "readme.md") != paths.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Const Iterator")
|
||||||
|
{
|
||||||
|
ArchiveReader reader(iteratorTestArchivePath());
|
||||||
|
const ArchiveReader& const_reader = reader;
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
for (const auto& entry: const_reader)
|
||||||
|
{
|
||||||
|
REQUIRE(!entry.first.path.empty());
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
REQUIRE(count == 5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Iterator Edge Cases", "[ArchiveReaderIterator]")
|
||||||
|
{
|
||||||
|
SECTION("Empty Archive")
|
||||||
|
{
|
||||||
|
// Create empty archive
|
||||||
|
std::filesystem::path emptyPath = iteratorTestArchiveDir() / "empty.zip";
|
||||||
|
{
|
||||||
|
ArchiveWriter writer(emptyPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
ArchiveReader reader(emptyPath);
|
||||||
|
REQUIRE(reader.begin() == reader.end());
|
||||||
|
|
||||||
|
int count = 0;
|
||||||
|
for (const auto& entry : reader)
|
||||||
|
{
|
||||||
|
(void)entry;
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
REQUIRE(count == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Iterator Invalidation")
|
||||||
|
{
|
||||||
|
makeIteratorTestArchive();
|
||||||
|
|
||||||
|
ArchiveReader reader(iteratorTestArchivePath());
|
||||||
|
|
||||||
|
auto it = reader.begin();
|
||||||
|
REQUIRE(it != reader.end());
|
||||||
|
|
||||||
|
// Advance to end
|
||||||
|
while (it != reader.end())
|
||||||
|
{
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Further increment should not crash
|
||||||
|
++it;
|
||||||
|
REQUIRE(it == reader.end());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Iterator with STL Algorithms", "[ArchiveReaderIterator]")
|
||||||
|
{
|
||||||
|
makeIteratorTestArchive();
|
||||||
|
|
||||||
|
SECTION("std::distance")
|
||||||
|
{
|
||||||
|
ArchiveReader reader(iteratorTestArchivePath());
|
||||||
|
auto distance = std::distance(reader.begin(), reader.end());
|
||||||
|
REQUIRE(distance == 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("std::count_if")
|
||||||
|
{
|
||||||
|
ArchiveReader reader(iteratorTestArchivePath());
|
||||||
|
auto txt_count = std::count_if(reader.begin(), reader.end(),
|
||||||
|
[](const auto& desc)
|
||||||
|
{ return desc.first.path.find(".txt") != std::string::npos; });
|
||||||
|
REQUIRE(txt_count == 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("std::find_if")
|
||||||
|
{
|
||||||
|
ArchiveReader reader(iteratorTestArchivePath());
|
||||||
|
auto it = std::find_if(reader.begin(), reader.end(),
|
||||||
|
[](const auto& desc)
|
||||||
|
{ return desc.first.path == "readme.md"; });
|
||||||
|
|
||||||
|
REQUIRE(it != reader.end());
|
||||||
|
REQUIRE(it->first.path == "readme.md");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Iterator Copy and Assignment", "[ArchiveReaderIterator]")
|
||||||
|
{
|
||||||
|
makeIteratorTestArchive();
|
||||||
|
|
||||||
|
SECTION("Copy Constructor")
|
||||||
|
{
|
||||||
|
ArchiveReader reader(iteratorTestArchivePath());
|
||||||
|
auto it1 = reader.begin(), it2 = it1;
|
||||||
|
REQUIRE(it1 == it2);
|
||||||
|
REQUIRE(it1->first.path == it2->first.path);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Assignment Operator")
|
||||||
|
{
|
||||||
|
ArchiveReader reader(iteratorTestArchivePath());
|
||||||
|
auto it1 = reader.begin(), it2 = reader.end();
|
||||||
|
it2 = it1;
|
||||||
|
REQUIRE(it1 == it2);
|
||||||
|
REQUIRE(it2 != reader.end());
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user