Add ArchiveReaderIterator

This commit is contained in:
Georg Hagen
2025-05-22 21:50:45 +02:00
parent ee2a579450
commit 3d56c6a410
6 changed files with 397 additions and 31 deletions

View 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());
}
}