/* 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 . */ #include #include #include #include "dwarfs/block_compressor.h" #include "dwarfs/entry.h" #include "dwarfs/filesystem.h" #include "dwarfs/filesystem_writer.h" #include "dwarfs/logger.h" #include "dwarfs/options.h" #include "dwarfs/os_access.h" #include "dwarfs/progress.h" #include "dwarfs/scanner.h" #include "dwarfs/script.h" #include "loremipsum.h" namespace dwarfs { namespace test { class dir_reader_mock : public dir_reader { public: dir_reader_mock(std::vector&& files) : m_files(files) , m_index(0) {} bool read(std::string& name) const override { if (m_index < m_files.size()) { name = m_files[m_index++]; return true; } return false; } private: std::vector m_files; mutable size_t m_index; }; namespace { struct simplestat { ::mode_t st_mode; ::uid_t st_uid; ::gid_t st_gid; ::off_t st_size; }; std::unordered_map statmap{ {"/", {S_IFDIR | 0777, 1000, 1000, 0}}, {"//test.pl", {S_IFREG | 0644, 1000, 1000, 0}}, {"//somelink", {S_IFLNK | 0777, 1000, 1000, 16}}, {"//somedir", {S_IFDIR | 0777, 1000, 1000, 0}}, {"//foo.pl", {S_IFREG | 0600, 1337, 0, 23456}}, {"//ipsum.txt", {S_IFREG | 0644, 1000, 1000, 2000000}}, {"//somedir/ipsum.py", {S_IFREG | 0644, 1000, 1000, 10000}}, }; } // namespace class mmap_mock : public mmif { public: mmap_mock(const std::string& data) : m_data(data) { assign(m_data.data(), m_data.size()); } private: const std::string m_data; }; class os_access_mock : public os_access { public: std::shared_ptr opendir(const std::string& path) const override { if (path == "/") { std::vector files{ ".", "..", "test.pl", "somelink", "somedir", "foo.pl", "ipsum.txt", }; return std::make_shared(std::move(files)); } else if (path == "//somedir") { std::vector files{ ".", "..", "ipsum.py", }; return std::make_shared(std::move(files)); } throw std::runtime_error("oops"); } void lstat(const std::string& path, struct ::stat* st) const override { const simplestat& sst = statmap[path]; ::memset(st, 0, sizeof(*st)); st->st_mode = sst.st_mode; st->st_uid = sst.st_uid; st->st_gid = sst.st_gid; st->st_size = sst.st_size; st->st_atime = 123; st->st_mtime = 234; st->st_ctime = 345; } std::string readlink(const std::string& path, size_t size) const override { if (path == "//somelink" && size == 16) { return "somedir/ipsum.py"; } throw std::runtime_error("oops"); } std::shared_ptr map_file(const std::string& path, size_t size) const override { const simplestat& sst = statmap[path]; if (size == static_cast(sst.st_size)) { return std::make_shared(loremipsum(size)); } throw std::runtime_error("oops"); } int access(const std::string&, int) const override { return 0; } }; class script_mock : public script { public: bool filter(file_interface const& /*fi*/) const override { return true; } void order(file_vector& /*fvi*/) const override { // do nothing } }; } // namespace test } // namespace dwarfs using namespace dwarfs; namespace { void basic_end_to_end_test(const std::string& compressor, unsigned block_size_bits, file_order_mode file_order, bool no_owner, bool no_time) { block_manager::config cfg; scanner_options options; cfg.blockhash_window_size.push_back(1 << 10); cfg.block_size_bits = block_size_bits; options.file_order = file_order; // force multithreading worker_group wg("writer", 4); std::ostringstream logss; stream_logger lgr(logss); // TODO: mock lgr.set_policy(); scanner s(lgr, wg, cfg, entry_factory::create(no_owner, no_time, file_order == file_order_mode::SIMILARITY), std::make_shared(), std::make_shared(), options); std::ostringstream oss; progress prog([](const progress&, bool) {}); block_compressor bc(compressor); filesystem_writer fsw(oss, lgr, wg, prog, bc, 64 << 20); s.scan(fsw, "/", prog); block_cache_options bco; bco.max_bytes = 1 << 20; filesystem fs(lgr, std::make_shared(oss.str()), bco); auto de = fs.find("/foo.pl"); struct ::stat st; ASSERT_NE(de, nullptr); EXPECT_EQ(fs.getattr(de, &st), 0); EXPECT_EQ(st.st_size, 23456); int inode = fs.open(de); EXPECT_GE(inode, 0); std::vector buf(st.st_size); ssize_t rv = fs.read(inode, &buf[0], st.st_size, 0); EXPECT_EQ(rv, st.st_size); EXPECT_EQ(std::string(buf.begin(), buf.end()), test::loremipsum(st.st_size)); } } // namespace TEST(dwarfs, basic_test) { std::vector compressions{"null", #ifdef DWARFS_HAVE_LIBLZ4 "lz4", "lz4hc:level=4", #endif #ifdef DWARFS_HAVE_LIBZSTD "zstd:level=1", #endif #ifdef DWARFS_HAVE_LIBLZMA "lzma:level=1" #endif }; std::vector block_sizes{12, 15, 20, 28}; std::vector file_orders{ file_order_mode::NONE, file_order_mode::PATH, file_order_mode::SCRIPT, file_order_mode::SIMILARITY}; for (const auto& comp : compressions) { for (const auto& bs : block_sizes) { for (const auto& order : file_orders) { basic_end_to_end_test(comp, bs, order, false, false); basic_end_to_end_test(comp, bs, order, false, true); basic_end_to_end_test(comp, bs, order, true, true); } } } }