From ff6c747e10848ce5c5846f880dfd649896a5268b Mon Sep 17 00:00:00 2001 From: Paul Cristian Sarbu Date: Tue, 26 Sep 2023 15:29:11 +0200 Subject: ArchiveOps: Move libarchive utilities outside other_tools This way they can be used by 'just serve'. --- test/end-to-end/just-mr/TARGETS | 2 +- test/end-to-end/just-mr/create_test_archives.cpp | 2 +- test/other_tools/utils/TARGETS | 20 +- test/other_tools/utils/archive_usage.test.cpp | 424 ----------------------- test/utils/TARGETS | 3 +- test/utils/archive/TARGETS | 15 + test/utils/archive/archive_usage.test.cpp | 424 +++++++++++++++++++++++ 7 files changed, 445 insertions(+), 445 deletions(-) delete mode 100644 test/other_tools/utils/archive_usage.test.cpp create mode 100644 test/utils/archive/TARGETS create mode 100644 test/utils/archive/archive_usage.test.cpp (limited to 'test') diff --git a/test/end-to-end/just-mr/TARGETS b/test/end-to-end/just-mr/TARGETS index 23ad1062..f8a8d10d 100644 --- a/test/end-to-end/just-mr/TARGETS +++ b/test/end-to-end/just-mr/TARGETS @@ -25,7 +25,7 @@ , "private-deps": [ ["@", "src", "src/buildtool/file_system", "file_system_manager"] , ["@", "src", "src/buildtool/logging", "logging"] - , ["@", "src", "src/other_tools/utils", "archive_ops"] + , ["@", "src", "src/utils/archive", "archive_ops"] , ["@", "src", "src/utils/cpp", "tmp_dir"] ] , "private-ldflags": ["-pthread"] diff --git a/test/end-to-end/just-mr/create_test_archives.cpp b/test/end-to-end/just-mr/create_test_archives.cpp index f7223410..5f50bade 100644 --- a/test/end-to-end/just-mr/create_test_archives.cpp +++ b/test/end-to-end/just-mr/create_test_archives.cpp @@ -17,7 +17,7 @@ #include "src/buildtool/file_system/file_system_manager.hpp" #include "src/buildtool/logging/log_config.hpp" #include "src/buildtool/logging/log_sink_cmdline.hpp" -#include "src/other_tools/utils/archive_ops.hpp" +#include "src/utils/archive/archive_ops.hpp" #include "src/utils/cpp/tmp_dir.hpp" namespace { diff --git a/test/other_tools/utils/TARGETS b/test/other_tools/utils/TARGETS index 11bb619b..b43788f1 100644 --- a/test/other_tools/utils/TARGETS +++ b/test/other_tools/utils/TARGETS @@ -1,17 +1,4 @@ -{ "archive_usage": - { "type": ["@", "rules", "CC/test", "test"] - , "name": ["archive_usage"] - , "srcs": ["archive_usage.test.cpp"] - , "private-deps": - [ ["@", "catch2", "", "catch2"] - , ["", "catch-main"] - , ["@", "src", "src/buildtool/file_system", "file_system_manager"] - , ["@", "src", "", "libarchive"] - , ["@", "src", "src/other_tools/utils", "archive_ops"] - ] - , "stage": ["test", "other_tools", "utils"] - } -, "curl_usage_install": +{ "curl_usage_install": { "type": ["@", "rules", "CC", "binary"] , "tainted": ["test"] , "name": ["curl_usage_install"] @@ -43,8 +30,5 @@ , "stage": ["test", "other_tools", "utils"] } , "TESTS": - { "type": "install" - , "tainted": ["test"] - , "deps": ["archive_usage", "curl_usage", "curl_url"] - } + {"type": "install", "tainted": ["test"], "deps": ["curl_usage", "curl_url"]} } diff --git a/test/other_tools/utils/archive_usage.test.cpp b/test/other_tools/utils/archive_usage.test.cpp deleted file mode 100644 index 09d080c5..00000000 --- a/test/other_tools/utils/archive_usage.test.cpp +++ /dev/null @@ -1,424 +0,0 @@ -// Copyright 2022 Huawei Cloud Computing Technology Co., Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include -#include -#include - -#include "catch2/catch_test_macros.hpp" -#include "catch2/generators/catch_generators_all.hpp" -#include "src/buildtool/file_system/file_system_manager.hpp" -#include "src/other_tools/utils/archive_ops.hpp" - -extern "C" { -#include -#include -} - -namespace { - -using file_t = std::pair; -using filetree_t = std::unordered_map; - -constexpr size_t kBlockSize = 10240; -constexpr int kFilePerm = 0644; -constexpr int kDirectoryPerm = 0755; - -auto const kExpected = filetree_t{{"foo", {"foo", AE_IFREG}}, - {"bar/", {"", AE_IFDIR}}, - {"bar/baz", {"baz", AE_IFREG}}}; - -struct archive_test_info_t { - std::string test_name; - ArchiveType type; - std::string test_dir; - std::string filename; - std::vector tools; - std::string cmd; -}; - -std::vector const kTestScenarios = { - {.test_name = "tar", - .type = ArchiveType::Tar, - .test_dir = "test_tar", - .filename = "test.tar", - .tools = {"tar"}, - .cmd = "/usr/bin/tar xf"}, - {.test_name = "tar.gz", - .type = ArchiveType::TarGz, - .test_dir = "test_tar_gz", - .filename = "test.tar.gz", - .tools = {"tar", "gzip"}, - .cmd = "/usr/bin/tar xzf"}, - {.test_name = "tar.bz2", - .type = ArchiveType::TarBz2, - .test_dir = "test_tar_bz2", - .filename = "test.tar.bz2", - .tools = {"tar", "bzip2"}, - .cmd = "/usr/bin/tar xjf"}, - {.test_name = "tar.xz", - .type = ArchiveType::TarXz, - .test_dir = "test_tar_xz", - .filename = "test.tar.xz", - .tools = {"tar", "xz"}, - .cmd = "/usr/bin/tar xJf"}, - {.test_name = "tar.lz", - .type = ArchiveType::TarLz, - .test_dir = "test_tar_lz", - .filename = "test.tar.lz", - .tools = {"tar", "lzip"}, - .cmd = "/usr/bin/tar --lzip -x -f"}, - {.test_name = "tar.lzma", - .type = ArchiveType::TarLzma, - .test_dir = "test_tar_lzma", - .filename = "test.tar.lzma", - .tools = {"tar", "lzma"}, - .cmd = "/usr/bin/tar --lzma -x -f"}, - {.test_name = "zip", - .type = ArchiveType::Zip, - .test_dir = "test_zip", - .filename = "test.zip", - .tools = {"unzip"}, - .cmd = "/usr/bin/unzip"}, - {.test_name = "7zip", - .type = ArchiveType::_7Zip, - .test_dir = "test_7zip", - .filename = "test.7z", - .tools = {"7z"}, // 7z comes with its own lzma-type compression - .cmd = "/usr/bin/7z x"}}; - -[[nodiscard]] auto read_archive(archive* a, std::string const& path) - -> filetree_t { - filetree_t result{}; - - REQUIRE(archive_read_open_filename(a, path.c_str(), kBlockSize) == - ARCHIVE_OK); - - archive_entry* entry{}; - while (archive_read_next_header(a, &entry) == ARCHIVE_OK) { - auto size = archive_entry_size(entry); - auto buf = std::string(static_cast(size), '\0'); - REQUIRE(archive_read_data(a, buf.data(), buf.size()) == - static_cast(buf.size())); - result.emplace(archive_entry_pathname(entry), - file_t{buf, archive_entry_filetype(entry)}); - } - REQUIRE(archive_read_close(a) == ARCHIVE_OK); - - return result; -} - -void write_archive(archive* a, - std::string const& path, - filetree_t const& files) { - REQUIRE(archive_write_open_filename(a, path.c_str()) == ARCHIVE_OK); - - archive_entry* entry = archive_entry_new(); - for (auto const& [path, file] : files) { - auto const& [content, type] = file; - archive_entry_set_pathname(entry, path.c_str()); - archive_entry_set_filetype(entry, type); - if (type == AE_IFREG) { - auto buf = std::filesystem::path{path}.filename().string(); - archive_entry_set_perm(entry, kFilePerm); - archive_entry_set_size(entry, static_cast(buf.size())); - REQUIRE(archive_write_header(a, entry) == ARCHIVE_OK); - REQUIRE(archive_write_data(a, buf.data(), buf.size()) == - static_cast(buf.size())); - } - else { - archive_entry_set_perm(entry, kDirectoryPerm); - archive_entry_set_size(entry, 0); - REQUIRE(archive_write_header(a, entry) == ARCHIVE_OK); - } - entry = archive_entry_clear(entry); - } - archive_entry_free(entry); - REQUIRE(archive_write_close(a) == ARCHIVE_OK); -} - -void extract_archive(std::string const& path) { - auto* a = archive_read_new(); - REQUIRE(a != nullptr); - REQUIRE(archive_read_support_format_tar(a) == ARCHIVE_OK); - REQUIRE(archive_read_support_format_zip(a) == ARCHIVE_OK); - REQUIRE(archive_read_support_format_7zip(a) == ARCHIVE_OK); - REQUIRE(archive_read_support_filter_gzip(a) == ARCHIVE_OK); - REQUIRE(archive_read_support_filter_bzip2(a) == ARCHIVE_OK); - REQUIRE(archive_read_support_filter_xz(a) == ARCHIVE_OK); - REQUIRE(archive_read_support_filter_lzip(a) == ARCHIVE_OK); - REQUIRE(archive_read_support_filter_lzma(a) == ARCHIVE_OK); - REQUIRE(archive_read_open_filename(a, path.c_str(), kBlockSize) == - ARCHIVE_OK); - - auto* out = archive_write_disk_new(); - REQUIRE(out != nullptr); - archive_entry* entry{}; - int r{}; - while ((r = archive_read_next_header(a, &entry)) == ARCHIVE_OK) { - REQUIRE(archive_write_header(out, entry) == ARCHIVE_OK); - if (archive_entry_size(entry) > 0) { - void const* buf{}; - size_t size{}; - int64_t offset{}; - int r2{}; - while ((r2 = archive_read_data_block(a, &buf, &size, &offset)) == - ARCHIVE_OK) { - REQUIRE(archive_write_data_block(out, buf, size, offset) == - ARCHIVE_OK); - } - REQUIRE(r2 == ARCHIVE_EOF); - REQUIRE(archive_write_finish_entry(out) == ARCHIVE_OK); - } - } - REQUIRE(r == ARCHIVE_EOF); - REQUIRE(archive_read_close(a) == ARCHIVE_OK); - REQUIRE(archive_read_free(a) == ARCHIVE_OK); - REQUIRE(archive_write_close(out) == ARCHIVE_OK); - REQUIRE(archive_write_free(out) == ARCHIVE_OK); -} - -void compare_extracted( - std::filesystem::path const& extract_dir = ".") noexcept { - for (auto const& [path, file] : kExpected) { - auto const& [content, type] = file; - switch (type) { - case AE_IFREG: { - REQUIRE(FileSystemManager::IsFile(extract_dir / path)); - auto data = FileSystemManager::ReadFile(extract_dir / path); - REQUIRE(data); - CHECK(*data == content); - } break; - case AE_IFDIR: - CHECK(FileSystemManager::IsDirectory(extract_dir / path)); - break; - default: - CHECK(false); - } - } -} - -void create_files(std::filesystem::path const& destDir = ".") noexcept { - for (auto const& [path, file] : kExpected) { - auto const& [content, type] = file; - switch (type) { - case AE_IFREG: { - CHECK(FileSystemManager::WriteFile(content, destDir / path)); - } break; - case AE_IFDIR: - CHECK(FileSystemManager::CreateDirectory(destDir / path)); - break; - default: - CHECK(false); - } - } -} - -void enable_write_format_and_filter(archive* aw, ArchiveType type) { - switch (type) { - case ArchiveType::Zip: { - REQUIRE(archive_write_set_format_zip(aw) == ARCHIVE_OK); - } break; - case ArchiveType::_7Zip: { - REQUIRE(archive_write_set_format_7zip(aw) == ARCHIVE_OK); - } break; - case ArchiveType::Tar: { - REQUIRE(archive_write_set_format_pax_restricted(aw) == ARCHIVE_OK); - } break; - case ArchiveType::TarGz: { - REQUIRE(archive_write_set_format_pax_restricted(aw) == ARCHIVE_OK); - REQUIRE(archive_write_add_filter_gzip(aw) == ARCHIVE_OK); - } break; - case ArchiveType::TarBz2: { - REQUIRE(archive_write_set_format_pax_restricted(aw) == ARCHIVE_OK); - REQUIRE(archive_write_add_filter_bzip2(aw) == ARCHIVE_OK); - } break; - case ArchiveType::TarXz: { - REQUIRE(archive_write_set_format_pax_restricted(aw) == ARCHIVE_OK); - REQUIRE(archive_write_add_filter_xz(aw) == ARCHIVE_OK); - } break; - case ArchiveType::TarLz: { - REQUIRE(archive_write_set_format_pax_restricted(aw) == ARCHIVE_OK); - REQUIRE(archive_write_add_filter_lzip(aw) == ARCHIVE_OK); - } break; - case ArchiveType::TarLzma: { - REQUIRE(archive_write_set_format_pax_restricted(aw) == ARCHIVE_OK); - REQUIRE(archive_write_add_filter_lzma(aw) == ARCHIVE_OK); - } break; - case ArchiveType::ZipAuto: - case ArchiveType::TarAuto: - return; // unused - } -} - -void enable_read_format_and_filter(archive* ar, ArchiveType type) { - switch (type) { - case ArchiveType::Zip: { - REQUIRE(archive_read_support_format_zip(ar) == ARCHIVE_OK); - } break; - case ArchiveType::_7Zip: { - REQUIRE(archive_read_support_format_7zip(ar) == ARCHIVE_OK); - } break; - case ArchiveType::Tar: { - REQUIRE(archive_read_support_format_tar(ar) == ARCHIVE_OK); - } break; - case ArchiveType::TarGz: { - REQUIRE(archive_read_support_format_tar(ar) == ARCHIVE_OK); - REQUIRE(archive_read_support_filter_gzip(ar) == ARCHIVE_OK); - } break; - case ArchiveType::TarBz2: { - REQUIRE(archive_read_support_format_tar(ar) == ARCHIVE_OK); - REQUIRE(archive_read_support_filter_bzip2(ar) == ARCHIVE_OK); - } break; - case ArchiveType::TarXz: { - REQUIRE(archive_read_support_format_tar(ar) == ARCHIVE_OK); - REQUIRE(archive_read_support_filter_xz(ar) == ARCHIVE_OK); - } break; - case ArchiveType::TarLz: { - REQUIRE(archive_read_support_format_tar(ar) == ARCHIVE_OK); - REQUIRE(archive_read_support_filter_lzip(ar) == ARCHIVE_OK); - } break; - case ArchiveType::TarLzma: { - REQUIRE(archive_read_support_format_tar(ar) == ARCHIVE_OK); - REQUIRE(archive_read_support_filter_lzma(ar) == ARCHIVE_OK); - } break; - case ArchiveType::ZipAuto: - case ArchiveType::TarAuto: - return; // unused - } -} - -} // namespace - -TEST_CASE("Archive read context", "[archive_context]") { - auto* a = archive_read_new(); - REQUIRE(a != nullptr); - CHECK(archive_read_free(a) == ARCHIVE_OK); -} - -TEST_CASE("Archive write context", "[archive_context]") { - auto* a = archive_write_new(); - REQUIRE(a != nullptr); - CHECK(archive_write_free(a) == ARCHIVE_OK); -} - -TEST_CASE("Archive write disk context", "[archive_context]") { - auto* a = archive_write_disk_new(); - REQUIRE(a != nullptr); - CHECK(archive_read_free(a) == ARCHIVE_OK); -} - -TEST_CASE("Read-write archives", "[archive_read_write]") { - // get the scenario - auto test_index = - GENERATE(Catch::Generators::range(0, kTestScenarios.size())); - auto const& scenario = kTestScenarios[test_index]; - - // perform the test - REQUIRE(FileSystemManager::RemoveDirectory(scenario.test_dir, - /*recursively=*/true)); - REQUIRE(FileSystemManager::CreateDirectory(scenario.test_dir)); - auto anchor = FileSystemManager::ChangeDirectory(scenario.test_dir); - - SECTION(std::string("Write ") + scenario.test_name) { - auto* out = archive_write_new(); - REQUIRE(out != nullptr); - enable_write_format_and_filter(out, scenario.type); - write_archive(out, scenario.filename, kExpected); - REQUIRE(archive_write_free(out) == ARCHIVE_OK); - - SECTION(std::string("Read ") + scenario.test_name) { - auto* in = archive_read_new(); - REQUIRE(in != nullptr); - enable_read_format_and_filter(in, scenario.type); - CHECK(read_archive(in, scenario.filename) == kExpected); - REQUIRE(archive_read_free(in) == ARCHIVE_OK); - } - - SECTION(std::string("Extract ") + scenario.test_name + " to disk") { - extract_archive(scenario.filename); - compare_extracted(); - } - - bool tools_exist{true}; - for (auto const& tool : scenario.tools) { - tools_exist &= FileSystemManager::IsExecutable( - std::string("/usr/bin/") + tool); - } - - if (tools_exist) { - SECTION("Extract via system tools") { - REQUIRE( - system((scenario.cmd + " " + scenario.filename).c_str()) == - 0); - compare_extracted(); - } - } - } -} - -TEST_CASE("ArchiveOps", "[archive_ops]") { - // get the scenario - auto test_index = - GENERATE(Catch::Generators::range(0, kTestScenarios.size())); - auto const& scenario = kTestScenarios[test_index]; - - // perform the test - std::optional res{std::nullopt}; - - SECTION(std::string("Write ") + scenario.test_name) { - REQUIRE(FileSystemManager::RemoveDirectory(scenario.test_dir, - /*recursively=*/true)); - REQUIRE(FileSystemManager::CreateDirectory(scenario.test_dir)); - - create_files(scenario.test_dir); - - res = ArchiveOps::CreateArchive( - scenario.type, scenario.filename, scenario.test_dir, "."); - if (res != std::nullopt) { - FAIL(*res); - } - - SECTION(std::string("Extract ") + scenario.test_name + " to disk") { - REQUIRE(FileSystemManager::RemoveDirectory(scenario.test_dir, - /*recursively=*/true)); - REQUIRE(FileSystemManager::CreateDirectory(scenario.test_dir)); - res = ArchiveOps::ExtractArchive( - scenario.type, scenario.filename, "."); - if (res != std::nullopt) { - FAIL(*res); - } - compare_extracted(scenario.test_dir); - } - - bool tools_exist{true}; - for (auto const& tool : scenario.tools) { - tools_exist &= FileSystemManager::IsExecutable( - std::string("/usr/bin/") + tool); - } - if (tools_exist) { - SECTION("Extract via system tools") { - REQUIRE( - FileSystemManager::RemoveDirectory(scenario.test_dir, - /*recursively=*/true)); - REQUIRE(FileSystemManager::CreateDirectory(scenario.test_dir)); - - REQUIRE( - system((scenario.cmd + " " + scenario.filename).c_str()) == - 0); - compare_extracted(scenario.test_dir); - } - } - } -} diff --git a/test/utils/TARGETS b/test/utils/TARGETS index 2a6f99d0..94fb936f 100644 --- a/test/utils/TARGETS +++ b/test/utils/TARGETS @@ -92,6 +92,7 @@ , "TESTS": { "type": "install" , "tainted": ["test"] - , "dirs": [[["./", "cpp", "TESTS"], "cpp"]] + , "dirs": + [[["./", "cpp", "TESTS"], "cpp"], [["./", "archive", "TESTS"], "archive"]] } } diff --git a/test/utils/archive/TARGETS b/test/utils/archive/TARGETS new file mode 100644 index 00000000..15b849c5 --- /dev/null +++ b/test/utils/archive/TARGETS @@ -0,0 +1,15 @@ +{ "archive_usage": + { "type": ["@", "rules", "CC/test", "test"] + , "name": ["archive_usage"] + , "srcs": ["archive_usage.test.cpp"] + , "private-deps": + [ ["@", "catch2", "", "catch2"] + , ["", "catch-main"] + , ["@", "src", "src/buildtool/file_system", "file_system_manager"] + , ["@", "src", "", "libarchive"] + , ["@", "src", "src/utils/archive", "archive_ops"] + ] + , "stage": ["test", "utils", "archive"] + } +, "TESTS": {"type": "install", "tainted": ["test"], "deps": ["archive_usage"]} +} diff --git a/test/utils/archive/archive_usage.test.cpp b/test/utils/archive/archive_usage.test.cpp new file mode 100644 index 00000000..6a8eb751 --- /dev/null +++ b/test/utils/archive/archive_usage.test.cpp @@ -0,0 +1,424 @@ +// Copyright 2022 Huawei Cloud Computing Technology Co., Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include + +#include "catch2/catch_test_macros.hpp" +#include "catch2/generators/catch_generators_all.hpp" +#include "src/buildtool/file_system/file_system_manager.hpp" +#include "src/utils/archive/archive_ops.hpp" + +extern "C" { +#include +#include +} + +namespace { + +using file_t = std::pair; +using filetree_t = std::unordered_map; + +constexpr size_t kBlockSize = 10240; +constexpr int kFilePerm = 0644; +constexpr int kDirectoryPerm = 0755; + +auto const kExpected = filetree_t{{"foo", {"foo", AE_IFREG}}, + {"bar/", {"", AE_IFDIR}}, + {"bar/baz", {"baz", AE_IFREG}}}; + +struct archive_test_info_t { + std::string test_name; + ArchiveType type; + std::string test_dir; + std::string filename; + std::vector tools; + std::string cmd; +}; + +std::vector const kTestScenarios = { + {.test_name = "tar", + .type = ArchiveType::Tar, + .test_dir = "test_tar", + .filename = "test.tar", + .tools = {"tar"}, + .cmd = "/usr/bin/tar xf"}, + {.test_name = "tar.gz", + .type = ArchiveType::TarGz, + .test_dir = "test_tar_gz", + .filename = "test.tar.gz", + .tools = {"tar", "gzip"}, + .cmd = "/usr/bin/tar xzf"}, + {.test_name = "tar.bz2", + .type = ArchiveType::TarBz2, + .test_dir = "test_tar_bz2", + .filename = "test.tar.bz2", + .tools = {"tar", "bzip2"}, + .cmd = "/usr/bin/tar xjf"}, + {.test_name = "tar.xz", + .type = ArchiveType::TarXz, + .test_dir = "test_tar_xz", + .filename = "test.tar.xz", + .tools = {"tar", "xz"}, + .cmd = "/usr/bin/tar xJf"}, + {.test_name = "tar.lz", + .type = ArchiveType::TarLz, + .test_dir = "test_tar_lz", + .filename = "test.tar.lz", + .tools = {"tar", "lzip"}, + .cmd = "/usr/bin/tar --lzip -x -f"}, + {.test_name = "tar.lzma", + .type = ArchiveType::TarLzma, + .test_dir = "test_tar_lzma", + .filename = "test.tar.lzma", + .tools = {"tar", "lzma"}, + .cmd = "/usr/bin/tar --lzma -x -f"}, + {.test_name = "zip", + .type = ArchiveType::Zip, + .test_dir = "test_zip", + .filename = "test.zip", + .tools = {"unzip"}, + .cmd = "/usr/bin/unzip"}, + {.test_name = "7zip", + .type = ArchiveType::_7Zip, + .test_dir = "test_7zip", + .filename = "test.7z", + .tools = {"7z"}, // 7z comes with its own lzma-type compression + .cmd = "/usr/bin/7z x"}}; + +[[nodiscard]] auto read_archive(archive* a, std::string const& path) + -> filetree_t { + filetree_t result{}; + + REQUIRE(archive_read_open_filename(a, path.c_str(), kBlockSize) == + ARCHIVE_OK); + + archive_entry* entry{}; + while (archive_read_next_header(a, &entry) == ARCHIVE_OK) { + auto size = archive_entry_size(entry); + auto buf = std::string(static_cast(size), '\0'); + REQUIRE(archive_read_data(a, buf.data(), buf.size()) == + static_cast(buf.size())); + result.emplace(archive_entry_pathname(entry), + file_t{buf, archive_entry_filetype(entry)}); + } + REQUIRE(archive_read_close(a) == ARCHIVE_OK); + + return result; +} + +void write_archive(archive* a, + std::string const& path, + filetree_t const& files) { + REQUIRE(archive_write_open_filename(a, path.c_str()) == ARCHIVE_OK); + + archive_entry* entry = archive_entry_new(); + for (auto const& [path, file] : files) { + auto const& [content, type] = file; + archive_entry_set_pathname(entry, path.c_str()); + archive_entry_set_filetype(entry, type); + if (type == AE_IFREG) { + auto buf = std::filesystem::path{path}.filename().string(); + archive_entry_set_perm(entry, kFilePerm); + archive_entry_set_size(entry, static_cast(buf.size())); + REQUIRE(archive_write_header(a, entry) == ARCHIVE_OK); + REQUIRE(archive_write_data(a, buf.data(), buf.size()) == + static_cast(buf.size())); + } + else { + archive_entry_set_perm(entry, kDirectoryPerm); + archive_entry_set_size(entry, 0); + REQUIRE(archive_write_header(a, entry) == ARCHIVE_OK); + } + entry = archive_entry_clear(entry); + } + archive_entry_free(entry); + REQUIRE(archive_write_close(a) == ARCHIVE_OK); +} + +void extract_archive(std::string const& path) { + auto* a = archive_read_new(); + REQUIRE(a != nullptr); + REQUIRE(archive_read_support_format_tar(a) == ARCHIVE_OK); + REQUIRE(archive_read_support_format_zip(a) == ARCHIVE_OK); + REQUIRE(archive_read_support_format_7zip(a) == ARCHIVE_OK); + REQUIRE(archive_read_support_filter_gzip(a) == ARCHIVE_OK); + REQUIRE(archive_read_support_filter_bzip2(a) == ARCHIVE_OK); + REQUIRE(archive_read_support_filter_xz(a) == ARCHIVE_OK); + REQUIRE(archive_read_support_filter_lzip(a) == ARCHIVE_OK); + REQUIRE(archive_read_support_filter_lzma(a) == ARCHIVE_OK); + REQUIRE(archive_read_open_filename(a, path.c_str(), kBlockSize) == + ARCHIVE_OK); + + auto* out = archive_write_disk_new(); + REQUIRE(out != nullptr); + archive_entry* entry{}; + int r{}; + while ((r = archive_read_next_header(a, &entry)) == ARCHIVE_OK) { + REQUIRE(archive_write_header(out, entry) == ARCHIVE_OK); + if (archive_entry_size(entry) > 0) { + void const* buf{}; + size_t size{}; + int64_t offset{}; + int r2{}; + while ((r2 = archive_read_data_block(a, &buf, &size, &offset)) == + ARCHIVE_OK) { + REQUIRE(archive_write_data_block(out, buf, size, offset) == + ARCHIVE_OK); + } + REQUIRE(r2 == ARCHIVE_EOF); + REQUIRE(archive_write_finish_entry(out) == ARCHIVE_OK); + } + } + REQUIRE(r == ARCHIVE_EOF); + REQUIRE(archive_read_close(a) == ARCHIVE_OK); + REQUIRE(archive_read_free(a) == ARCHIVE_OK); + REQUIRE(archive_write_close(out) == ARCHIVE_OK); + REQUIRE(archive_write_free(out) == ARCHIVE_OK); +} + +void compare_extracted( + std::filesystem::path const& extract_dir = ".") noexcept { + for (auto const& [path, file] : kExpected) { + auto const& [content, type] = file; + switch (type) { + case AE_IFREG: { + REQUIRE(FileSystemManager::IsFile(extract_dir / path)); + auto data = FileSystemManager::ReadFile(extract_dir / path); + REQUIRE(data); + CHECK(*data == content); + } break; + case AE_IFDIR: + CHECK(FileSystemManager::IsDirectory(extract_dir / path)); + break; + default: + CHECK(false); + } + } +} + +void create_files(std::filesystem::path const& destDir = ".") noexcept { + for (auto const& [path, file] : kExpected) { + auto const& [content, type] = file; + switch (type) { + case AE_IFREG: { + CHECK(FileSystemManager::WriteFile(content, destDir / path)); + } break; + case AE_IFDIR: + CHECK(FileSystemManager::CreateDirectory(destDir / path)); + break; + default: + CHECK(false); + } + } +} + +void enable_write_format_and_filter(archive* aw, ArchiveType type) { + switch (type) { + case ArchiveType::Zip: { + REQUIRE(archive_write_set_format_zip(aw) == ARCHIVE_OK); + } break; + case ArchiveType::_7Zip: { + REQUIRE(archive_write_set_format_7zip(aw) == ARCHIVE_OK); + } break; + case ArchiveType::Tar: { + REQUIRE(archive_write_set_format_pax_restricted(aw) == ARCHIVE_OK); + } break; + case ArchiveType::TarGz: { + REQUIRE(archive_write_set_format_pax_restricted(aw) == ARCHIVE_OK); + REQUIRE(archive_write_add_filter_gzip(aw) == ARCHIVE_OK); + } break; + case ArchiveType::TarBz2: { + REQUIRE(archive_write_set_format_pax_restricted(aw) == ARCHIVE_OK); + REQUIRE(archive_write_add_filter_bzip2(aw) == ARCHIVE_OK); + } break; + case ArchiveType::TarXz: { + REQUIRE(archive_write_set_format_pax_restricted(aw) == ARCHIVE_OK); + REQUIRE(archive_write_add_filter_xz(aw) == ARCHIVE_OK); + } break; + case ArchiveType::TarLz: { + REQUIRE(archive_write_set_format_pax_restricted(aw) == ARCHIVE_OK); + REQUIRE(archive_write_add_filter_lzip(aw) == ARCHIVE_OK); + } break; + case ArchiveType::TarLzma: { + REQUIRE(archive_write_set_format_pax_restricted(aw) == ARCHIVE_OK); + REQUIRE(archive_write_add_filter_lzma(aw) == ARCHIVE_OK); + } break; + case ArchiveType::ZipAuto: + case ArchiveType::TarAuto: + return; // unused + } +} + +void enable_read_format_and_filter(archive* ar, ArchiveType type) { + switch (type) { + case ArchiveType::Zip: { + REQUIRE(archive_read_support_format_zip(ar) == ARCHIVE_OK); + } break; + case ArchiveType::_7Zip: { + REQUIRE(archive_read_support_format_7zip(ar) == ARCHIVE_OK); + } break; + case ArchiveType::Tar: { + REQUIRE(archive_read_support_format_tar(ar) == ARCHIVE_OK); + } break; + case ArchiveType::TarGz: { + REQUIRE(archive_read_support_format_tar(ar) == ARCHIVE_OK); + REQUIRE(archive_read_support_filter_gzip(ar) == ARCHIVE_OK); + } break; + case ArchiveType::TarBz2: { + REQUIRE(archive_read_support_format_tar(ar) == ARCHIVE_OK); + REQUIRE(archive_read_support_filter_bzip2(ar) == ARCHIVE_OK); + } break; + case ArchiveType::TarXz: { + REQUIRE(archive_read_support_format_tar(ar) == ARCHIVE_OK); + REQUIRE(archive_read_support_filter_xz(ar) == ARCHIVE_OK); + } break; + case ArchiveType::TarLz: { + REQUIRE(archive_read_support_format_tar(ar) == ARCHIVE_OK); + REQUIRE(archive_read_support_filter_lzip(ar) == ARCHIVE_OK); + } break; + case ArchiveType::TarLzma: { + REQUIRE(archive_read_support_format_tar(ar) == ARCHIVE_OK); + REQUIRE(archive_read_support_filter_lzma(ar) == ARCHIVE_OK); + } break; + case ArchiveType::ZipAuto: + case ArchiveType::TarAuto: + return; // unused + } +} + +} // namespace + +TEST_CASE("Archive read context", "[archive_context]") { + auto* a = archive_read_new(); + REQUIRE(a != nullptr); + CHECK(archive_read_free(a) == ARCHIVE_OK); +} + +TEST_CASE("Archive write context", "[archive_context]") { + auto* a = archive_write_new(); + REQUIRE(a != nullptr); + CHECK(archive_write_free(a) == ARCHIVE_OK); +} + +TEST_CASE("Archive write disk context", "[archive_context]") { + auto* a = archive_write_disk_new(); + REQUIRE(a != nullptr); + CHECK(archive_read_free(a) == ARCHIVE_OK); +} + +TEST_CASE("Read-write archives", "[archive_read_write]") { + // get the scenario + auto test_index = + GENERATE(Catch::Generators::range(0, kTestScenarios.size())); + auto const& scenario = kTestScenarios[test_index]; + + // perform the test + REQUIRE(FileSystemManager::RemoveDirectory(scenario.test_dir, + /*recursively=*/true)); + REQUIRE(FileSystemManager::CreateDirectory(scenario.test_dir)); + auto anchor = FileSystemManager::ChangeDirectory(scenario.test_dir); + + SECTION(std::string("Write ") + scenario.test_name) { + auto* out = archive_write_new(); + REQUIRE(out != nullptr); + enable_write_format_and_filter(out, scenario.type); + write_archive(out, scenario.filename, kExpected); + REQUIRE(archive_write_free(out) == ARCHIVE_OK); + + SECTION(std::string("Read ") + scenario.test_name) { + auto* in = archive_read_new(); + REQUIRE(in != nullptr); + enable_read_format_and_filter(in, scenario.type); + CHECK(read_archive(in, scenario.filename) == kExpected); + REQUIRE(archive_read_free(in) == ARCHIVE_OK); + } + + SECTION(std::string("Extract ") + scenario.test_name + " to disk") { + extract_archive(scenario.filename); + compare_extracted(); + } + + bool tools_exist{true}; + for (auto const& tool : scenario.tools) { + tools_exist &= FileSystemManager::IsExecutable( + std::string("/usr/bin/") + tool); + } + + if (tools_exist) { + SECTION("Extract via system tools") { + REQUIRE( + system((scenario.cmd + " " + scenario.filename).c_str()) == + 0); + compare_extracted(); + } + } + } +} + +TEST_CASE("ArchiveOps", "[archive_ops]") { + // get the scenario + auto test_index = + GENERATE(Catch::Generators::range(0, kTestScenarios.size())); + auto const& scenario = kTestScenarios[test_index]; + + // perform the test + std::optional res{std::nullopt}; + + SECTION(std::string("Write ") + scenario.test_name) { + REQUIRE(FileSystemManager::RemoveDirectory(scenario.test_dir, + /*recursively=*/true)); + REQUIRE(FileSystemManager::CreateDirectory(scenario.test_dir)); + + create_files(scenario.test_dir); + + res = ArchiveOps::CreateArchive( + scenario.type, scenario.filename, scenario.test_dir, "."); + if (res != std::nullopt) { + FAIL(*res); + } + + SECTION(std::string("Extract ") + scenario.test_name + " to disk") { + REQUIRE(FileSystemManager::RemoveDirectory(scenario.test_dir, + /*recursively=*/true)); + REQUIRE(FileSystemManager::CreateDirectory(scenario.test_dir)); + res = ArchiveOps::ExtractArchive( + scenario.type, scenario.filename, "."); + if (res != std::nullopt) { + FAIL(*res); + } + compare_extracted(scenario.test_dir); + } + + bool tools_exist{true}; + for (auto const& tool : scenario.tools) { + tools_exist &= FileSystemManager::IsExecutable( + std::string("/usr/bin/") + tool); + } + if (tools_exist) { + SECTION("Extract via system tools") { + REQUIRE( + FileSystemManager::RemoveDirectory(scenario.test_dir, + /*recursively=*/true)); + REQUIRE(FileSystemManager::CreateDirectory(scenario.test_dir)); + + REQUIRE( + system((scenario.cmd + " " + scenario.filename).c_str()) == + 0); + compare_extracted(scenario.test_dir); + } + } + } +} -- cgit v1.2.3