/* * 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 #include "IO/Archive/ArchiveReader.hpp" #include "IO/Archive/ArchiveReaderIterator.hpp" #include "IO/Archive/ArchiveWriter.hpp" #include "IO/AppFolders.hpp" #include #include #include #include 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> 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 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()); } }