mirror of
https://github.com/facebook/sapling.git
synced 2024-10-09 08:18:15 +03:00
a6ae3edab9
Summary: This change makes it so that all of the C++ code related to the edenfs daemon is now contained in the eden/fs subdirectory. Reviewed By: bolinfest, wez Differential Revision: D4889053 fbshipit-source-id: d0bd4774cc0bdb5d1d6b6f47d716ecae52391f37
300 lines
9.0 KiB
C++
300 lines
9.0 KiB
C++
/*
|
|
* Copyright (c) 2016-present, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
*/
|
|
#include <gtest/gtest.h>
|
|
#include "eden/fs/inodes/FileInode.h"
|
|
#include "eden/fs/inodes/InodeBase.h"
|
|
#include "eden/fs/inodes/InodePtr.h"
|
|
#include "eden/fs/inodes/TreeInode.h"
|
|
#include "eden/fs/testharness/FakeTreeBuilder.h"
|
|
#include "eden/fs/testharness/TestMount.h"
|
|
#include "eden/fs/testharness/TestUtil.h"
|
|
#include "eden/fs/utils/test/TestChecks.h"
|
|
|
|
using namespace facebook::eden;
|
|
|
|
namespace facebook {
|
|
namespace eden {
|
|
/*
|
|
* InodePtrTestHelper is declared as a friend by InodeBase.
|
|
*
|
|
* We can use this class to contain any functions that need to access private
|
|
* inode state for the purpose of our tests.
|
|
*/
|
|
class InodePtrTestHelper {
|
|
public:
|
|
template <typename InodePtrType>
|
|
static uint32_t getRefcount(const InodePtrType& inode) {
|
|
return inode->ptrRefcount_.load(std::memory_order_acquire);
|
|
}
|
|
};
|
|
}
|
|
}
|
|
|
|
#define EXPECT_REFCOUNT(expected, inodePtr) \
|
|
EXPECT_EQ(expected, InodePtrTestHelper::getRefcount(inodePtr))
|
|
|
|
// The refcount for the root should be 3:
|
|
// - The InodeMap keeps 1 reference to the root inode
|
|
// - The .eden inode holds another
|
|
// - We hold a third reference during the tests below
|
|
static constexpr size_t kRootRefCount = 3;
|
|
|
|
TEST(InodePtr, constructionAndAssignment) {
|
|
FakeTreeBuilder emptyBuilder;
|
|
TestMount testMount{emptyBuilder};
|
|
|
|
// Get the root inode
|
|
auto rootPtr = testMount.getEdenMount()->getRootInode();
|
|
EXPECT_REFCOUNT(kRootRefCount, rootPtr);
|
|
EXPECT_TRUE(rootPtr);
|
|
|
|
{
|
|
// Construction through newPtrFromExisting()
|
|
auto ptr2 = TreeInodePtr::newPtrFromExisting(rootPtr.get());
|
|
EXPECT_REFCOUNT(kRootRefCount + 1, rootPtr);
|
|
EXPECT_EQ(rootPtr.get(), ptr2.get());
|
|
// reset()
|
|
ptr2.reset();
|
|
EXPECT_REFCOUNT(kRootRefCount, rootPtr);
|
|
EXPECT_FALSE(ptr2);
|
|
}
|
|
|
|
{
|
|
// Copy construction
|
|
auto ptr2 = rootPtr;
|
|
EXPECT_REFCOUNT(kRootRefCount + 1, rootPtr);
|
|
EXPECT_EQ(rootPtr.get(), ptr2.get());
|
|
}
|
|
// Decrement via destruction
|
|
EXPECT_REFCOUNT(kRootRefCount, rootPtr);
|
|
|
|
{
|
|
// Default construction, then copy assignment
|
|
TreeInodePtr ptr2;
|
|
EXPECT_FALSE(ptr2);
|
|
EXPECT_REFCOUNT(kRootRefCount, rootPtr);
|
|
ptr2 = rootPtr;
|
|
EXPECT_REFCOUNT(kRootRefCount + 1, rootPtr);
|
|
EXPECT_TRUE(ptr2);
|
|
EXPECT_EQ(rootPtr.get(), ptr2.get());
|
|
|
|
// Move construction
|
|
TreeInodePtr ptr3{std::move(ptr2)};
|
|
EXPECT_REFCOUNT(kRootRefCount + 1, rootPtr);
|
|
EXPECT_EQ(rootPtr.get(), ptr3.get());
|
|
EXPECT_TRUE(ptr3);
|
|
EXPECT_TRUE(nullptr == ptr2.get());
|
|
EXPECT_FALSE(ptr2);
|
|
|
|
// Move assignment
|
|
ptr2 = std::move(ptr3);
|
|
EXPECT_REFCOUNT(kRootRefCount + 1, rootPtr);
|
|
EXPECT_EQ(rootPtr.get(), ptr2.get());
|
|
EXPECT_TRUE(ptr2);
|
|
EXPECT_TRUE(nullptr == ptr3.get());
|
|
EXPECT_FALSE(ptr3);
|
|
|
|
// Try move assigning to the value it already points to
|
|
// This effectively decrements the refcount since the right-hand side gets
|
|
// reset but the left-hand side of the assignment stays the same.
|
|
ptr3 = rootPtr;
|
|
EXPECT_REFCOUNT(kRootRefCount + 2, rootPtr);
|
|
ptr2 = std::move(ptr3);
|
|
EXPECT_REFCOUNT(kRootRefCount + 1, rootPtr);
|
|
EXPECT_FALSE(ptr3);
|
|
EXPECT_TRUE(ptr2);
|
|
EXPECT_EQ(rootPtr.get(), ptr2.get());
|
|
}
|
|
EXPECT_REFCOUNT(kRootRefCount, rootPtr);
|
|
|
|
{
|
|
// Copy assignment from null
|
|
// First set ptr2 to non-null
|
|
TreeInodePtr ptr2 = rootPtr;
|
|
EXPECT_REFCOUNT(kRootRefCount + 1, rootPtr);
|
|
EXPECT_TRUE(ptr2);
|
|
TreeInodePtr nullTreePtr;
|
|
ptr2 = nullTreePtr;
|
|
EXPECT_REFCOUNT(kRootRefCount, rootPtr);
|
|
EXPECT_FALSE(ptr2);
|
|
EXPECT_FALSE(nullTreePtr);
|
|
|
|
// Move assignment from null
|
|
// First set ptr2 to non-null
|
|
ptr2 = rootPtr;
|
|
EXPECT_REFCOUNT(kRootRefCount + 1, rootPtr);
|
|
EXPECT_TRUE(ptr2);
|
|
ptr2 = std::move(nullTreePtr);
|
|
EXPECT_REFCOUNT(kRootRefCount, rootPtr);
|
|
EXPECT_FALSE(ptr2);
|
|
EXPECT_FALSE(nullTreePtr);
|
|
|
|
// Copy construction from null
|
|
TreeInodePtr ptr4{nullTreePtr};
|
|
EXPECT_REFCOUNT(kRootRefCount, rootPtr);
|
|
EXPECT_FALSE(ptr4);
|
|
|
|
// Move construction from null
|
|
TreeInodePtr ptr5{std::move(nullTreePtr)};
|
|
EXPECT_REFCOUNT(kRootRefCount, rootPtr);
|
|
EXPECT_FALSE(ptr5);
|
|
}
|
|
EXPECT_REFCOUNT(kRootRefCount, rootPtr);
|
|
}
|
|
|
|
TEST(InodePtr, baseConstructionAndAssignment) {
|
|
FakeTreeBuilder emptyBuilder;
|
|
TestMount testMount{emptyBuilder};
|
|
auto rootPtr = testMount.getEdenMount()->getRootInode();
|
|
EXPECT_REFCOUNT(kRootRefCount, rootPtr);
|
|
|
|
// Construct an InodePtr from TreeInodePtr
|
|
InodePtr basePtr = rootPtr;
|
|
EXPECT_REFCOUNT(kRootRefCount + 1, rootPtr);
|
|
EXPECT_EQ(rootPtr.get(), basePtr.get());
|
|
EXPECT_TRUE(basePtr);
|
|
|
|
{
|
|
// Move construction from TreeInodePtr
|
|
TreeInodePtr root2 = rootPtr;
|
|
EXPECT_REFCOUNT(kRootRefCount + 2, rootPtr);
|
|
EXPECT_EQ(rootPtr.get(), root2.get());
|
|
InodePtr basePtr2(std::move(root2));
|
|
EXPECT_REFCOUNT(kRootRefCount + 2, rootPtr);
|
|
EXPECT_EQ(rootPtr.get(), basePtr2.get());
|
|
EXPECT_TRUE(basePtr2);
|
|
EXPECT_FALSE(root2);
|
|
|
|
// Copy assignment from TreeInodePtr
|
|
InodePtr basePtr3;
|
|
EXPECT_FALSE(basePtr3);
|
|
basePtr3 = rootPtr;
|
|
EXPECT_TRUE(basePtr3);
|
|
EXPECT_TRUE(rootPtr);
|
|
EXPECT_EQ(rootPtr.get(), basePtr3.get());
|
|
EXPECT_REFCOUNT(kRootRefCount + 3, rootPtr);
|
|
|
|
// Move assignment from TreeInodePtr
|
|
basePtr3.reset();
|
|
EXPECT_REFCOUNT(kRootRefCount + 2, rootPtr);
|
|
root2 = rootPtr;
|
|
EXPECT_REFCOUNT(kRootRefCount + 3, rootPtr);
|
|
EXPECT_FALSE(basePtr3);
|
|
basePtr3 = std::move(root2);
|
|
EXPECT_TRUE(basePtr3);
|
|
EXPECT_FALSE(root2);
|
|
EXPECT_REFCOUNT(kRootRefCount + 3, rootPtr);
|
|
|
|
// Try move assigning to the value it already points to
|
|
root2 = rootPtr;
|
|
EXPECT_REFCOUNT(kRootRefCount + 4, rootPtr);
|
|
basePtr3 = std::move(root2);
|
|
EXPECT_REFCOUNT(kRootRefCount + 3, rootPtr);
|
|
EXPECT_FALSE(root2);
|
|
EXPECT_TRUE(basePtr3);
|
|
EXPECT_EQ(rootPtr.get(), basePtr3.get());
|
|
}
|
|
EXPECT_REFCOUNT(kRootRefCount + 1, rootPtr);
|
|
}
|
|
|
|
TEST(InodePtr, baseCasting) {
|
|
FakeTreeBuilder emptyBuilder;
|
|
TestMount testMount{emptyBuilder};
|
|
auto rootPtr = testMount.getEdenMount()->getRootInode();
|
|
EXPECT_REFCOUNT(kRootRefCount, rootPtr);
|
|
|
|
// Construct an InodePtr from TreeInodePtr
|
|
InodePtr basePtr = rootPtr;
|
|
EXPECT_REFCOUNT(kRootRefCount + 1, rootPtr);
|
|
|
|
// Test the various asTree* methods
|
|
{
|
|
// Raw pointer versions
|
|
EXPECT_EQ(rootPtr.get(), basePtr.get());
|
|
EXPECT_EQ(rootPtr.get(), basePtr.asTree());
|
|
EXPECT_EQ(rootPtr.get(), basePtr.asTreeOrNull());
|
|
EXPECT_REFCOUNT(kRootRefCount + 1, rootPtr);
|
|
}
|
|
{
|
|
// Copy versions
|
|
auto rawPtr = basePtr.asTree();
|
|
auto rawPtr2 = basePtr.asTreeOrNull();
|
|
EXPECT_REFCOUNT(kRootRefCount + 1, rootPtr);
|
|
auto ptr2 = basePtr.asTreePtr();
|
|
EXPECT_TRUE(basePtr);
|
|
EXPECT_EQ(rootPtr.get(), basePtr.get());
|
|
EXPECT_EQ(rootPtr.get(), ptr2.get());
|
|
EXPECT_TRUE(ptr2);
|
|
EXPECT_REFCOUNT(kRootRefCount + 2, rootPtr);
|
|
auto ptr3 = basePtr.asTreePtrOrNull();
|
|
EXPECT_REFCOUNT(kRootRefCount + 3, rootPtr);
|
|
}
|
|
EXPECT_REFCOUNT(kRootRefCount + 1, rootPtr);
|
|
{
|
|
// Move versions
|
|
auto base2 = basePtr;
|
|
EXPECT_REFCOUNT(kRootRefCount + 2, rootPtr);
|
|
auto ptr2 = std::move(base2).asTreePtr();
|
|
EXPECT_REFCOUNT(kRootRefCount + 2, rootPtr);
|
|
EXPECT_FALSE(base2);
|
|
EXPECT_EQ(rootPtr.get(), ptr2.get());
|
|
|
|
ptr2.reset();
|
|
EXPECT_REFCOUNT(kRootRefCount + 1, rootPtr);
|
|
base2 = basePtr;
|
|
EXPECT_REFCOUNT(kRootRefCount + 2, rootPtr);
|
|
ptr2 = std::move(base2).asTreePtrOrNull();
|
|
EXPECT_REFCOUNT(kRootRefCount + 2, rootPtr);
|
|
EXPECT_FALSE(base2);
|
|
EXPECT_EQ(rootPtr.get(), ptr2.get());
|
|
}
|
|
EXPECT_REFCOUNT(kRootRefCount + 1, rootPtr);
|
|
|
|
// Test the various asFile* methods
|
|
{
|
|
// Raw pointer versions
|
|
EXPECT_EQ(rootPtr.get(), basePtr.get());
|
|
EXPECT_THROW_ERRNO(basePtr.asFile(), EISDIR);
|
|
EXPECT_TRUE(nullptr == basePtr.asFileOrNull());
|
|
EXPECT_REFCOUNT(kRootRefCount + 1, rootPtr);
|
|
auto rawPtr = basePtr.asFileOrNull();
|
|
EXPECT_TRUE(nullptr == rawPtr);
|
|
EXPECT_REFCOUNT(kRootRefCount + 1, rootPtr);
|
|
}
|
|
{
|
|
// Copy versions
|
|
EXPECT_THROW_ERRNO(basePtr.asFile(), EISDIR);
|
|
EXPECT_THROW_ERRNO(basePtr.asFilePtr(), EISDIR);
|
|
EXPECT_REFCOUNT(kRootRefCount + 1, rootPtr);
|
|
auto filePtr = basePtr.asFilePtrOrNull();
|
|
EXPECT_FALSE(filePtr);
|
|
EXPECT_REFCOUNT(kRootRefCount + 1, rootPtr);
|
|
}
|
|
{
|
|
// Move versions
|
|
auto base2 = basePtr;
|
|
EXPECT_REFCOUNT(kRootRefCount + 2, rootPtr);
|
|
|
|
EXPECT_THROW_ERRNO(std::move(base2).asFile(), EISDIR);
|
|
EXPECT_TRUE(base2);
|
|
EXPECT_REFCOUNT(kRootRefCount + 2, rootPtr);
|
|
|
|
EXPECT_THROW_ERRNO(std::move(base2).asFilePtr(), EISDIR);
|
|
EXPECT_TRUE(base2);
|
|
EXPECT_REFCOUNT(kRootRefCount + 2, rootPtr);
|
|
|
|
auto filePtr = std::move(base2).asFilePtrOrNull();
|
|
EXPECT_FALSE(filePtr);
|
|
EXPECT_TRUE(base2);
|
|
EXPECT_REFCOUNT(kRootRefCount + 2, rootPtr);
|
|
}
|
|
EXPECT_REFCOUNT(kRootRefCount + 1, rootPtr);
|
|
}
|