blob: 4d5a363446b82ea572eb66cb7106903d9923ff60 [file] [log] [blame]
// Copyright 2019 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 <lib/async-loop/cpp/loop.h>
#include <lib/async-loop/default.h>
#include <zircon/errors.h>
#include <optional>
#include <type_traits>
#include <utility>
#include <zxtest/zxtest.h>
#include "fbl/ref_ptr.h"
#include "src/lib/storage/vfs/cpp/synchronous_vfs.h"
#include "src/lib/storage/vfs/cpp/vfs_types.h"
#include "src/lib/storage/vfs/cpp/vnode.h"
// "Vnode Contract Tests" verifies the runtime contracts enforced by the vnode APIs. They could be
// consistency checks or other invariants.
namespace {
// This vnode returns a file in |GetProtocols|, but a directory in |GetNodeInfoForProtocol|.
class ErraticVnode : public fs::Vnode {
public:
ErraticVnode() = default;
fs::VnodeProtocolSet GetProtocols() const final { return fs::VnodeProtocol::kFile; }
zx_status_t GetNodeInfoForProtocol(fs::VnodeProtocol protocol, fs::Rights,
fs::VnodeRepresentation* info) final {
EXPECT_EQ(protocol, fs::VnodeProtocol::kFile);
*info = fs::VnodeRepresentation::Directory();
return ZX_OK;
}
};
TEST(Vnode, ProtocolShouldAgreeWithNodeInfo) {
ErraticVnode vnode;
if (ZX_DEBUG_ASSERT_IMPLEMENTED) {
ASSERT_DEATH([&] {
fs::VnodeRepresentation info;
vnode.GetNodeInfo(fs::Rights::All(), &info);
});
}
}
// This vnode supports the connector, file, and directory protocol.
class PolymorphicVnode : public fs::Vnode {
public:
PolymorphicVnode() = default;
PolymorphicVnode(fs::VnodeProtocolSet expected_candidate)
: expected_candidate_(expected_candidate) {}
fs::VnodeProtocolSet GetProtocols() const final {
return fs::VnodeProtocol::kConnector | fs::VnodeProtocol::kFile | fs::VnodeProtocol::kDirectory;
}
zx_status_t GetNodeInfoForProtocol(fs::VnodeProtocol, fs::Rights,
fs::VnodeRepresentation*) final {
EXPECT_TRUE(false, "Should not be called");
return ZX_ERR_INTERNAL;
}
fs::VnodeProtocol Negotiate(fs::VnodeProtocolSet protocols) const final {
EXPECT_TRUE(expected_candidate_.has_value());
if (expected_candidate_.has_value()) {
EXPECT_EQ(protocols, expected_candidate_);
}
negotiate_called_ = true;
return protocols.first().value();
}
zx_status_t ConnectService(zx::channel) final { return ZX_OK; }
bool negotiate_called() const { return negotiate_called_; }
private:
mutable bool negotiate_called_ = false;
std::optional<fs::VnodeProtocolSet> expected_candidate_;
};
TEST(Vnode, NegotiateIsCalledIfMultipleCandidateProtocols) {
auto vnode = fbl::MakeRefCounted<PolymorphicVnode>(fs::VnodeProtocol::kConnector |
fs::VnodeProtocol::kFile);
fs::SynchronousVfs vfs;
zx::channel client_end, server_end;
ASSERT_OK(zx::channel::create(0u, &client_end, &server_end));
ASSERT_FALSE(vnode->negotiate_called());
ASSERT_OK(vfs.Serve(vnode, std::move(server_end),
fs::VnodeConnectionOptions::ReadOnly().set_not_directory()));
ASSERT_TRUE(vnode->negotiate_called());
}
TEST(Vnode, NegotiateIsNotCalledIfSingleCandidateProtocol) {
auto vnode = fbl::MakeRefCounted<PolymorphicVnode>();
async::Loop loop(&kAsyncLoopConfigNoAttachToCurrentThread);
fs::SynchronousVfs vfs(loop.dispatcher());
zx::channel client_end, server_end;
ASSERT_OK(zx::channel::create(0u, &client_end, &server_end));
ASSERT_FALSE(vnode->negotiate_called());
ASSERT_OK(vfs.Serve(vnode, std::move(server_end),
fs::VnodeConnectionOptions::ReadOnly().set_directory()));
ASSERT_FALSE(vnode->negotiate_called());
}
} // namespace