mirror of
https://github.com/mhx/dwarfs.git
synced 2025-09-10 13:04:15 -04:00
refactor: factor out helpers to simplify/split tool_main_test.cpp
This commit is contained in:
parent
a8d8e0ad84
commit
b64d186a26
@ -465,7 +465,7 @@ endif()
|
|||||||
add_custom_target(symlinks ALL DEPENDS ${SYMLINKS})
|
add_custom_target(symlinks ALL DEPENDS ${SYMLINKS})
|
||||||
|
|
||||||
if(WITH_TESTS OR WITH_BENCHMARKS OR WITH_FUZZ)
|
if(WITH_TESTS OR WITH_BENCHMARKS OR WITH_FUZZ)
|
||||||
add_library(dwarfs_test_helpers OBJECT
|
add_library(dwarfs_test_helpers
|
||||||
test/test_helpers.cpp
|
test/test_helpers.cpp
|
||||||
test/test_iolayer.cpp
|
test/test_iolayer.cpp
|
||||||
test/loremipsum.cpp
|
test/loremipsum.cpp
|
||||||
@ -477,6 +477,16 @@ if(WITH_TESTS OR WITH_BENCHMARKS OR WITH_FUZZ)
|
|||||||
endif()
|
endif()
|
||||||
target_link_libraries(dwarfs_test_helpers PUBLIC dwarfs_reader dwarfs_writer dwarfs_tool)
|
target_link_libraries(dwarfs_test_helpers PUBLIC dwarfs_reader dwarfs_writer dwarfs_tool)
|
||||||
set_property(TARGET dwarfs_test_helpers PROPERTY CXX_STANDARD ${DWARFS_CXX_STANDARD})
|
set_property(TARGET dwarfs_test_helpers PROPERTY CXX_STANDARD ${DWARFS_CXX_STANDARD})
|
||||||
|
target_compile_definitions(dwarfs_test_helpers
|
||||||
|
PUBLIC TEST_DATA_DIR=\"${CMAKE_SOURCE_DIR}/test\"
|
||||||
|
TOOLS_BIN_DIR=\"${CMAKE_CURRENT_BINARY_DIR}\")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(WITH_TESTS AND WITH_TOOLS)
|
||||||
|
add_library(dwarfs_tool_main_tester test/test_tool_main_tester.cpp)
|
||||||
|
target_link_libraries(dwarfs_tool_main_tester PUBLIC dwarfs_test_helpers GTest::gtest)
|
||||||
|
target_link_libraries(dwarfs_tool_main_tester PRIVATE mkdwarfs_main dwarfsck_main dwarfsextract_main)
|
||||||
|
set_property(TARGET dwarfs_tool_main_tester PROPERTY CXX_STANDARD ${DWARFS_CXX_STANDARD})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(WITH_TESTS)
|
if(WITH_TESTS)
|
||||||
@ -572,7 +582,7 @@ if(WITH_TESTS)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
add_library(dwarfs_test_main OBJECT test/test_main.cpp)
|
add_library(dwarfs_test_main OBJECT test/test_main.cpp)
|
||||||
target_link_libraries(dwarfs_test_main PUBLIC gtest gmock)
|
target_link_libraries(dwarfs_test_main PUBLIC GTest::gtest GTest::gmock)
|
||||||
|
|
||||||
foreach (test ${DWARFS_TESTS})
|
foreach (test ${DWARFS_TESTS})
|
||||||
if(NOT TARGET ${test})
|
if(NOT TARGET ${test})
|
||||||
@ -598,10 +608,6 @@ if(WITH_TESTS)
|
|||||||
target_include_directories(${test} SYSTEM BEFORE PRIVATE ${gmock_include_dirs} ${gtest_include_dirs})
|
target_include_directories(${test} SYSTEM BEFORE PRIVATE ${gmock_include_dirs} ${gtest_include_dirs})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
target_compile_definitions(${test}
|
|
||||||
PRIVATE TEST_DATA_DIR=\"${CMAKE_SOURCE_DIR}/test\"
|
|
||||||
TOOLS_BIN_DIR=\"${CMAKE_CURRENT_BINARY_DIR}\")
|
|
||||||
|
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
target_compile_options(${test} PRIVATE /bigobj)
|
target_compile_options(${test} PRIVATE /bigobj)
|
||||||
endif()
|
endif()
|
||||||
@ -610,7 +616,7 @@ if(WITH_TESTS)
|
|||||||
endforeach()
|
endforeach()
|
||||||
|
|
||||||
if(TARGET tool_main_test)
|
if(TARGET tool_main_test)
|
||||||
target_link_libraries(tool_main_test PRIVATE mkdwarfs_main dwarfsck_main dwarfsextract_main PkgConfig::LIBARCHIVE)
|
target_link_libraries(tool_main_test PRIVATE dwarfs_tool_main_tester PkgConfig::LIBARCHIVE)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(TARGET dwarfs_unit_tests)
|
if(TARGET dwarfs_unit_tests)
|
||||||
|
368
test/test_tool_main_tester.cpp
Normal file
368
test/test_tool_main_tester.cpp
Normal file
@ -0,0 +1,368 @@
|
|||||||
|
/* vim:set ts=2 sw=2 sts=2 et: */
|
||||||
|
/**
|
||||||
|
* \author Marcus Holland-Moritz (github@mhxnet.de)
|
||||||
|
* \copyright Copyright (c) Marcus Holland-Moritz
|
||||||
|
*
|
||||||
|
* This file is part of dwarfs.
|
||||||
|
*
|
||||||
|
* dwarfs is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* dwarfs is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with dwarfs. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <dwarfs/util.h>
|
||||||
|
|
||||||
|
#include <dwarfs_tool_main.h>
|
||||||
|
|
||||||
|
#include "test_tool_main_tester.h"
|
||||||
|
|
||||||
|
namespace dwarfs::test {
|
||||||
|
|
||||||
|
namespace fs = std::filesystem;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
struct locale_setup_helper {
|
||||||
|
locale_setup_helper() { setup_default_locale(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
inline void setup_locale() { static locale_setup_helper helper; }
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
fs::path const test_dir = fs::path(TEST_DATA_DIR).make_preferred();
|
||||||
|
fs::path const audio_data_dir = test_dir / "pcmaudio";
|
||||||
|
fs::path const fits_data_dir = test_dir / "fits";
|
||||||
|
|
||||||
|
std::ostream& operator<<(std::ostream& os, input_mode m) {
|
||||||
|
switch (m) {
|
||||||
|
case input_mode::from_file:
|
||||||
|
os << "from_file";
|
||||||
|
break;
|
||||||
|
case input_mode::from_stdin:
|
||||||
|
os << "from_stdin";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostream& operator<<(std::ostream& os, path_type m) {
|
||||||
|
switch (m) {
|
||||||
|
case path_type::relative:
|
||||||
|
os << "relative";
|
||||||
|
break;
|
||||||
|
case path_type::absolute:
|
||||||
|
os << "absolute";
|
||||||
|
break;
|
||||||
|
case path_type::mixed:
|
||||||
|
os << "mixed";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
|
||||||
|
void tool_main_test::SetUp() {
|
||||||
|
setup_locale();
|
||||||
|
iol = std::make_unique<test::test_iolayer>();
|
||||||
|
}
|
||||||
|
|
||||||
|
void tool_main_test::TearDown() { iol.reset(); }
|
||||||
|
|
||||||
|
tester_common::tester_common(main_ptr_t mp, std::string toolname,
|
||||||
|
std::shared_ptr<test::os_access_mock> pos)
|
||||||
|
: fa{std::make_shared<test::test_file_access>()}
|
||||||
|
, os{std::move(pos)}
|
||||||
|
, iol{std::make_unique<test::test_iolayer>(os, fa)}
|
||||||
|
, main_{mp}
|
||||||
|
, toolname_{std::move(toolname)} {
|
||||||
|
setup_locale();
|
||||||
|
}
|
||||||
|
|
||||||
|
int tester_common::run(std::vector<std::string> args) {
|
||||||
|
args.insert(args.begin(), toolname_);
|
||||||
|
return tool::main_adapter(main_)(args, iol->get());
|
||||||
|
}
|
||||||
|
|
||||||
|
int tester_common::run(std::initializer_list<std::string> args) {
|
||||||
|
return run(std::vector<std::string>(args));
|
||||||
|
}
|
||||||
|
|
||||||
|
int tester_common::run(std::string args) { return run(test::parse_args(args)); }
|
||||||
|
|
||||||
|
mkdwarfs_tester::mkdwarfs_tester(std::shared_ptr<test::os_access_mock> pos)
|
||||||
|
: tester_common(tool::mkdwarfs_main, "mkdwarfs", std::move(pos)) {}
|
||||||
|
|
||||||
|
mkdwarfs_tester::mkdwarfs_tester()
|
||||||
|
: mkdwarfs_tester(test::os_access_mock::create_test_instance()) {}
|
||||||
|
|
||||||
|
mkdwarfs_tester mkdwarfs_tester::create_empty() {
|
||||||
|
return mkdwarfs_tester(std::make_shared<test::os_access_mock>());
|
||||||
|
}
|
||||||
|
|
||||||
|
void mkdwarfs_tester::add_stream_logger(std::ostream& st,
|
||||||
|
logger::level_type level) {
|
||||||
|
lgr = std::make_unique<stream_logger>(std::make_shared<test::test_terminal>(),
|
||||||
|
st, logger_options{.threshold = level});
|
||||||
|
}
|
||||||
|
|
||||||
|
void mkdwarfs_tester::add_root_dir() {
|
||||||
|
os->add("", {1, 040755, 1, 0, 0, 10, 42, 0, 0, 0});
|
||||||
|
}
|
||||||
|
|
||||||
|
void mkdwarfs_tester::add_special_files() {
|
||||||
|
static constexpr file_stat::off_type const size = 10;
|
||||||
|
std::string data(size, 'x');
|
||||||
|
os->add("suid", {1001, 0104755, 1, 0, 0, size, 0, 3333, 2222, 1111}, data);
|
||||||
|
os->add("sgid", {1002, 0102755, 1, 0, 0, size, 0, 0, 0, 0}, data);
|
||||||
|
os->add("sticky", {1003, 0101755, 1, 0, 0, size, 0, 0, 0, 0}, data);
|
||||||
|
os->add("block", {1004, 060666, 1, 0, 0, 0, 77, 0, 0, 0}, std::string{});
|
||||||
|
os->add("sock", {1005, 0140666, 1, 0, 0, 0, 0, 0, 0, 0}, std::string{});
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::pair<fs::path, std::string>>
|
||||||
|
mkdwarfs_tester::add_random_file_tree(random_file_tree_options const& opt) {
|
||||||
|
size_t max_size{128 * static_cast<size_t>(opt.avg_size)};
|
||||||
|
std::mt19937_64 rng{42};
|
||||||
|
std::exponential_distribution<> size_dist{1 / opt.avg_size};
|
||||||
|
std::uniform_int_distribution<> path_comp_size_dist{0, opt.max_name_len};
|
||||||
|
std::uniform_int_distribution<> invalid_dist{0, 1};
|
||||||
|
std::vector<std::pair<fs::path, std::string>> paths;
|
||||||
|
|
||||||
|
auto random_path_component = [&] {
|
||||||
|
auto size = path_comp_size_dist(rng);
|
||||||
|
if (opt.with_invalid_utf8 && invalid_dist(rng) == 0) {
|
||||||
|
return test::create_random_string(size, 96, 255, rng);
|
||||||
|
}
|
||||||
|
return test::create_random_string(size, 'A', 'Z', rng);
|
||||||
|
};
|
||||||
|
|
||||||
|
test::lz_params text_lzp{};
|
||||||
|
test::lz_params binary_lzp{};
|
||||||
|
text_lzp.text_mode = true;
|
||||||
|
binary_lzp.text_mode = false;
|
||||||
|
text_lzp.seed = rng();
|
||||||
|
binary_lzp.seed = rng();
|
||||||
|
test::lz_synthetic_generator text_gen{text_lzp};
|
||||||
|
test::lz_synthetic_generator binary_gen{binary_lzp};
|
||||||
|
|
||||||
|
for (int x = 0; x < opt.dimension; ++x) {
|
||||||
|
fs::path d1{random_path_component() + std::to_string(x)};
|
||||||
|
os->add_dir(d1);
|
||||||
|
|
||||||
|
for (int y = 0; y < opt.dimension; ++y) {
|
||||||
|
fs::path d2{d1 / (random_path_component() + std::to_string(y))};
|
||||||
|
os->add_dir(d2);
|
||||||
|
|
||||||
|
for (int z = 0; z < opt.dimension; ++z) {
|
||||||
|
fs::path f{d2 / (random_path_component() + std::to_string(z))};
|
||||||
|
auto const size =
|
||||||
|
std::min(max_size, static_cast<size_t>(size_dist(rng)));
|
||||||
|
std::string data;
|
||||||
|
|
||||||
|
auto const choice = rng() % 4;
|
||||||
|
switch (choice) {
|
||||||
|
case 0:
|
||||||
|
data = test::create_random_string(size, rng);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
data = test::loremipsum(size);
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
data = text_gen.generate(size);
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
data = binary_gen.generate(size);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
os->add_file(f, data);
|
||||||
|
paths.emplace_back(f, data);
|
||||||
|
|
||||||
|
if (opt.with_errors) {
|
||||||
|
auto failpath = fs::path{"/"} / f;
|
||||||
|
switch (rng() % 8) {
|
||||||
|
case 0:
|
||||||
|
os->set_access_fail(failpath);
|
||||||
|
[[fallthrough]];
|
||||||
|
case 1:
|
||||||
|
case 2:
|
||||||
|
os->set_map_file_error(
|
||||||
|
failpath,
|
||||||
|
std::make_exception_ptr(std::runtime_error("map_file_error")),
|
||||||
|
rng() % 4);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return paths;
|
||||||
|
}
|
||||||
|
|
||||||
|
void mkdwarfs_tester::add_test_file_tree() {
|
||||||
|
for (auto const& [stat, name] : test::test_dirtree()) {
|
||||||
|
auto path = name.substr(name.size() == 5 ? 5 : 6);
|
||||||
|
|
||||||
|
switch (stat.type()) {
|
||||||
|
case posix_file_type::regular:
|
||||||
|
os->add(path, stat,
|
||||||
|
[size = stat.size] { return test::loremipsum(size); });
|
||||||
|
break;
|
||||||
|
case posix_file_type::symlink:
|
||||||
|
os->add(path, stat, test::loremipsum(stat.size));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
os->add(path, stat);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
reader::filesystem_v2
|
||||||
|
mkdwarfs_tester::fs_from_data(std::string data,
|
||||||
|
reader::filesystem_options const& opt) {
|
||||||
|
if (!lgr) {
|
||||||
|
lgr = std::make_unique<test::test_logger>();
|
||||||
|
}
|
||||||
|
auto mm = std::make_shared<test::mmap_mock>(std::move(data));
|
||||||
|
return reader::filesystem_v2(*lgr, *os, mm, opt);
|
||||||
|
}
|
||||||
|
|
||||||
|
reader::filesystem_v2
|
||||||
|
mkdwarfs_tester::fs_from_file(std::string path,
|
||||||
|
reader::filesystem_options const& opt) {
|
||||||
|
auto fsimage = fa->get_file(path);
|
||||||
|
if (!fsimage) {
|
||||||
|
throw std::runtime_error("file not found: " + path);
|
||||||
|
}
|
||||||
|
return fs_from_data(std::move(fsimage.value()), opt);
|
||||||
|
}
|
||||||
|
|
||||||
|
reader::filesystem_v2
|
||||||
|
mkdwarfs_tester::fs_from_stdout(reader::filesystem_options const& opt) {
|
||||||
|
return fs_from_data(out(), opt);
|
||||||
|
}
|
||||||
|
|
||||||
|
dwarfsck_tester::dwarfsck_tester(std::shared_ptr<test::os_access_mock> pos)
|
||||||
|
: tester_common(tool::dwarfsck_main, "dwarfsck", std::move(pos)) {}
|
||||||
|
|
||||||
|
dwarfsck_tester::dwarfsck_tester()
|
||||||
|
: dwarfsck_tester(std::make_shared<test::os_access_mock>()) {}
|
||||||
|
|
||||||
|
dwarfsck_tester dwarfsck_tester::create_with_image(std::string image) {
|
||||||
|
auto os = std::make_shared<test::os_access_mock>();
|
||||||
|
os->add("", {1, 040755, 1, 0, 0, 10, 42, 0, 0, 0});
|
||||||
|
os->add_file("image.dwarfs", std::move(image));
|
||||||
|
return dwarfsck_tester(std::move(os));
|
||||||
|
}
|
||||||
|
|
||||||
|
dwarfsck_tester dwarfsck_tester::create_with_image() {
|
||||||
|
return create_with_image(build_test_image());
|
||||||
|
}
|
||||||
|
|
||||||
|
dwarfsextract_tester::dwarfsextract_tester(
|
||||||
|
std::shared_ptr<test::os_access_mock> pos)
|
||||||
|
: tester_common(tool::dwarfsextract_main, "dwarfsextract", std::move(pos)) {
|
||||||
|
}
|
||||||
|
|
||||||
|
dwarfsextract_tester::dwarfsextract_tester()
|
||||||
|
: dwarfsextract_tester(std::make_shared<test::os_access_mock>()) {}
|
||||||
|
|
||||||
|
dwarfsextract_tester
|
||||||
|
dwarfsextract_tester::create_with_image(std::string image) {
|
||||||
|
auto os = std::make_shared<test::os_access_mock>();
|
||||||
|
os->add("", {1, 040755, 1, 0, 0, 10, 42, 0, 0, 0});
|
||||||
|
os->add_file("image.dwarfs", std::move(image));
|
||||||
|
return dwarfsextract_tester(std::move(os));
|
||||||
|
}
|
||||||
|
|
||||||
|
dwarfsextract_tester dwarfsextract_tester::create_with_image() {
|
||||||
|
return create_with_image(build_test_image());
|
||||||
|
}
|
||||||
|
|
||||||
|
int mkdwarfs_main_test::run(std::vector<std::string> args) {
|
||||||
|
args.insert(args.begin(), "mkdwarfs");
|
||||||
|
return tool::main_adapter(tool::mkdwarfs_main)(args, iol->get());
|
||||||
|
}
|
||||||
|
|
||||||
|
int dwarfsck_main_test::run(std::vector<std::string> args) {
|
||||||
|
args.insert(args.begin(), "dwarfsck");
|
||||||
|
return tool::main_adapter(tool::dwarfsck_main)(args, iol->get());
|
||||||
|
}
|
||||||
|
|
||||||
|
int dwarfsextract_main_test::run(std::vector<std::string> args) {
|
||||||
|
args.insert(args.begin(), "dwarfsextract");
|
||||||
|
return tool::main_adapter(tool::dwarfsextract_main)(args, iol->get());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string build_test_image(std::vector<std::string> extra_args,
|
||||||
|
std::map<std::string, std::string> extra_files) {
|
||||||
|
mkdwarfs_tester t;
|
||||||
|
for (auto const& [name, contents] : extra_files) {
|
||||||
|
t.fa->set_file(name, contents);
|
||||||
|
}
|
||||||
|
std::vector<std::string> args = {"-i", "/", "-o", "-"};
|
||||||
|
args.insert(args.end(), extra_args.begin(), extra_args.end());
|
||||||
|
if (t.run(args) != 0) {
|
||||||
|
throw std::runtime_error("failed to build test image:\n" + t.err());
|
||||||
|
}
|
||||||
|
return t.out();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::tuple<std::optional<reader::filesystem_v2>, mkdwarfs_tester>
|
||||||
|
build_with_args(std::vector<std::string> opt_args) {
|
||||||
|
std::string const image_file = "test.dwarfs";
|
||||||
|
mkdwarfs_tester t;
|
||||||
|
std::vector<std::string> args = {"-i", "/", "-o", image_file};
|
||||||
|
args.insert(args.end(), opt_args.begin(), opt_args.end());
|
||||||
|
if (t.run(args) != 0) {
|
||||||
|
return {std::nullopt, std::move(t)};
|
||||||
|
}
|
||||||
|
return {t.fs_from_file(image_file), std::move(t)};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::set<uint64_t> get_all_fs_times(reader::filesystem_v2 const& fs) {
|
||||||
|
std::set<uint64_t> times;
|
||||||
|
fs.walk([&](auto const& e) {
|
||||||
|
auto st = fs.getattr(e.inode());
|
||||||
|
times.insert(st.atime());
|
||||||
|
times.insert(st.ctime());
|
||||||
|
times.insert(st.mtime());
|
||||||
|
});
|
||||||
|
return times;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::set<uint64_t> get_all_fs_uids(reader::filesystem_v2 const& fs) {
|
||||||
|
std::set<uint64_t> uids;
|
||||||
|
fs.walk([&](auto const& e) {
|
||||||
|
auto st = fs.getattr(e.inode());
|
||||||
|
uids.insert(st.uid());
|
||||||
|
});
|
||||||
|
return uids;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::set<uint64_t> get_all_fs_gids(reader::filesystem_v2 const& fs) {
|
||||||
|
std::set<uint64_t> gids;
|
||||||
|
fs.walk([&](auto const& e) {
|
||||||
|
auto st = fs.getattr(e.inode());
|
||||||
|
gids.insert(st.gid());
|
||||||
|
});
|
||||||
|
return gids;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace dwarfs::test
|
214
test/test_tool_main_tester.h
Normal file
214
test/test_tool_main_tester.h
Normal file
@ -0,0 +1,214 @@
|
|||||||
|
/* vim:set ts=2 sw=2 sts=2 et: */
|
||||||
|
/**
|
||||||
|
* \author Marcus Holland-Moritz (github@mhxnet.de)
|
||||||
|
* \copyright Copyright (c) Marcus Holland-Moritz
|
||||||
|
*
|
||||||
|
* This file is part of dwarfs.
|
||||||
|
*
|
||||||
|
* dwarfs is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* dwarfs is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with dwarfs. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <initializer_list>
|
||||||
|
#include <iosfwd>
|
||||||
|
#include <map>
|
||||||
|
#include <memory>
|
||||||
|
#include <optional>
|
||||||
|
#include <random>
|
||||||
|
#include <set>
|
||||||
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
#include <dwarfs/config.h>
|
||||||
|
#include <dwarfs/logger.h>
|
||||||
|
#include <dwarfs/reader/filesystem_options.h>
|
||||||
|
#include <dwarfs/reader/filesystem_v2.h>
|
||||||
|
#include <dwarfs/tool/main_adapter.h>
|
||||||
|
|
||||||
|
#include "loremipsum.h"
|
||||||
|
#include "lz_synthetic_generator.h"
|
||||||
|
#include "mmap_mock.h"
|
||||||
|
#include "test_helpers.h"
|
||||||
|
#include "test_logger.h"
|
||||||
|
|
||||||
|
namespace dwarfs::test {
|
||||||
|
|
||||||
|
// TODO: this is a workaround for older Clang versions
|
||||||
|
struct fs_path_hash {
|
||||||
|
auto operator()(std::filesystem::path const& p) const noexcept {
|
||||||
|
return std::filesystem::hash_value(p);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
extern std::filesystem::path const test_dir;
|
||||||
|
extern std::filesystem::path const audio_data_dir;
|
||||||
|
extern std::filesystem::path const fits_data_dir;
|
||||||
|
|
||||||
|
constexpr std::array<std::string_view, 6> const log_level_strings{
|
||||||
|
"error", "warn", "info", "verbose", "debug", "trace"};
|
||||||
|
|
||||||
|
enum class input_mode {
|
||||||
|
from_file,
|
||||||
|
from_stdin,
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr std::array input_modes{
|
||||||
|
input_mode::from_file,
|
||||||
|
input_mode::from_stdin,
|
||||||
|
};
|
||||||
|
|
||||||
|
std::ostream& operator<<(std::ostream& os, input_mode m);
|
||||||
|
|
||||||
|
enum class path_type {
|
||||||
|
relative,
|
||||||
|
absolute,
|
||||||
|
mixed,
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr std::array path_types{path_type::relative, path_type::absolute,
|
||||||
|
path_type::mixed};
|
||||||
|
|
||||||
|
std::ostream& operator<<(std::ostream& os, path_type m);
|
||||||
|
|
||||||
|
class tester_common {
|
||||||
|
public:
|
||||||
|
using main_ptr_t = tool::main_adapter::main_fn_type;
|
||||||
|
|
||||||
|
tester_common(main_ptr_t mp, std::string toolname,
|
||||||
|
std::shared_ptr<test::os_access_mock> pos);
|
||||||
|
|
||||||
|
int run(std::vector<std::string> args);
|
||||||
|
int run(std::initializer_list<std::string> args);
|
||||||
|
int run(std::string args);
|
||||||
|
|
||||||
|
std::string out() const { return iol->out(); }
|
||||||
|
std::string err() const { return iol->err(); }
|
||||||
|
|
||||||
|
std::shared_ptr<test::test_file_access> fa;
|
||||||
|
std::shared_ptr<test::os_access_mock> os;
|
||||||
|
std::unique_ptr<test::test_iolayer> iol;
|
||||||
|
|
||||||
|
private:
|
||||||
|
main_ptr_t main_;
|
||||||
|
std::string toolname_;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct random_file_tree_options {
|
||||||
|
double avg_size{4096.0};
|
||||||
|
int dimension{20};
|
||||||
|
int max_name_len{50};
|
||||||
|
bool with_errors{false};
|
||||||
|
bool with_invalid_utf8{false};
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr auto default_fs_opts = reader::filesystem_options{
|
||||||
|
.block_cache = {.max_bytes = 256 * 1024,
|
||||||
|
.sequential_access_detector_threshold = 4},
|
||||||
|
.metadata = {.check_consistency = true}};
|
||||||
|
|
||||||
|
class mkdwarfs_tester : public tester_common {
|
||||||
|
public:
|
||||||
|
mkdwarfs_tester();
|
||||||
|
explicit mkdwarfs_tester(std::shared_ptr<test::os_access_mock> pos);
|
||||||
|
|
||||||
|
static mkdwarfs_tester create_empty();
|
||||||
|
|
||||||
|
void add_stream_logger(std::ostream& st,
|
||||||
|
logger::level_type level = logger::VERBOSE);
|
||||||
|
|
||||||
|
void add_root_dir();
|
||||||
|
void add_special_files();
|
||||||
|
void add_test_file_tree();
|
||||||
|
|
||||||
|
std::vector<std::pair<std::filesystem::path, std::string>>
|
||||||
|
add_random_file_tree(
|
||||||
|
random_file_tree_options const& opt = random_file_tree_options{});
|
||||||
|
|
||||||
|
reader::filesystem_v2
|
||||||
|
fs_from_data(std::string data,
|
||||||
|
reader::filesystem_options const& opt = default_fs_opts);
|
||||||
|
|
||||||
|
reader::filesystem_v2
|
||||||
|
fs_from_file(std::string path,
|
||||||
|
reader::filesystem_options const& opt = default_fs_opts);
|
||||||
|
|
||||||
|
reader::filesystem_v2
|
||||||
|
fs_from_stdout(reader::filesystem_options const& opt = default_fs_opts);
|
||||||
|
|
||||||
|
std::unique_ptr<logger> lgr;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string
|
||||||
|
build_test_image(std::vector<std::string> extra_args = {},
|
||||||
|
std::map<std::string, std::string> extra_files = {});
|
||||||
|
|
||||||
|
class dwarfsck_tester : public tester_common {
|
||||||
|
public:
|
||||||
|
dwarfsck_tester();
|
||||||
|
explicit dwarfsck_tester(std::shared_ptr<test::os_access_mock> pos);
|
||||||
|
|
||||||
|
static dwarfsck_tester create_with_image();
|
||||||
|
static dwarfsck_tester create_with_image(std::string image);
|
||||||
|
};
|
||||||
|
|
||||||
|
class dwarfsextract_tester : public tester_common {
|
||||||
|
public:
|
||||||
|
dwarfsextract_tester();
|
||||||
|
explicit dwarfsextract_tester(std::shared_ptr<test::os_access_mock> pos);
|
||||||
|
|
||||||
|
static dwarfsextract_tester create_with_image();
|
||||||
|
static dwarfsextract_tester create_with_image(std::string image);
|
||||||
|
};
|
||||||
|
|
||||||
|
std::tuple<std::optional<reader::filesystem_v2>, mkdwarfs_tester>
|
||||||
|
build_with_args(std::vector<std::string> opt_args = {});
|
||||||
|
|
||||||
|
std::set<uint64_t> get_all_fs_times(reader::filesystem_v2 const& fs);
|
||||||
|
std::set<uint64_t> get_all_fs_uids(reader::filesystem_v2 const& fs);
|
||||||
|
std::set<uint64_t> get_all_fs_gids(reader::filesystem_v2 const& fs);
|
||||||
|
|
||||||
|
class tool_main_test : public testing::Test {
|
||||||
|
public:
|
||||||
|
void SetUp() override;
|
||||||
|
void TearDown() override;
|
||||||
|
|
||||||
|
std::string out() const { return iol->out(); }
|
||||||
|
std::string err() const { return iol->err(); }
|
||||||
|
|
||||||
|
std::unique_ptr<test::test_iolayer> iol;
|
||||||
|
};
|
||||||
|
|
||||||
|
class mkdwarfs_main_test : public tool_main_test {
|
||||||
|
public:
|
||||||
|
int run(std::vector<std::string> args);
|
||||||
|
};
|
||||||
|
|
||||||
|
class dwarfsck_main_test : public tool_main_test {
|
||||||
|
public:
|
||||||
|
int run(std::vector<std::string> args);
|
||||||
|
};
|
||||||
|
|
||||||
|
class dwarfsextract_main_test : public tool_main_test {
|
||||||
|
public:
|
||||||
|
int run(std::vector<std::string> args);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace dwarfs::test
|
@ -22,16 +22,10 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <array>
|
|
||||||
#include <filesystem>
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <random>
|
|
||||||
#include <regex>
|
#include <regex>
|
||||||
#include <set>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include <gmock/gmock.h>
|
#include <gmock/gmock.h>
|
||||||
#include <gtest/gtest.h>
|
|
||||||
|
|
||||||
#include <archive.h>
|
#include <archive.h>
|
||||||
#include <archive_entry.h>
|
#include <archive_entry.h>
|
||||||
@ -51,451 +45,22 @@
|
|||||||
#include <range/v3/view/join.hpp>
|
#include <range/v3/view/join.hpp>
|
||||||
#include <range/v3/view/map.hpp>
|
#include <range/v3/view/map.hpp>
|
||||||
|
|
||||||
#include <dwarfs/config.h>
|
|
||||||
#include <dwarfs/file_util.h>
|
#include <dwarfs/file_util.h>
|
||||||
#include <dwarfs/history.h>
|
#include <dwarfs/history.h>
|
||||||
#include <dwarfs/logger.h>
|
|
||||||
#include <dwarfs/reader/filesystem_options.h>
|
|
||||||
#include <dwarfs/reader/filesystem_v2.h>
|
|
||||||
#include <dwarfs/reader/fsinfo_options.h>
|
#include <dwarfs/reader/fsinfo_options.h>
|
||||||
#include <dwarfs/reader/iovec_read_buf.h>
|
#include <dwarfs/reader/iovec_read_buf.h>
|
||||||
#include <dwarfs/sorted_array_map.h>
|
#include <dwarfs/sorted_array_map.h>
|
||||||
#include <dwarfs/string.h>
|
#include <dwarfs/string.h>
|
||||||
#include <dwarfs/tool/main_adapter.h>
|
|
||||||
#include <dwarfs/util.h>
|
#include <dwarfs/util.h>
|
||||||
#include <dwarfs_tool_main.h>
|
|
||||||
|
|
||||||
#include "filter_test_data.h"
|
#include "filter_test_data.h"
|
||||||
#include "loremipsum.h"
|
#include "test_tool_main_tester.h"
|
||||||
#include "lz_synthetic_generator.h"
|
|
||||||
#include "mmap_mock.h"
|
|
||||||
#include "test_helpers.h"
|
|
||||||
#include "test_logger.h"
|
|
||||||
|
|
||||||
using namespace dwarfs;
|
using namespace dwarfs;
|
||||||
|
using namespace dwarfs::test;
|
||||||
|
|
||||||
namespace fs = std::filesystem;
|
namespace fs = std::filesystem;
|
||||||
|
|
||||||
using tool::iolayer;
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
// TODO: this is a workaround for older Clang versions
|
|
||||||
struct fs_path_hash {
|
|
||||||
auto operator()(std::filesystem::path const& p) const noexcept {
|
|
||||||
return std::filesystem::hash_value(p);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
auto test_dir = fs::path(TEST_DATA_DIR).make_preferred();
|
|
||||||
auto audio_data_dir = test_dir / "pcmaudio";
|
|
||||||
auto fits_data_dir = test_dir / "fits";
|
|
||||||
|
|
||||||
constexpr std::array<std::string_view, 6> const log_level_strings{
|
|
||||||
"error", "warn", "info", "verbose", "debug", "trace"};
|
|
||||||
|
|
||||||
enum class input_mode {
|
|
||||||
from_file,
|
|
||||||
from_stdin,
|
|
||||||
};
|
|
||||||
|
|
||||||
constexpr std::array input_modes{
|
|
||||||
input_mode::from_file,
|
|
||||||
input_mode::from_stdin,
|
|
||||||
};
|
|
||||||
|
|
||||||
std::ostream& operator<<(std::ostream& os, input_mode m) {
|
|
||||||
switch (m) {
|
|
||||||
case input_mode::from_file:
|
|
||||||
os << "from_file";
|
|
||||||
break;
|
|
||||||
case input_mode::from_stdin:
|
|
||||||
os << "from_stdin";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return os;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum class path_type {
|
|
||||||
relative,
|
|
||||||
absolute,
|
|
||||||
mixed,
|
|
||||||
};
|
|
||||||
|
|
||||||
constexpr std::array path_types{path_type::relative, path_type::absolute,
|
|
||||||
path_type::mixed};
|
|
||||||
|
|
||||||
std::ostream& operator<<(std::ostream& os, path_type m) {
|
|
||||||
switch (m) {
|
|
||||||
case path_type::relative:
|
|
||||||
os << "relative";
|
|
||||||
break;
|
|
||||||
case path_type::absolute:
|
|
||||||
os << "absolute";
|
|
||||||
break;
|
|
||||||
case path_type::mixed:
|
|
||||||
os << "mixed";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return os;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct locale_setup_helper {
|
|
||||||
locale_setup_helper() { setup_default_locale(); }
|
|
||||||
};
|
|
||||||
|
|
||||||
void setup_locale() { static locale_setup_helper helper; }
|
|
||||||
|
|
||||||
class tool_main_test : public testing::Test {
|
|
||||||
public:
|
|
||||||
void SetUp() override {
|
|
||||||
setup_locale();
|
|
||||||
iol = std::make_unique<test::test_iolayer>();
|
|
||||||
}
|
|
||||||
|
|
||||||
void TearDown() override { iol.reset(); }
|
|
||||||
|
|
||||||
std::string out() const { return iol->out(); }
|
|
||||||
std::string err() const { return iol->err(); }
|
|
||||||
|
|
||||||
std::unique_ptr<test::test_iolayer> iol;
|
|
||||||
};
|
|
||||||
|
|
||||||
class tester_common {
|
|
||||||
public:
|
|
||||||
using main_ptr_t = tool::main_adapter::main_fn_type;
|
|
||||||
|
|
||||||
tester_common(main_ptr_t mp, std::string toolname,
|
|
||||||
std::shared_ptr<test::os_access_mock> pos)
|
|
||||||
: fa{std::make_shared<test::test_file_access>()}
|
|
||||||
, os{std::move(pos)}
|
|
||||||
, iol{std::make_unique<test::test_iolayer>(os, fa)}
|
|
||||||
, main_{mp}
|
|
||||||
, toolname_{std::move(toolname)} {
|
|
||||||
setup_locale();
|
|
||||||
}
|
|
||||||
|
|
||||||
int run(std::vector<std::string> args) {
|
|
||||||
args.insert(args.begin(), toolname_);
|
|
||||||
return tool::main_adapter(main_)(args, iol->get());
|
|
||||||
}
|
|
||||||
|
|
||||||
int run(std::initializer_list<std::string> args) {
|
|
||||||
return run(std::vector<std::string>(args));
|
|
||||||
}
|
|
||||||
|
|
||||||
int run(std::string args) { return run(test::parse_args(args)); }
|
|
||||||
|
|
||||||
std::string out() const { return iol->out(); }
|
|
||||||
std::string err() const { return iol->err(); }
|
|
||||||
|
|
||||||
std::shared_ptr<test::test_file_access> fa;
|
|
||||||
std::shared_ptr<test::os_access_mock> os;
|
|
||||||
std::unique_ptr<test::test_iolayer> iol;
|
|
||||||
|
|
||||||
private:
|
|
||||||
main_ptr_t main_;
|
|
||||||
std::string toolname_;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct random_file_tree_options {
|
|
||||||
double avg_size{4096.0};
|
|
||||||
int dimension{20};
|
|
||||||
int max_name_len{50};
|
|
||||||
bool with_errors{false};
|
|
||||||
bool with_invalid_utf8{false};
|
|
||||||
};
|
|
||||||
|
|
||||||
constexpr auto default_fs_opts = reader::filesystem_options{
|
|
||||||
.block_cache = {.max_bytes = 256 * 1024,
|
|
||||||
.sequential_access_detector_threshold = 4},
|
|
||||||
.metadata = {.check_consistency = true}};
|
|
||||||
|
|
||||||
class mkdwarfs_tester : public tester_common {
|
|
||||||
public:
|
|
||||||
mkdwarfs_tester(std::shared_ptr<test::os_access_mock> pos)
|
|
||||||
: tester_common(tool::mkdwarfs_main, "mkdwarfs", std::move(pos)) {}
|
|
||||||
|
|
||||||
mkdwarfs_tester()
|
|
||||||
: mkdwarfs_tester(test::os_access_mock::create_test_instance()) {}
|
|
||||||
|
|
||||||
static mkdwarfs_tester create_empty() {
|
|
||||||
return mkdwarfs_tester(std::make_shared<test::os_access_mock>());
|
|
||||||
}
|
|
||||||
|
|
||||||
void add_stream_logger(std::ostream& st,
|
|
||||||
logger::level_type level = logger::VERBOSE) {
|
|
||||||
lgr =
|
|
||||||
std::make_unique<stream_logger>(std::make_shared<test::test_terminal>(),
|
|
||||||
st, logger_options{.threshold = level});
|
|
||||||
}
|
|
||||||
|
|
||||||
void add_root_dir() { os->add("", {1, 040755, 1, 0, 0, 10, 42, 0, 0, 0}); }
|
|
||||||
|
|
||||||
void add_special_files() {
|
|
||||||
static constexpr file_stat::off_type const size = 10;
|
|
||||||
std::string data(size, 'x');
|
|
||||||
os->add("suid", {1001, 0104755, 1, 0, 0, size, 0, 3333, 2222, 1111}, data);
|
|
||||||
os->add("sgid", {1002, 0102755, 1, 0, 0, size, 0, 0, 0, 0}, data);
|
|
||||||
os->add("sticky", {1003, 0101755, 1, 0, 0, size, 0, 0, 0, 0}, data);
|
|
||||||
os->add("block", {1004, 060666, 1, 0, 0, 0, 77, 0, 0, 0}, std::string{});
|
|
||||||
os->add("sock", {1005, 0140666, 1, 0, 0, 0, 0, 0, 0, 0}, std::string{});
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<std::pair<fs::path, std::string>> add_random_file_tree(
|
|
||||||
random_file_tree_options const& opt = random_file_tree_options{}) {
|
|
||||||
size_t max_size{128 * static_cast<size_t>(opt.avg_size)};
|
|
||||||
std::mt19937_64 rng{42};
|
|
||||||
std::exponential_distribution<> size_dist{1 / opt.avg_size};
|
|
||||||
std::uniform_int_distribution<> path_comp_size_dist{0, opt.max_name_len};
|
|
||||||
std::uniform_int_distribution<> invalid_dist{0, 1};
|
|
||||||
std::vector<std::pair<fs::path, std::string>> paths;
|
|
||||||
|
|
||||||
auto random_path_component = [&] {
|
|
||||||
auto size = path_comp_size_dist(rng);
|
|
||||||
if (opt.with_invalid_utf8 && invalid_dist(rng) == 0) {
|
|
||||||
return test::create_random_string(size, 96, 255, rng);
|
|
||||||
}
|
|
||||||
return test::create_random_string(size, 'A', 'Z', rng);
|
|
||||||
};
|
|
||||||
|
|
||||||
test::lz_params text_lzp{};
|
|
||||||
test::lz_params binary_lzp{};
|
|
||||||
text_lzp.text_mode = true;
|
|
||||||
binary_lzp.text_mode = false;
|
|
||||||
text_lzp.seed = rng();
|
|
||||||
binary_lzp.seed = rng();
|
|
||||||
test::lz_synthetic_generator text_gen{text_lzp};
|
|
||||||
test::lz_synthetic_generator binary_gen{binary_lzp};
|
|
||||||
|
|
||||||
for (int x = 0; x < opt.dimension; ++x) {
|
|
||||||
fs::path d1{random_path_component() + std::to_string(x)};
|
|
||||||
os->add_dir(d1);
|
|
||||||
|
|
||||||
for (int y = 0; y < opt.dimension; ++y) {
|
|
||||||
fs::path d2{d1 / (random_path_component() + std::to_string(y))};
|
|
||||||
os->add_dir(d2);
|
|
||||||
|
|
||||||
for (int z = 0; z < opt.dimension; ++z) {
|
|
||||||
fs::path f{d2 / (random_path_component() + std::to_string(z))};
|
|
||||||
auto const size =
|
|
||||||
std::min(max_size, static_cast<size_t>(size_dist(rng)));
|
|
||||||
std::string data;
|
|
||||||
|
|
||||||
auto const choice = rng() % 4;
|
|
||||||
switch (choice) {
|
|
||||||
case 0:
|
|
||||||
data = test::create_random_string(size, rng);
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
data = test::loremipsum(size);
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
data = text_gen.generate(size);
|
|
||||||
break;
|
|
||||||
case 4:
|
|
||||||
data = binary_gen.generate(size);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
os->add_file(f, data);
|
|
||||||
paths.emplace_back(f, data);
|
|
||||||
|
|
||||||
if (opt.with_errors) {
|
|
||||||
auto failpath = fs::path{"/"} / f;
|
|
||||||
switch (rng() % 8) {
|
|
||||||
case 0:
|
|
||||||
os->set_access_fail(failpath);
|
|
||||||
[[fallthrough]];
|
|
||||||
case 1:
|
|
||||||
case 2:
|
|
||||||
os->set_map_file_error(
|
|
||||||
failpath,
|
|
||||||
std::make_exception_ptr(std::runtime_error("map_file_error")),
|
|
||||||
rng() % 4);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return paths;
|
|
||||||
}
|
|
||||||
|
|
||||||
void add_test_file_tree() {
|
|
||||||
for (auto const& [stat, name] : test::test_dirtree()) {
|
|
||||||
auto path = name.substr(name.size() == 5 ? 5 : 6);
|
|
||||||
|
|
||||||
switch (stat.type()) {
|
|
||||||
case posix_file_type::regular:
|
|
||||||
os->add(path, stat,
|
|
||||||
[size = stat.size] { return test::loremipsum(size); });
|
|
||||||
break;
|
|
||||||
case posix_file_type::symlink:
|
|
||||||
os->add(path, stat, test::loremipsum(stat.size));
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
os->add(path, stat);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
reader::filesystem_v2
|
|
||||||
fs_from_data(std::string data,
|
|
||||||
reader::filesystem_options const& opt = default_fs_opts) {
|
|
||||||
if (!lgr) {
|
|
||||||
lgr = std::make_unique<test::test_logger>();
|
|
||||||
}
|
|
||||||
auto mm = std::make_shared<test::mmap_mock>(std::move(data));
|
|
||||||
return reader::filesystem_v2(*lgr, *os, mm, opt);
|
|
||||||
}
|
|
||||||
|
|
||||||
reader::filesystem_v2
|
|
||||||
fs_from_file(std::string path,
|
|
||||||
reader::filesystem_options const& opt = default_fs_opts) {
|
|
||||||
auto fsimage = fa->get_file(path);
|
|
||||||
if (!fsimage) {
|
|
||||||
throw std::runtime_error("file not found: " + path);
|
|
||||||
}
|
|
||||||
return fs_from_data(std::move(fsimage.value()), opt);
|
|
||||||
}
|
|
||||||
|
|
||||||
reader::filesystem_v2
|
|
||||||
fs_from_stdout(reader::filesystem_options const& opt = default_fs_opts) {
|
|
||||||
return fs_from_data(out(), opt);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unique_ptr<logger> lgr;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::string
|
|
||||||
build_test_image(std::vector<std::string> extra_args = {},
|
|
||||||
std::map<std::string, std::string> extra_files = {}) {
|
|
||||||
mkdwarfs_tester t;
|
|
||||||
for (auto const& [name, contents] : extra_files) {
|
|
||||||
t.fa->set_file(name, contents);
|
|
||||||
}
|
|
||||||
std::vector<std::string> args = {"-i", "/", "-o", "-"};
|
|
||||||
args.insert(args.end(), extra_args.begin(), extra_args.end());
|
|
||||||
if (t.run(args) != 0) {
|
|
||||||
throw std::runtime_error("failed to build test image:\n" + t.err());
|
|
||||||
}
|
|
||||||
return t.out();
|
|
||||||
}
|
|
||||||
|
|
||||||
class dwarfsck_tester : public tester_common {
|
|
||||||
public:
|
|
||||||
dwarfsck_tester(std::shared_ptr<test::os_access_mock> pos)
|
|
||||||
: tester_common(tool::dwarfsck_main, "dwarfsck", std::move(pos)) {}
|
|
||||||
|
|
||||||
dwarfsck_tester()
|
|
||||||
: dwarfsck_tester(std::make_shared<test::os_access_mock>()) {}
|
|
||||||
|
|
||||||
static dwarfsck_tester create_with_image(std::string image) {
|
|
||||||
auto os = std::make_shared<test::os_access_mock>();
|
|
||||||
os->add("", {1, 040755, 1, 0, 0, 10, 42, 0, 0, 0});
|
|
||||||
os->add_file("image.dwarfs", std::move(image));
|
|
||||||
return dwarfsck_tester(std::move(os));
|
|
||||||
}
|
|
||||||
|
|
||||||
static dwarfsck_tester create_with_image() {
|
|
||||||
return create_with_image(build_test_image());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class dwarfsextract_tester : public tester_common {
|
|
||||||
public:
|
|
||||||
dwarfsextract_tester(std::shared_ptr<test::os_access_mock> pos)
|
|
||||||
: tester_common(tool::dwarfsextract_main, "dwarfsextract",
|
|
||||||
std::move(pos)) {}
|
|
||||||
|
|
||||||
dwarfsextract_tester()
|
|
||||||
: dwarfsextract_tester(std::make_shared<test::os_access_mock>()) {}
|
|
||||||
|
|
||||||
static dwarfsextract_tester create_with_image(std::string image) {
|
|
||||||
auto os = std::make_shared<test::os_access_mock>();
|
|
||||||
os->add("", {1, 040755, 1, 0, 0, 10, 42, 0, 0, 0});
|
|
||||||
os->add_file("image.dwarfs", std::move(image));
|
|
||||||
return dwarfsextract_tester(std::move(os));
|
|
||||||
}
|
|
||||||
|
|
||||||
static dwarfsextract_tester create_with_image() {
|
|
||||||
return create_with_image(build_test_image());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
std::tuple<std::optional<reader::filesystem_v2>, mkdwarfs_tester>
|
|
||||||
build_with_args(std::vector<std::string> opt_args = {}) {
|
|
||||||
std::string const image_file = "test.dwarfs";
|
|
||||||
mkdwarfs_tester t;
|
|
||||||
std::vector<std::string> args = {"-i", "/", "-o", image_file};
|
|
||||||
args.insert(args.end(), opt_args.begin(), opt_args.end());
|
|
||||||
if (t.run(args) != 0) {
|
|
||||||
return {std::nullopt, std::move(t)};
|
|
||||||
}
|
|
||||||
return {t.fs_from_file(image_file), std::move(t)};
|
|
||||||
}
|
|
||||||
|
|
||||||
std::set<uint64_t> get_all_fs_times(reader::filesystem_v2 const& fs) {
|
|
||||||
std::set<uint64_t> times;
|
|
||||||
fs.walk([&](auto const& e) {
|
|
||||||
auto st = fs.getattr(e.inode());
|
|
||||||
times.insert(st.atime());
|
|
||||||
times.insert(st.ctime());
|
|
||||||
times.insert(st.mtime());
|
|
||||||
});
|
|
||||||
return times;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::set<uint64_t> get_all_fs_uids(reader::filesystem_v2 const& fs) {
|
|
||||||
std::set<uint64_t> uids;
|
|
||||||
fs.walk([&](auto const& e) {
|
|
||||||
auto st = fs.getattr(e.inode());
|
|
||||||
uids.insert(st.uid());
|
|
||||||
});
|
|
||||||
return uids;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::set<uint64_t> get_all_fs_gids(reader::filesystem_v2 const& fs) {
|
|
||||||
std::set<uint64_t> gids;
|
|
||||||
fs.walk([&](auto const& e) {
|
|
||||||
auto st = fs.getattr(e.inode());
|
|
||||||
gids.insert(st.gid());
|
|
||||||
});
|
|
||||||
return gids;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
class mkdwarfs_main_test : public tool_main_test {
|
|
||||||
public:
|
|
||||||
int run(std::vector<std::string> args) {
|
|
||||||
args.insert(args.begin(), "mkdwarfs");
|
|
||||||
return tool::main_adapter(tool::mkdwarfs_main)(args, iol->get());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class dwarfsck_main_test : public tool_main_test {
|
|
||||||
public:
|
|
||||||
int run(std::vector<std::string> args) {
|
|
||||||
args.insert(args.begin(), "dwarfsck");
|
|
||||||
return tool::main_adapter(tool::dwarfsck_main)(args, iol->get());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class dwarfsextract_main_test : public tool_main_test {
|
|
||||||
public:
|
|
||||||
int run(std::vector<std::string> args) {
|
|
||||||
args.insert(args.begin(), "dwarfsextract");
|
|
||||||
return tool::main_adapter(tool::dwarfsextract_main)(args, iol->get());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
TEST_F(mkdwarfs_main_test, no_cmdline_args) {
|
TEST_F(mkdwarfs_main_test, no_cmdline_args) {
|
||||||
auto exit_code = run({});
|
auto exit_code = run({});
|
||||||
EXPECT_EQ(exit_code, 0);
|
EXPECT_EQ(exit_code, 0);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user