mirror of
https://github.com/mhx/dwarfs.git
synced 2025-09-19 01:07:00 -04:00
Improve test coverage
This commit is contained in:
parent
44b135f548
commit
6ef5361fc5
201
test/dwarfs.cpp
201
test/dwarfs.cpp
@ -19,8 +19,11 @@
|
||||
* along with dwarfs. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
@ -64,7 +67,9 @@ class dir_reader_mock : public dir_reader {
|
||||
namespace {
|
||||
|
||||
struct simplestat {
|
||||
::ino_t st_ino;
|
||||
::mode_t st_mode;
|
||||
::nlink_t st_nlink;
|
||||
::uid_t st_uid;
|
||||
::gid_t st_gid;
|
||||
::off_t st_size;
|
||||
@ -75,19 +80,24 @@ struct simplestat {
|
||||
};
|
||||
|
||||
std::map<std::string, simplestat> statmap{
|
||||
{"", {S_IFDIR | 0777, 1000, 100, 0, 0, 1, 2, 3}},
|
||||
{"/test.pl", {S_IFREG | 0644, 1000, 100, 0, 0, 1001, 1002, 1003}},
|
||||
{"/somelink", {S_IFLNK | 0777, 1000, 100, 16, 0, 2001, 2002, 2003}},
|
||||
{"/somedir", {S_IFDIR | 0777, 1000, 100, 0, 0, 3001, 3002, 3003}},
|
||||
{"/foo.pl", {S_IFREG | 0600, 1337, 0, 23456, 0, 4001, 4002, 4003}},
|
||||
{"/ipsum.txt", {S_IFREG | 0644, 1000, 100, 2000000, 0, 5001, 5002, 5003}},
|
||||
{"", {1, S_IFDIR | 0777, 1, 1000, 100, 0, 0, 1, 2, 3}},
|
||||
{"/test.pl", {3, S_IFREG | 0644, 2, 1000, 100, 0, 0, 1001, 1002, 1003}},
|
||||
{"/somelink", {4, S_IFLNK | 0777, 1, 1000, 100, 16, 0, 2001, 2002, 2003}},
|
||||
{"/somedir", {5, S_IFDIR | 0777, 1, 1000, 100, 0, 0, 3001, 3002, 3003}},
|
||||
{"/foo.pl", {6, S_IFREG | 0600, 2, 1337, 0, 23456, 0, 4001, 4002, 4003}},
|
||||
{"/bar.pl", {6, S_IFREG | 0600, 2, 1337, 0, 23456, 0, 4001, 4002, 4003}},
|
||||
{"/baz.pl", {16, S_IFREG | 0600, 2, 1337, 0, 23456, 0, 8001, 8002, 8003}},
|
||||
{"/ipsum.txt",
|
||||
{7, S_IFREG | 0644, 1, 1000, 100, 2000000, 0, 5001, 5002, 5003}},
|
||||
{"/somedir/ipsum.py",
|
||||
{S_IFREG | 0644, 1000, 100, 10000, 0, 6001, 6002, 6003}},
|
||||
{"/somedir/bad", {S_IFLNK | 0777, 1000, 100, 6, 0, 7001, 7002, 7003}},
|
||||
{"/somedir/pipe", {S_IFIFO | 0644, 1000, 100, 0, 0, 8001, 8002, 8003}},
|
||||
{"/somedir/null", {S_IFCHR | 0666, 0, 0, 0, 259, 9001, 9002, 9003}},
|
||||
{9, S_IFREG | 0644, 1, 1000, 100, 10000, 0, 6001, 6002, 6003}},
|
||||
{"/somedir/bad",
|
||||
{10, S_IFLNK | 0777, 1, 1000, 100, 6, 0, 7001, 7002, 7003}},
|
||||
{"/somedir/pipe",
|
||||
{12, S_IFIFO | 0644, 1, 1000, 100, 0, 0, 8001, 8002, 8003}},
|
||||
{"/somedir/null", {13, S_IFCHR | 0666, 1, 0, 0, 0, 259, 9001, 9002, 9003}},
|
||||
{"/somedir/zero",
|
||||
{S_IFCHR | 0666, 0, 0, 0, 261, 4000010001, 4000020002, 4000030003}},
|
||||
{14, S_IFCHR | 0666, 1, 0, 0, 0, 261, 4000010001, 4000020002, 4000030003}},
|
||||
};
|
||||
} // namespace
|
||||
|
||||
@ -96,7 +106,8 @@ class os_access_mock : public os_access {
|
||||
std::shared_ptr<dir_reader> opendir(const std::string& path) const override {
|
||||
if (path.empty()) {
|
||||
std::vector<std::string> files{
|
||||
".", "..", "test.pl", "somelink", "somedir", "foo.pl", "ipsum.txt",
|
||||
".", "..", "test.pl", "somelink", "somedir",
|
||||
"foo.pl", "bar.pl", "baz.pl", "ipsum.txt",
|
||||
};
|
||||
|
||||
return std::make_shared<dir_reader_mock>(std::move(files));
|
||||
@ -113,8 +124,10 @@ class os_access_mock : public os_access {
|
||||
|
||||
void lstat(const std::string& path, struct ::stat* st) const override {
|
||||
const simplestat& sst = statmap[path];
|
||||
::memset(st, 0, sizeof(*st));
|
||||
std::memset(st, 0, sizeof(*st));
|
||||
st->st_ino = sst.st_ino;
|
||||
st->st_mode = sst.st_mode;
|
||||
st->st_nlink = sst.st_nlink;
|
||||
st->st_uid = sst.st_uid;
|
||||
st->st_gid = sst.st_gid;
|
||||
st->st_size = sst.st_size;
|
||||
@ -122,7 +135,6 @@ class os_access_mock : public os_access {
|
||||
st->st_mtime = sst.mtime;
|
||||
st->st_ctime = sst.ctime;
|
||||
st->st_rdev = sst.st_rdev;
|
||||
st->st_nlink = 1;
|
||||
}
|
||||
|
||||
std::string readlink(const std::string& path, size_t size) const override {
|
||||
@ -179,7 +191,8 @@ namespace {
|
||||
void basic_end_to_end_test(std::string const& compressor,
|
||||
unsigned block_size_bits, file_order_mode file_order,
|
||||
bool with_devices, bool with_specials, bool set_uid,
|
||||
bool set_gid, bool set_time, bool keep_all_times) {
|
||||
bool set_gid, bool set_time, bool keep_all_times,
|
||||
bool enable_nlink) {
|
||||
block_manager::config cfg;
|
||||
scanner_options options;
|
||||
|
||||
@ -228,6 +241,7 @@ void basic_end_to_end_test(std::string const& compressor,
|
||||
|
||||
filesystem_options opts;
|
||||
opts.block_cache.max_bytes = 1 << 20;
|
||||
opts.metadata.enable_nlink = enable_nlink;
|
||||
|
||||
filesystem_v2 fs(lgr, mm, opts);
|
||||
|
||||
@ -331,6 +345,150 @@ void basic_end_to_end_test(std::string const& compressor,
|
||||
} else {
|
||||
EXPECT_FALSE(entry);
|
||||
}
|
||||
|
||||
entry = fs.find("/");
|
||||
|
||||
ASSERT_TRUE(entry);
|
||||
auto dir = fs.opendir(*entry);
|
||||
ASSERT_TRUE(dir);
|
||||
EXPECT_EQ(9, fs.dirsize(*dir));
|
||||
|
||||
entry = fs.find("/somedir");
|
||||
|
||||
ASSERT_TRUE(entry);
|
||||
dir = fs.opendir(*entry);
|
||||
ASSERT_TRUE(dir);
|
||||
EXPECT_EQ(4 + 2 * with_devices + with_specials, fs.dirsize(*dir));
|
||||
|
||||
std::vector<std::string> names;
|
||||
for (size_t i = 0; i < fs.dirsize(*dir); ++i) {
|
||||
auto r = fs.readdir(*dir, i);
|
||||
ASSERT_TRUE(r);
|
||||
auto [view, name] = *r;
|
||||
names.emplace_back(name);
|
||||
}
|
||||
|
||||
std::vector<std::string> expected{
|
||||
".",
|
||||
"..",
|
||||
"bad",
|
||||
"ipsum.py",
|
||||
};
|
||||
|
||||
if (with_devices) {
|
||||
expected.emplace_back("null");
|
||||
}
|
||||
|
||||
if (with_specials) {
|
||||
expected.emplace_back("pipe");
|
||||
}
|
||||
|
||||
if (with_devices) {
|
||||
expected.emplace_back("zero");
|
||||
}
|
||||
|
||||
EXPECT_EQ(expected, names);
|
||||
|
||||
entry = fs.find("/foo.pl");
|
||||
ASSERT_TRUE(entry);
|
||||
|
||||
auto e2 = fs.find("/bar.pl");
|
||||
ASSERT_TRUE(e2);
|
||||
|
||||
EXPECT_EQ(entry->inode(), e2->inode());
|
||||
|
||||
struct ::stat st1, st2;
|
||||
ASSERT_EQ(0, fs.getattr(*entry, &st1));
|
||||
ASSERT_EQ(0, fs.getattr(*e2, &st2));
|
||||
|
||||
EXPECT_EQ(st1.st_ino, st2.st_ino);
|
||||
if (enable_nlink) {
|
||||
EXPECT_EQ(3, st1.st_nlink); // TODO: this should be 2
|
||||
EXPECT_EQ(3, st2.st_nlink); // TODO: this should be 2
|
||||
}
|
||||
|
||||
entry = fs.find("/");
|
||||
ASSERT_TRUE(entry);
|
||||
EXPECT_EQ(0, entry->inode());
|
||||
e2 = fs.find(0);
|
||||
ASSERT_TRUE(e2);
|
||||
EXPECT_EQ(e2->inode(), 0);
|
||||
e2 = fs.find(0, "baz.pl");
|
||||
ASSERT_TRUE(e2);
|
||||
EXPECT_GT(e2->inode(), 0);
|
||||
entry = fs.find(e2->inode());
|
||||
ASSERT_TRUE(entry);
|
||||
ASSERT_EQ(0, fs.getattr(*entry, &st1));
|
||||
EXPECT_EQ(23456, st1.st_size);
|
||||
e2 = fs.find(0, "somedir");
|
||||
ASSERT_TRUE(e2);
|
||||
ASSERT_EQ(0, fs.getattr(*e2, &st2));
|
||||
entry = fs.find(st2.st_ino, "ipsum.py");
|
||||
ASSERT_TRUE(entry);
|
||||
ASSERT_EQ(0, fs.getattr(*entry, &st1));
|
||||
EXPECT_EQ(10000, st1.st_size);
|
||||
EXPECT_EQ(0, fs.access(*entry, R_OK, 1000, 100));
|
||||
entry = fs.find(0, "baz.pl");
|
||||
ASSERT_TRUE(entry);
|
||||
EXPECT_EQ(set_uid ? EACCES : 0, fs.access(*entry, R_OK, 1337, 0));
|
||||
|
||||
using mptype = void (filesystem_v2::*)(
|
||||
std::function<void(entry_view, directory_view)> const&) const;
|
||||
|
||||
for (auto mp : {static_cast<mptype>(&filesystem_v2::walk),
|
||||
static_cast<mptype>(&filesystem_v2::walk_inode_order)}) {
|
||||
std::map<std::string, struct ::stat> entries;
|
||||
std::vector<int> inodes;
|
||||
|
||||
(fs.*mp)([&](entry_view e, directory_view d) {
|
||||
struct ::stat stbuf;
|
||||
ASSERT_EQ(0, fs.getattr(e, &stbuf));
|
||||
inodes.push_back(stbuf.st_ino);
|
||||
std::string path;
|
||||
if (e.inode() > 0) {
|
||||
if (auto dp = d.path(); !dp.empty()) {
|
||||
path += "/" + dp;
|
||||
}
|
||||
path += "/" + std::string(e.name());
|
||||
}
|
||||
EXPECT_TRUE(entries.emplace(path, stbuf).second);
|
||||
});
|
||||
|
||||
EXPECT_EQ(entries.size(), dwarfs::test::statmap.size() + 2 * with_devices +
|
||||
with_specials - 3);
|
||||
|
||||
for (auto const& [p, st] : entries) {
|
||||
auto const& stref = dwarfs::test::statmap.at(p);
|
||||
EXPECT_EQ(stref.st_mode, st.st_mode) << p;
|
||||
EXPECT_EQ(set_uid ? 0 : stref.st_uid, st.st_uid) << p;
|
||||
EXPECT_EQ(set_gid ? 0 : stref.st_gid, st.st_gid) << p;
|
||||
if (!S_ISDIR(st.st_mode)) {
|
||||
EXPECT_EQ(stref.st_size, st.st_size) << p;
|
||||
}
|
||||
}
|
||||
|
||||
if (mp == static_cast<mptype>(&filesystem_v2::walk_inode_order)) {
|
||||
EXPECT_TRUE(std::is_sorted(inodes.begin(), inodes.end()));
|
||||
}
|
||||
}
|
||||
|
||||
std::ostringstream dumpss;
|
||||
|
||||
fs.dump(dumpss, 9);
|
||||
|
||||
EXPECT_GT(dumpss.str().size(), 1000) << dumpss.str();
|
||||
|
||||
auto dyn = fs.metadata_as_dynamic();
|
||||
|
||||
EXPECT_TRUE(dyn.isObject());
|
||||
|
||||
auto json = fs.serialize_metadata_as_json(true);
|
||||
|
||||
EXPECT_GT(json.size(), 1000) << json;
|
||||
|
||||
json = fs.serialize_metadata_as_json(false);
|
||||
|
||||
EXPECT_GT(json.size(), 1000) << json;
|
||||
}
|
||||
|
||||
std::vector<std::string> const compressions{"null",
|
||||
@ -352,7 +510,8 @@ class compression_test
|
||||
std::tuple<std::string, unsigned, file_order_mode>> {};
|
||||
|
||||
class scanner_test : public testing::TestWithParam<
|
||||
std::tuple<bool, bool, bool, bool, bool, bool>> {};
|
||||
std::tuple<bool, bool, bool, bool, bool, bool, bool>> {
|
||||
};
|
||||
|
||||
TEST_P(compression_test, end_to_end) {
|
||||
auto [compressor, block_size_bits, file_order] = GetParam();
|
||||
@ -363,16 +522,16 @@ TEST_P(compression_test, end_to_end) {
|
||||
}
|
||||
|
||||
basic_end_to_end_test(compressor, block_size_bits, file_order, true, true,
|
||||
false, false, false, false);
|
||||
false, false, false, false, false);
|
||||
}
|
||||
|
||||
TEST_P(scanner_test, end_to_end) {
|
||||
auto [with_devices, with_specials, set_uid, set_gid, set_time,
|
||||
keep_all_times] = GetParam();
|
||||
auto [with_devices, with_specials, set_uid, set_gid, set_time, keep_all_times,
|
||||
enable_nlink] = GetParam();
|
||||
|
||||
basic_end_to_end_test(compressions[0], 15, file_order_mode::NONE,
|
||||
with_devices, with_specials, set_uid, set_gid, set_time,
|
||||
keep_all_times);
|
||||
keep_all_times, enable_nlink);
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
@ -386,5 +545,5 @@ INSTANTIATE_TEST_SUITE_P(
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
dwarfs, scanner_test,
|
||||
::testing::Combine(::testing::Bool(), ::testing::Bool(), ::testing::Bool(),
|
||||
::testing::Bool(), ::testing::Bool(),
|
||||
::testing::Bool(), ::testing::Bool(), ::testing::Bool(),
|
||||
::testing::Bool()));
|
||||
|
@ -21,6 +21,11 @@
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
// TODO: this test should be autogenerated somehow...
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <map>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
@ -163,13 +168,25 @@ std::vector<std::string> versions{
|
||||
"0.3.0",
|
||||
};
|
||||
|
||||
std::string format_sh = R"(#!/bin/bash
|
||||
find test/ src/ include/ -type f -name '*.[ch]*' | xargs -d $'\n' clang-format -i
|
||||
)";
|
||||
|
||||
struct ::stat make_stat(::mode_t mode, ::off_t size) {
|
||||
struct ::stat st;
|
||||
std::memset(&st, 0, sizeof(st));
|
||||
st.st_mode = mode;
|
||||
st.st_size = size;
|
||||
return st;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
using namespace dwarfs;
|
||||
|
||||
class compat : public testing::TestWithParam<std::string> {};
|
||||
class compat_metadata : public testing::TestWithParam<std::string> {};
|
||||
|
||||
TEST_P(compat, backwards_compatibility) {
|
||||
TEST_P(compat_metadata, backwards_compat) {
|
||||
std::ostringstream oss;
|
||||
stream_logger lgr(oss);
|
||||
auto filename =
|
||||
@ -180,7 +197,138 @@ TEST_P(compat, backwards_compatibility) {
|
||||
EXPECT_EQ(ref, meta);
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(dwarfs, compat, ::testing::ValuesIn(versions));
|
||||
INSTANTIATE_TEST_SUITE_P(dwarfs, compat_metadata,
|
||||
::testing::ValuesIn(versions));
|
||||
|
||||
class compat_filesystem
|
||||
: public testing::TestWithParam<std::tuple<std::string, bool>> {};
|
||||
|
||||
TEST_P(compat_filesystem, backwards_compat) {
|
||||
auto [version, enable_nlink] = GetParam();
|
||||
|
||||
std::ostringstream oss;
|
||||
stream_logger lgr(oss);
|
||||
auto filename = std::string(TEST_DATA_DIR "/compat-v") + version + ".dwarfs";
|
||||
|
||||
filesystem_options opts;
|
||||
opts.metadata.enable_nlink = enable_nlink;
|
||||
|
||||
filesystem_v2 fs(lgr, std::make_shared<mmap>(filename), opts);
|
||||
|
||||
auto json = fs.serialize_metadata_as_json(true);
|
||||
EXPECT_GT(json.size(), 1000) << json;
|
||||
|
||||
std::ostringstream dumpss;
|
||||
fs.dump(dumpss, 9);
|
||||
EXPECT_GT(dumpss.str().size(), 1000) << dumpss.str();
|
||||
|
||||
auto entry = fs.find("/format.sh");
|
||||
struct ::stat st;
|
||||
|
||||
ASSERT_TRUE(entry);
|
||||
EXPECT_EQ(0, fs.getattr(*entry, &st));
|
||||
EXPECT_EQ(94, st.st_size);
|
||||
EXPECT_EQ(S_IFREG | 0755, st.st_mode);
|
||||
EXPECT_EQ(1000, st.st_uid);
|
||||
EXPECT_EQ(100, st.st_gid);
|
||||
EXPECT_EQ(1606161908 + 1007022, st.st_atime);
|
||||
EXPECT_EQ(1606161908 + 94137, st.st_mtime);
|
||||
EXPECT_EQ(1606161908 + 94137, st.st_ctime);
|
||||
|
||||
EXPECT_EQ(0, fs.access(*entry, R_OK, 1000, 0));
|
||||
|
||||
auto inode = fs.open(*entry);
|
||||
EXPECT_GE(inode, 0);
|
||||
|
||||
std::vector<char> buf(st.st_size);
|
||||
auto rv = fs.read(inode, &buf[0], st.st_size, 0);
|
||||
EXPECT_EQ(rv, st.st_size);
|
||||
EXPECT_EQ(format_sh, std::string(buf.begin(), buf.end()));
|
||||
|
||||
entry = fs.find("/foo/bad");
|
||||
ASSERT_TRUE(entry);
|
||||
std::string link;
|
||||
EXPECT_EQ(fs.readlink(*entry, &link), 0);
|
||||
EXPECT_EQ(link, "../foo");
|
||||
|
||||
entry = fs.find(0, "foo");
|
||||
ASSERT_TRUE(entry);
|
||||
|
||||
auto dir = fs.opendir(*entry);
|
||||
ASSERT_TRUE(dir);
|
||||
EXPECT_EQ(5, fs.dirsize(*dir));
|
||||
|
||||
std::vector<std::string> names;
|
||||
for (size_t i = 0; i < fs.dirsize(*dir); ++i) {
|
||||
auto r = fs.readdir(*dir, i);
|
||||
ASSERT_TRUE(r);
|
||||
auto [view, name] = *r;
|
||||
names.emplace_back(name);
|
||||
}
|
||||
|
||||
std::vector<std::string> expected{
|
||||
".", "..", "bad", "bar", "bla.sh",
|
||||
};
|
||||
|
||||
EXPECT_EQ(expected, names);
|
||||
|
||||
std::map<std::string, struct ::stat> ref_entries{
|
||||
{"", make_stat(S_IFDIR | 0755, 8)},
|
||||
{"/bench.sh", make_stat(S_IFREG | 0644, 1517)},
|
||||
{"/dev", make_stat(S_IFDIR | 0755, 0)},
|
||||
{"/empty", make_stat(S_IFDIR | 0755, 1)},
|
||||
{"/empty/alsoempty", make_stat(S_IFDIR | 0755, 0)},
|
||||
{"/foo", make_stat(S_IFDIR | 0755, 3)},
|
||||
{"/foo/bad", make_stat(S_IFLNK | 0777, 6)},
|
||||
{"/foo/bar", make_stat(S_IFREG | 0644, 0)},
|
||||
{"/foo/bla.sh", make_stat(S_IFREG | 0644, 1517)},
|
||||
{"/foobar", make_stat(S_IFLNK | 0777, 7)},
|
||||
{"/format.sh", make_stat(S_IFREG | 0755, 94)},
|
||||
{"/perl-exec.sh", make_stat(S_IFREG | 0644, 87)},
|
||||
{"/test.py", make_stat(S_IFREG | 0644, 1012)},
|
||||
};
|
||||
|
||||
using mptype = void (filesystem_v2::*)(
|
||||
std::function<void(entry_view, directory_view)> const&) const;
|
||||
|
||||
for (auto mp : {static_cast<mptype>(&filesystem_v2::walk),
|
||||
static_cast<mptype>(&filesystem_v2::walk_inode_order)}) {
|
||||
std::map<std::string, struct ::stat> entries;
|
||||
std::vector<int> inodes;
|
||||
|
||||
(fs.*mp)([&](entry_view e, directory_view d) {
|
||||
struct ::stat stbuf;
|
||||
ASSERT_EQ(0, fs.getattr(e, &stbuf));
|
||||
inodes.push_back(stbuf.st_ino);
|
||||
std::string path;
|
||||
if (e.inode() > 0) {
|
||||
if (auto dp = d.path(); !dp.empty()) {
|
||||
path += "/" + dp;
|
||||
}
|
||||
path += "/" + std::string(e.name());
|
||||
}
|
||||
EXPECT_TRUE(entries.emplace(path, stbuf).second);
|
||||
});
|
||||
|
||||
EXPECT_EQ(entries.size(), ref_entries.size());
|
||||
|
||||
for (auto const& [p, st] : entries) {
|
||||
auto const& stref = ref_entries.at(p);
|
||||
EXPECT_EQ(stref.st_mode, st.st_mode) << p;
|
||||
EXPECT_EQ(1000, st.st_uid) << p;
|
||||
EXPECT_EQ(100, st.st_gid) << p;
|
||||
EXPECT_EQ(stref.st_size, st.st_size) << p;
|
||||
}
|
||||
|
||||
if (mp == static_cast<mptype>(&filesystem_v2::walk_inode_order)) {
|
||||
EXPECT_TRUE(std::is_sorted(inodes.begin(), inodes.end()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(dwarfs, compat_filesystem,
|
||||
::testing::Combine(::testing::ValuesIn(versions),
|
||||
::testing::Bool()));
|
||||
|
||||
class rewrite
|
||||
: public testing::TestWithParam<std::tuple<std::string, bool, bool>> {};
|
||||
|
Loading…
x
Reference in New Issue
Block a user