Merge pull request 'ArchiveReader bugfixes & tests for ArchiveReader/ArchiveWriter' (#144) from archive_reader_bugfix into master

Reviewed-on: https://git.madvoxel.net/OpenVulkano/OpenVulkano/pulls/144
Reviewed-by: Georg Hagen <georg.hagen@madvoxel.com>
This commit is contained in:
Vladyslav_Baranovskyi_EXT
2024-10-14 14:48:40 +02:00
5 changed files with 344 additions and 2 deletions

View File

@@ -57,6 +57,14 @@ namespace OpenVulkano
bool ArchiveReader::Open(const char* archiveFile)
{
if (archiveFile[0] == '\0')
{
if (m_logger)
{
m_logger->error("Unable to open archive file with an empty name!");
}
return false;
}
PrepOpen();
ChkErr(archive_read_open_filename(m_archive, archiveFile, BUFFER_SIZE));
ReadNextHeader();

View File

@@ -88,7 +88,7 @@ namespace OpenVulkano
else if (archiveResult <= ARCHIVE_FAILED) lvl = spdlog::level::level_enum::err;
const char* errorString = archive_error_string(arch);
if (logger) logger->log(lvl, errorString ? errorString : "Unknown error while handling archive");
if (archiveResult == ARCHIVE_FAILED) throw std::runtime_error(errorString);
if (archiveResult == ARCHIVE_FAILED || archiveResult == ARCHIVE_FATAL) throw std::runtime_error(errorString);
return false;
}
}

View File

@@ -19,7 +19,7 @@ namespace OpenVulkano
std::string path;
size_t size;
std::filesystem::perms permissions;
time_t createTime, modTime;
time_t createTime = {}, modTime = {};
static FileDescription MakeDescriptionForFile(const char* path, size_t size)
{

View File

@@ -0,0 +1,154 @@
/*
* 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 <fstream>
#include <filesystem>
#include <string>
#include "IO/Archive/ArchiveReader.hpp"
#include "IO/Archive/ArchiveWriter.hpp"
#include "IO/AppFolders.hpp"
using namespace OpenVulkano;
namespace
{
std::string dummyArchiveDir()
{
return AppFolders::GetAppTempDir().string();
}
std::string dummyArchivePath()
{
return dummyArchiveDir() + "/test_archive.zip";
}
void makeDummyArchive()
{
ArchiveWriter writer(dummyArchivePath().c_str());
FileDescription desc;
desc.path = "stream1.txt";
desc.size = 20;
desc.type = std::filesystem::file_type::regular;
desc.permissions = (std::filesystem::perms) 0644;
{
auto stream = writer.AddFileStream(desc);
stream.write("Streaming data..", 17);
stream.flush();
}
desc.path = "stream2.txt";
desc.size = 23;
desc.type = std::filesystem::file_type::regular;
desc.permissions = (std::filesystem::perms) 0644;
{
auto stream = writer.AddFileStream(desc);
stream.write("Other streaming data..", 22);
stream.flush();
}
}
}
TEST_CASE("Dummy archive creation, default constructor", "[ArchiveReader]")
{
makeDummyArchive();
ArchiveReader reader;
REQUIRE(reader.IsOpen() == false);
reader.Open("");
REQUIRE(reader.IsOpen() == false);
}
TEST_CASE("Open Archive from File", "[ArchiveReader]")
{
{
std::string testArchive = dummyArchivePath();
ArchiveReader reader;
bool opened = reader.Open(testArchive);
REQUIRE(opened == true);
REQUIRE(reader.IsOpen() == true);
REQUIRE(reader.HasNext() == true);
}
{
const char* invalidArchive = "invalid_archive.tar";
ArchiveReader reader;
REQUIRE_THROWS_AS(reader.Open(invalidArchive), std::runtime_error);
}
}
TEST_CASE("Open Archive from Directory with Pattern", "[ArchiveReader]")
{
std::filesystem::path testDir = dummyArchiveDir();
std::string pattern = R"(.*\.zip$)";
{
size_t outSize = 0;
ArchiveReader reader;
bool opened = reader.Open(testDir, pattern, &outSize);
REQUIRE(opened == true);
REQUIRE(reader.IsOpen() == true);
REQUIRE(outSize > 0);
}
{
size_t outSize = 0;
std::string invalidPattern = R"(\.invalid$)";
ArchiveReader reader;
bool opened = reader.Open(testDir, invalidPattern, &outSize);
REQUIRE(opened == false);
REQUIRE(reader.IsOpen() == false);
REQUIRE(outSize == 0);
}
}
TEST_CASE("ExtractNext and Skip Functions", "[ArchiveReader]")
{
std::string testArchive = dummyArchivePath();
ArchiveReader reader(testArchive);
size_t extractedFiles = reader.ExtractRemaining(dummyArchiveDir().c_str());
REQUIRE(extractedFiles > 0);
reader.SkipNext();
REQUIRE(reader.HasNext() == false);
bool foundDirectory = reader.SkipTill(std::filesystem::file_type::directory);
REQUIRE(foundDirectory == false);
REQUIRE(reader.HasNext() == false);
}
TEST_CASE("Get Next File Operations", "[ArchiveReader]")
{
std::string testArchive = dummyArchivePath();
ArchiveReader reader(testArchive);
{
auto nextFile = reader.GetNextFile();
REQUIRE(nextFile.has_value() == true);
REQUIRE(nextFile->first.size > 0);
REQUIRE(nextFile->second.Size() == nextFile->first.size);
}
{
auto nextFile = reader.GetNextFileAsVector();
REQUIRE(nextFile.has_value() == true);
REQUIRE(nextFile->first.size > 0);
REQUIRE(nextFile->second.size() == nextFile->first.size);
}
{
std::ostringstream stream;
auto fileDescription = reader.StreamNextFile(stream);
REQUIRE(fileDescription.has_value() == false);
REQUIRE(stream.str().empty());
}
}

View File

@@ -0,0 +1,180 @@
/*
* 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 <fstream>
#include <filesystem>
#include <string>
#include "IO/Archive/ArchiveReader.hpp"
#include "IO/Archive/ArchiveWriter.hpp"
#include "IO/AppFolders.hpp"
#include "IO/FileDescription.hpp"
using namespace OpenVulkano;
TEST_CASE("AddFile from buffer", "[ArchiveWriter]")
{
auto tempDir = AppFolders::GetAppTempDir();
auto archivePath = tempDir / "test_archive_from_buffer.zip";
{
ArchiveWriter writer(archivePath.string().c_str());
std::string buffer = "Hello";
FileDescription desc;
desc.type = std::filesystem::file_type::regular;
desc.path = "test.txt";
desc.size = buffer.size();
desc.permissions = (std::filesystem::perms) 0644;
REQUIRE(writer.AddFile(desc, buffer.c_str()) == true);
}
ArchiveReader reader(archivePath.string().c_str());
REQUIRE(reader.IsOpen());
REQUIRE(reader.HasNext() == true);
auto [fileDesc, fileData] = reader.GetNextFileAsVector().value();
REQUIRE(fileDesc.path == "test.txt");
REQUIRE(std::string(fileData.begin(), fileData.end()) == "Hello");
}
TEST_CASE("AddFile from multiple buffers", "[ArchiveWriter]")
{
auto tempDir = AppFolders::GetAppTempDir();
auto archivePath = tempDir / "test_archive_from_buffers.zip";
{
ArchiveWriter writer(archivePath.string().c_str());
FileDescription desc;
desc.type = std::filesystem::file_type::regular;
desc.path = "test.txt";
desc.size = 10;
desc.permissions = (std::filesystem::perms) 0644;
const char* buffer1 = "Hello";
const char* buffer2 = "World";
std::vector<std::pair<const void*, size_t>> buffers = { { buffer1, std::strlen(buffer1) },
{ buffer2, std::strlen(buffer2) } };
REQUIRE(writer.AddFile(desc, buffers) == true);
}
ArchiveReader reader(archivePath.string().c_str());
REQUIRE(reader.IsOpen());
REQUIRE(reader.HasNext() == true);
auto [fileDesc, fileData] = reader.GetNextFileAsVector().value();
REQUIRE(fileDesc.path == "test.txt");
REQUIRE(std::string(fileData.begin(), fileData.end()) == "HelloWorld");
}
TEST_CASE("AddFile from file", "[ArchiveWriter]")
{
auto tempDir = AppFolders::GetAppTempDir();
auto testFilePath = tempDir / "test.txt";
auto archivePath = tempDir / "test_archive_from_file.zip";
std::ofstream testFile(testFilePath);
testFile << "Hello from file";
testFile.close();
{
ArchiveWriter writer(archivePath.string().c_str());
REQUIRE(writer.AddFile(testFilePath.string().c_str(), "test.txt") == true);
}
ArchiveReader reader(archivePath.string().c_str());
REQUIRE(reader.IsOpen());
REQUIRE(reader.HasNext() == true);
auto [fileDesc, fileData] = reader.GetNextFileAsVector().value();
REQUIRE(fileDesc.path == "test.txt");
REQUIRE(std::string(fileData.begin(), fileData.end()) == "Hello from file");
}
TEST_CASE("AddFileStream", "[ArchiveWriter]")
{
auto tempDir = AppFolders::GetAppTempDir();
auto archivePath = tempDir / "test_archive_stream.zip";
{
ArchiveWriter writer(archivePath.string().c_str());
std::string buffer = "Streamed data.";
FileDescription desc;
desc.type = std::filesystem::file_type::regular;
desc.path = "stream.txt";
desc.size = buffer.size();
desc.permissions = (std::filesystem::perms) 0644;
ArchiveOStream stream = writer.AddFileStream(desc);
stream << buffer;
stream.Close();
}
ArchiveReader reader(archivePath.string().c_str());
REQUIRE(reader.IsOpen());
REQUIRE(reader.HasNext() == true);
auto [fileDesc, fileData] = reader.GetNextFileAsVector().value();
REQUIRE(fileDesc.path == "stream.txt");
REQUIRE(std::string(fileData.begin(), fileData.end()) == "Streamed data.");
}
TEST_CASE("Compression settings", "[ArchiveWriter]")
{
auto tempDir = AppFolders::GetAppTempDir();
auto archivePath = tempDir / "test_archive_compression.zip";
{
ArchiveConfiguration config(ArchiveType::ZIP, CompressionType::GZIP, 9);
ArchiveWriter writer(archivePath.string().c_str(), config);
std::string buffer = "Hello";
FileDescription desc;
desc.type = std::filesystem::file_type::regular;
desc.path = "compressed.txt";
desc.size = buffer.size();
desc.permissions = (std::filesystem::perms) 0644;
writer.SetShouldCompressFunction([](const FileDescription&) { return true; });
REQUIRE(writer.AddFile(desc, buffer.c_str()) == true);
}
ArchiveReader reader(archivePath.string().c_str());
REQUIRE(reader.IsOpen());
REQUIRE(reader.HasNext() == true);
auto [fileDesc, fileData] = reader.GetNextFileAsVector().value();
REQUIRE(fileDesc.path == "compressed.txt");
REQUIRE(std::string(fileData.begin(), fileData.end()) == "Hello");
}
TEST_CASE("Uncompressed settings", "[ArchiveWriter]")
{
auto tempDir = AppFolders::GetAppTempDir();
auto archivePath = tempDir / "test_archive_uncompressed.zip";
{
ArchiveConfiguration config(ArchiveType::ZIP, CompressionType::NONE, 0);
ArchiveWriter writer(archivePath.string().c_str(), config);
std::string buffer = "Hello";
FileDescription desc;
desc.type = std::filesystem::file_type::regular;
desc.path = "uncompressed.txt";
desc.size = buffer.size();
desc.permissions = (std::filesystem::perms) 0644;
writer.SetShouldCompressFunction([](const FileDescription&) { return false; });
REQUIRE(writer.AddFile(desc, buffer.c_str()) == true);
}
ArchiveReader reader(archivePath.string().c_str());
REQUIRE(reader.IsOpen());
REQUIRE(reader.HasNext() == true);
auto [fileDesc, fileData] = reader.GetNextFileAsVector().value();
REQUIRE(fileDesc.path == "uncompressed.txt");
REQUIRE(std::string(fileData.begin(), fileData.end()) == "Hello");
}