blob: 105d79e00e8f551e58a94acd7d2049f357e9d859 [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <fbl/vector.h>
#include <fs/lazy-dir.h>
#include <fs/pseudo-file.h>
#include <fs/vfs.h>
#include <unittest/unittest.h>
#include <utility>
namespace {
class DirentChecker {
public:
DirentChecker(const void* buffer, size_t length)
: current_(reinterpret_cast<const uint8_t*>(buffer)), remaining_(length) {}
bool ExpectEnd() {
BEGIN_HELPER;
EXPECT_EQ(0u, remaining_);
END_HELPER;
}
bool ExpectEntry(const char* name, uint32_t vtype) {
BEGIN_HELPER;
ASSERT_NE(0u, remaining_);
auto entry = reinterpret_cast<const vdirent_t*>(current_);
size_t entry_size = entry->size + sizeof(vdirent_t);
ASSERT_GE(remaining_, entry_size);
current_ += entry_size;
remaining_ -= entry_size;
EXPECT_BYTES_EQ(reinterpret_cast<const uint8_t*>(name),
reinterpret_cast<const uint8_t*>(entry->name), strlen(name), "name");
EXPECT_EQ(VTYPE_TO_DTYPE(vtype), entry->type);
END_HELPER;
}
private:
const uint8_t* current_;
size_t remaining_;
};
class TestLazyDirHelper : public fs::LazyDir {
public:
struct TestContent {
uint64_t id;
fbl::String name;
};
void GetContents(LazyEntryVector* out_vector) override {
out_vector->reserve(contents_.size());
for (const auto& content : contents_) {
out_vector->push_back({content.id, content.name, V_TYPE_FILE});
}
}
zx_status_t GetFile(fbl::RefPtr<fs::Vnode>* out, uint64_t id, fbl::String name) override {
last_output_file = fbl::AdoptRef(new fs::UnbufferedPseudoFile());
*out = last_output_file;
last_id = id;
last_name = name;
return ZX_OK;
}
void AddContent(TestContent content) {
contents_.push_back(std::move(content));
}
fbl::RefPtr<fs::Vnode> last_output_file;
uint64_t last_id;
fbl::String last_name;
private:
fbl::Vector<TestContent> contents_;
};
bool TestLazyDir() {
BEGIN_TEST;
TestLazyDirHelper test;
{
fs::vdircookie_t cookie = {};
uint8_t buffer[4096];
size_t len;
EXPECT_EQ(test.Readdir(&cookie, buffer, sizeof(buffer), &len), ZX_OK);
DirentChecker dc(buffer, len);
EXPECT_TRUE(dc.ExpectEntry(".", V_TYPE_DIR));
EXPECT_TRUE(dc.ExpectEnd());
}
test.AddContent({1, "test"});
{
fs::vdircookie_t cookie = {};
uint8_t buffer[4096];
size_t len;
EXPECT_EQ(test.Readdir(&cookie, buffer, sizeof(buffer), &len), ZX_OK);
DirentChecker dc(buffer, len);
EXPECT_TRUE(dc.ExpectEntry(".", V_TYPE_DIR));
EXPECT_TRUE(dc.ExpectEntry("test", V_TYPE_FILE));
EXPECT_TRUE(dc.ExpectEnd());
fbl::RefPtr<fs::Vnode> out;
EXPECT_EQ(test.Lookup(&out, "test"), ZX_OK);
EXPECT_EQ(1, test.last_id);
EXPECT_TRUE(strcmp("test", test.last_name.c_str()) == 0);
EXPECT_EQ(out.get(), test.last_output_file.get());
EXPECT_EQ(test.Lookup(&out, "test2"), ZX_ERR_NOT_FOUND);
}
test.AddContent({33, "aaaa"});
{
fs::vdircookie_t cookie = {};
uint8_t buffer[4096];
size_t len;
EXPECT_EQ(test.Readdir(&cookie, buffer, sizeof(buffer), &len), ZX_OK);
DirentChecker dc(buffer, len);
EXPECT_TRUE(dc.ExpectEntry(".", V_TYPE_DIR));
EXPECT_TRUE(dc.ExpectEntry("test", V_TYPE_FILE));
EXPECT_TRUE(dc.ExpectEntry("aaaa", V_TYPE_FILE));
EXPECT_TRUE(dc.ExpectEnd());
fbl::RefPtr<fs::Vnode> out;
EXPECT_EQ(test.Lookup(&out, "aaaa"), ZX_OK);
EXPECT_EQ(33, test.last_id);
EXPECT_TRUE(strcmp("aaaa", test.last_name.c_str()) == 0);
EXPECT_EQ(out.get(), test.last_output_file.get());
}
{
// Ensure manually setting cookie past entries excludes them, but leaves "."
fs::vdircookie_t cookie = {};
cookie.n = 30;
uint8_t buffer[4096];
size_t len;
EXPECT_EQ(test.Readdir(&cookie, buffer, sizeof(buffer), &len), ZX_OK);
DirentChecker dc(buffer, len);
EXPECT_TRUE(dc.ExpectEntry(".", V_TYPE_DIR));
EXPECT_TRUE(dc.ExpectEntry("aaaa", V_TYPE_FILE));
EXPECT_TRUE(dc.ExpectEnd());
// Expect that "." is missing when reusing the cookie.
EXPECT_EQ(test.Readdir(&cookie, buffer, sizeof(buffer), &len), ZX_OK);
dc = DirentChecker(buffer, len);
EXPECT_TRUE(dc.ExpectEnd());
}
END_TEST;
}
} // namespace
BEGIN_TEST_CASE(lazy_dir_tests)
RUN_TEST(TestLazyDir)
END_TEST_CASE(lazy_dir_tests)