mirror of
https://github.com/facebook/sapling.git
synced 2024-10-07 15:27:13 +03:00
compute permission bits based on FileType in TreeEntry
Summary: Our Model TreeEntry code was a bit too general - in reality, both git and hg only support a handful of specific tree entries: regular files, executable files, symlinks, and trees. (git also supports submodules.) This diff delays the expansion of a TreeEntry's type into a full mode_t. Reviewed By: simpkins Differential Revision: D6980003 fbshipit-source-id: 73729208000668078a180b728d7e0bb9169c6f3c
This commit is contained in:
parent
9dbdeaa4f4
commit
895a0196d9
@ -67,6 +67,10 @@ inline void PrintTo(
|
||||
|
||||
namespace {
|
||||
|
||||
bool isExecutable(int perms) {
|
||||
return perms & S_IXUSR;
|
||||
}
|
||||
|
||||
/**
|
||||
* An enum to control behavior for many of the checkout tests.
|
||||
*
|
||||
@ -230,7 +234,8 @@ void testAddFile(
|
||||
|
||||
// Prepare a second tree, by starting with builder1 then adding the new file
|
||||
auto builder2 = builder1.clone();
|
||||
builder2.setFile(newFilePath, "this is the new file contents\n", perms);
|
||||
builder2.setFile(
|
||||
newFilePath, "this is the new file contents\n", isExecutable(perms));
|
||||
builder2.finalize(testMount.getBackingStore(), true);
|
||||
auto commit2 = testMount.getBackingStore()->putCommit("2", builder2);
|
||||
commit2->setReady();
|
||||
@ -258,7 +263,6 @@ void runAddFileTests(folly::StringPiece path) {
|
||||
for (auto loadType : kAddLoadTypes) {
|
||||
SCOPED_TRACE(folly::to<string>("add ", path, " load type ", loadType));
|
||||
testAddFile(path, loadType);
|
||||
testAddFile(path, loadType, 0444);
|
||||
testAddFile(path, loadType, 0755);
|
||||
}
|
||||
}
|
||||
@ -332,13 +336,13 @@ void testModifyFile(
|
||||
builder1.setFile("a/test.txt", "test contents\n");
|
||||
builder1.setFile("a/b/dddd.c", "this is dddd.c\n");
|
||||
builder1.setFile("a/b/tttt.c", "this is tttt.c\n");
|
||||
builder1.setFile(path, contents1, perms1);
|
||||
builder1.setFile(path, contents1, isExecutable(perms1));
|
||||
TestMount testMount{builder1};
|
||||
testMount.getClock().advance(9876min);
|
||||
|
||||
// Prepare the second tree
|
||||
auto builder2 = builder1.clone();
|
||||
builder2.replaceFile(path, contents2, perms2);
|
||||
builder2.replaceFile(path, contents2, isExecutable(perms2));
|
||||
builder2.finalize(testMount.getBackingStore(), true);
|
||||
auto commit2 = testMount.getBackingStore()->putCommit("2", builder2);
|
||||
commit2->setReady();
|
||||
@ -450,12 +454,12 @@ void testModifyConflict(
|
||||
workingDirBuilder.setFile("a/test.txt", "test contents\n");
|
||||
workingDirBuilder.setFile("a/b/dddd.c", "this is dddd.c\n");
|
||||
workingDirBuilder.setFile("a/b/tttt.c", "this is tttt.c\n");
|
||||
workingDirBuilder.setFile(path, currentContents, currentPerms);
|
||||
workingDirBuilder.setFile(path, currentContents, isExecutable(currentPerms));
|
||||
TestMount testMount{workingDirBuilder};
|
||||
|
||||
// Prepare the "before" tree
|
||||
auto builder1 = workingDirBuilder.clone();
|
||||
builder1.replaceFile(path, contents1, perms1);
|
||||
builder1.replaceFile(path, contents1, isExecutable(perms1));
|
||||
builder1.finalize(testMount.getBackingStore(), true);
|
||||
// Reset the EdenMount to point at the tree from builder1, even though the
|
||||
// contents are still from workingDirBuilder. This lets us trigger the
|
||||
@ -471,7 +475,7 @@ void testModifyConflict(
|
||||
|
||||
// Prepare the destination tree
|
||||
auto builder2 = builder1.clone();
|
||||
builder2.replaceFile(path, contents2, perms2);
|
||||
builder2.replaceFile(path, contents2, isExecutable(perms2));
|
||||
builder2.replaceFile("a/b/dddd.c", "new dddd contents\n");
|
||||
builder2.finalize(testMount.getBackingStore(), true);
|
||||
auto commit2 = testMount.getBackingStore()->putCommit("b", builder2);
|
||||
|
@ -483,7 +483,7 @@ void testResetFileModeChanged(bool loadInodes) {
|
||||
|
||||
DiffTest t;
|
||||
auto b2 = t.getBuilder().clone();
|
||||
b2.replaceFile("src/1.txt", "This is src/1.txt.\n", 0755);
|
||||
b2.replaceFile("src/1.txt", "This is src/1.txt.\n", true);
|
||||
|
||||
auto result = t.resetCommitAndDiff(b2, loadInodes);
|
||||
EXPECT_THAT(result.getErrors(), UnorderedElementsAre());
|
||||
|
@ -74,7 +74,7 @@ TEST(InodeMap, simpleLookups) {
|
||||
TEST(InodeMap, asyncLookup) {
|
||||
auto builder = FakeTreeBuilder();
|
||||
builder.setFile("README", "docs go here\n");
|
||||
builder.setFile("src/runme.sh", "#!/bin/sh\necho hello world\n", 0755);
|
||||
builder.setFile("src/runme.sh", "#!/bin/sh\necho hello world\n", true);
|
||||
builder.setFile("src/test.txt", "this is a test file");
|
||||
TestMount testMount{builder, false};
|
||||
|
||||
@ -100,7 +100,7 @@ TEST(InodeMap, asyncLookup) {
|
||||
TEST(InodeMap, asyncError) {
|
||||
auto builder = FakeTreeBuilder();
|
||||
builder.setFile("README", "docs go here\n");
|
||||
builder.setFile("src/runme.sh", "#!/bin/sh\necho hello world\n", 0755);
|
||||
builder.setFile("src/runme.sh", "#!/bin/sh\necho hello world\n", true);
|
||||
builder.setFile("src/test.txt", "this is a test file");
|
||||
TestMount testMount{builder, false};
|
||||
|
||||
|
@ -26,44 +26,47 @@ std::string TreeEntry::toLogString() const {
|
||||
case FileType::REGULAR_FILE:
|
||||
fileTypeChar = 'f';
|
||||
break;
|
||||
case FileType::EXECUTABLE_FILE:
|
||||
fileTypeChar = 'x';
|
||||
break;
|
||||
case FileType::SYMLINK:
|
||||
fileTypeChar = 'l';
|
||||
break;
|
||||
}
|
||||
std::array<char, 4> modeStr;
|
||||
modeStr[0] = fileTypeChar;
|
||||
modeStr[1] = (ownerPermissions_ & 0b100) ? 'r' : '-';
|
||||
modeStr[2] = (ownerPermissions_ & 0b010) ? 'w' : '-';
|
||||
modeStr[3] = (ownerPermissions_ & 0b001) ? 'x' : '-';
|
||||
|
||||
return folly::to<std::string>(
|
||||
"(",
|
||||
name_,
|
||||
", ",
|
||||
hash_.toString(),
|
||||
", ",
|
||||
folly::StringPiece{modeStr},
|
||||
")");
|
||||
"(", name_, ", ", hash_.toString(), ", ", fileTypeChar, ")");
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, FileType type) {
|
||||
switch (type) {
|
||||
case FileType::DIRECTORY:
|
||||
return os << "DIRECTORY";
|
||||
case FileType::REGULAR_FILE:
|
||||
return os << "REGULAR_FILE";
|
||||
case FileType::EXECUTABLE_FILE:
|
||||
return os << "EXECUTABLE_FILE";
|
||||
case FileType::SYMLINK:
|
||||
return os << "SYMLINK";
|
||||
}
|
||||
|
||||
return os << "FileType::" << int(type);
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, TreeEntryType type) {
|
||||
switch (type) {
|
||||
case TreeEntryType::TREE:
|
||||
os << "TREE";
|
||||
return os;
|
||||
return os << "TREE";
|
||||
case TreeEntryType::BLOB:
|
||||
os << "BLOB";
|
||||
return os;
|
||||
return os << "BLOB";
|
||||
}
|
||||
|
||||
os << "TreeEntryType::" << int(type);
|
||||
return os;
|
||||
return os << "TreeEntryType::" << int(type);
|
||||
}
|
||||
|
||||
bool operator==(const TreeEntry& entry1, const TreeEntry& entry2) {
|
||||
return (entry1.getHash() == entry2.getHash()) &&
|
||||
(entry1.getFileType() == entry2.getFileType()) &&
|
||||
(entry1.getOwnerPermissions() == entry2.getOwnerPermissions()) &&
|
||||
(entry1.getName() == entry2.getName());
|
||||
}
|
||||
|
||||
|
@ -14,6 +14,8 @@
|
||||
#include "eden/fs/utils/PathFuncs.h"
|
||||
|
||||
#include <folly/String.h>
|
||||
#include <folly/experimental/logging/xlog.h>
|
||||
#include <sys/stat.h>
|
||||
#include <iosfwd>
|
||||
|
||||
namespace facebook {
|
||||
@ -21,19 +23,16 @@ namespace eden {
|
||||
|
||||
class Hash;
|
||||
|
||||
enum class TreeEntryType {
|
||||
enum class TreeEntryType : uint8_t {
|
||||
BLOB,
|
||||
TREE,
|
||||
};
|
||||
|
||||
enum class FileType {
|
||||
// The values for these enum entries correspond to the ones we would expect to
|
||||
// find in <sys/stat.h>. We hardcode them just in case the values in the
|
||||
// users's <sys/stat.h> are different. Note that Git performs a similar check:
|
||||
// https://github.com/git/git/blob/v2.7.4/configure.ac#L912-L917
|
||||
DIRECTORY = 0040,
|
||||
REGULAR_FILE = 0100,
|
||||
SYMLINK = 0120,
|
||||
enum class FileType : uint8_t {
|
||||
DIRECTORY,
|
||||
REGULAR_FILE,
|
||||
EXECUTABLE_FILE,
|
||||
SYMLINK,
|
||||
};
|
||||
|
||||
class TreeEntry {
|
||||
@ -41,12 +40,8 @@ class TreeEntry {
|
||||
explicit TreeEntry(
|
||||
const Hash& hash,
|
||||
folly::StringPiece name,
|
||||
FileType fileType,
|
||||
uint8_t ownerPermissions)
|
||||
: fileType_(fileType),
|
||||
ownerPermissions_(ownerPermissions),
|
||||
hash_(hash),
|
||||
name_(PathComponentPiece(name)) {}
|
||||
FileType fileType)
|
||||
: fileType_(fileType), hash_(hash), name_(PathComponentPiece(name)) {}
|
||||
|
||||
const Hash& getHash() const {
|
||||
return hash_;
|
||||
@ -65,40 +60,29 @@ class TreeEntry {
|
||||
return fileType_;
|
||||
}
|
||||
|
||||
uint8_t getOwnerPermissions() const {
|
||||
return ownerPermissions_;
|
||||
}
|
||||
|
||||
mode_t getMode() const {
|
||||
mode_t mode = static_cast<mode_t>(fileType_) << 9;
|
||||
// We should always honor the explicit owner permissions.
|
||||
mode |= ownerPermissions_ << 6;
|
||||
|
||||
// We propagate the 'r' and 'x' values for the owner to group and other.
|
||||
mode |= (ownerPermissions_ & 0b101) << 3;
|
||||
mode |= (ownerPermissions_ & 0b101);
|
||||
return mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the owner permissions from a mode_t value.
|
||||
*
|
||||
* This can be used to construct the ownerPermissions parameter needed for
|
||||
* the TreeEntry constructor.
|
||||
*/
|
||||
static uint8_t modeToOwnerPermissions(mode_t mode) {
|
||||
return (mode >> 6) & 0b111;
|
||||
switch (fileType_) {
|
||||
case FileType::DIRECTORY:
|
||||
return S_IFDIR | 0755;
|
||||
case FileType::REGULAR_FILE:
|
||||
return S_IFREG | 0644;
|
||||
case FileType::EXECUTABLE_FILE:
|
||||
return S_IFREG | 0755;
|
||||
case FileType::SYMLINK:
|
||||
return S_IFLNK | 0755;
|
||||
}
|
||||
XLOG(FATAL) << "illegal file type " << int(fileType_);
|
||||
}
|
||||
|
||||
std::string toLogString() const;
|
||||
|
||||
private:
|
||||
FileType fileType_;
|
||||
uint8_t ownerPermissions_;
|
||||
Hash hash_;
|
||||
PathComponent name_;
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, FileType type);
|
||||
std::ostream& operator<<(std::ostream& os, TreeEntryType type);
|
||||
bool operator==(const TreeEntry& entry1, const TreeEntry& entry2);
|
||||
bool operator!=(const TreeEntry& entry1, const TreeEntry& entry2);
|
||||
|
@ -28,9 +28,6 @@ using std::vector;
|
||||
namespace facebook {
|
||||
namespace eden {
|
||||
|
||||
const int RWX = 0b111;
|
||||
const int RW_ = 0b110;
|
||||
|
||||
enum GitModeMask {
|
||||
DIRECTORY = 040000,
|
||||
GIT_LINK = 0160000,
|
||||
@ -80,19 +77,14 @@ std::unique_ptr<Tree> deserializeGitTree(
|
||||
|
||||
// Determine the individual fields from the mode.
|
||||
FileType fileType;
|
||||
uint8_t ownerPermissions;
|
||||
if (mode == GitModeMask::DIRECTORY) {
|
||||
fileType = FileType::DIRECTORY;
|
||||
ownerPermissions = RWX;
|
||||
} else if (mode == GitModeMask::REGULAR_FILE) {
|
||||
fileType = FileType::REGULAR_FILE;
|
||||
ownerPermissions = RW_;
|
||||
} else if (mode == GitModeMask::REGULAR_EXECUTABLE_FILE) {
|
||||
fileType = FileType::REGULAR_FILE;
|
||||
ownerPermissions = RWX;
|
||||
fileType = FileType::EXECUTABLE_FILE;
|
||||
} else if (mode == GitModeMask::SYMLINK) {
|
||||
fileType = FileType::SYMLINK;
|
||||
ownerPermissions = RWX;
|
||||
} else if (mode == GitModeMask::GIT_LINK) {
|
||||
throw std::domain_error(folly::sformat(
|
||||
"Gitlinks are not currently supported: {:o} in object {}",
|
||||
@ -105,8 +97,7 @@ std::unique_ptr<Tree> deserializeGitTree(
|
||||
hash.toString()));
|
||||
}
|
||||
|
||||
entries.emplace_back(
|
||||
Hash(hashBytes), std::move(name), fileType, ownerPermissions);
|
||||
entries.emplace_back(Hash(hashBytes), std::move(name), fileType);
|
||||
}
|
||||
|
||||
return std::make_unique<Tree>(std::move(entries), hash);
|
||||
@ -166,12 +157,10 @@ void GitTreeSerializer::addEntry(const TreeEntry& entry) {
|
||||
// affect the final tree hash.)
|
||||
|
||||
mode_t mode = 0;
|
||||
if (entry.getFileType() == FileType::REGULAR_FILE) {
|
||||
if (entry.getOwnerPermissions() & 0001) {
|
||||
mode = GitModeMask::REGULAR_EXECUTABLE_FILE;
|
||||
} else {
|
||||
mode = GitModeMask::REGULAR_FILE;
|
||||
}
|
||||
if (entry.getFileType() == FileType::EXECUTABLE_FILE) {
|
||||
mode = GitModeMask::REGULAR_EXECUTABLE_FILE;
|
||||
} else if (entry.getFileType() == FileType::REGULAR_FILE) {
|
||||
mode = GitModeMask::REGULAR_FILE;
|
||||
} else if (entry.getFileType() == FileType::DIRECTORY) {
|
||||
mode = GitModeMask::DIRECTORY;
|
||||
} else if (entry.getFileType() == FileType::SYMLINK) {
|
||||
|
@ -78,7 +78,6 @@ TEST(GitTree, testDeserialize) {
|
||||
EXPECT_EQ(".babelrc", babelrc.getName());
|
||||
EXPECT_EQ(facebook::eden::TreeEntryType::BLOB, babelrc.getType());
|
||||
EXPECT_EQ(facebook::eden::FileType::REGULAR_FILE, babelrc.getFileType());
|
||||
EXPECT_EQ(0b0110, babelrc.getOwnerPermissions());
|
||||
EXPECT_EQ(
|
||||
babelrc.getName(),
|
||||
tree->getEntryAt(PathComponentPiece(".babelrc")).getName());
|
||||
@ -91,8 +90,8 @@ TEST(GitTree, testDeserialize) {
|
||||
EXPECT_EQ("nuclide-start-server", nuclideStartServer.getName());
|
||||
EXPECT_EQ(facebook::eden::TreeEntryType::BLOB, nuclideStartServer.getType());
|
||||
EXPECT_EQ(
|
||||
facebook::eden::FileType::REGULAR_FILE, nuclideStartServer.getFileType());
|
||||
EXPECT_EQ(0b0111, nuclideStartServer.getOwnerPermissions());
|
||||
facebook::eden::FileType::EXECUTABLE_FILE,
|
||||
nuclideStartServer.getFileType());
|
||||
EXPECT_EQ(
|
||||
nuclideStartServer.getName(),
|
||||
tree->getEntryAt(PathComponentPiece("nuclide-start-server")).getName());
|
||||
@ -103,7 +102,6 @@ TEST(GitTree, testDeserialize) {
|
||||
EXPECT_EQ("lib", lib.getName());
|
||||
EXPECT_EQ(facebook::eden::TreeEntryType::TREE, lib.getType());
|
||||
EXPECT_EQ(facebook::eden::FileType::DIRECTORY, lib.getFileType());
|
||||
EXPECT_EQ(0b0111, lib.getOwnerPermissions());
|
||||
EXPECT_EQ(
|
||||
lib.getName(), tree->getEntryAt(PathComponentPiece("lib")).getName());
|
||||
|
||||
@ -150,7 +148,6 @@ TEST(GitTree, testDeserializeWithSymlink) {
|
||||
EXPECT_EQ("contributing.md", contributing.getName());
|
||||
EXPECT_EQ(facebook::eden::TreeEntryType::BLOB, contributing.getType());
|
||||
EXPECT_EQ(facebook::eden::FileType::SYMLINK, contributing.getFileType());
|
||||
EXPECT_EQ(0b0111, contributing.getOwnerPermissions());
|
||||
}
|
||||
|
||||
TEST(GitTree, deserializeEmpty) {
|
||||
@ -226,40 +223,35 @@ TEST(GitTree, serializeTree) {
|
||||
serializer.addEntry(TreeEntry(
|
||||
Hash("c66788d87933862e2111a86304b705dd90bbd427"),
|
||||
"README.md",
|
||||
FileType::REGULAR_FILE,
|
||||
0b110));
|
||||
FileType::REGULAR_FILE));
|
||||
serializer.addEntry(TreeEntry(
|
||||
Hash("a3c8e5c25e5523322f0ea490173dbdc1d844aefb"),
|
||||
"apm-rest-api.md",
|
||||
FileType::REGULAR_FILE,
|
||||
0b110));
|
||||
"run-tests.sh",
|
||||
FileType::EXECUTABLE_FILE));
|
||||
serializer.addEntry(TreeEntry(
|
||||
Hash("de0b8287939193ed239834991be65b96cbfc4508"),
|
||||
"build-instructions",
|
||||
FileType::DIRECTORY,
|
||||
0b111));
|
||||
FileType::DIRECTORY));
|
||||
serializer.addEntry(TreeEntry(
|
||||
Hash("4576635ff317960be244b1c4adfe2a6eb2eb024d"),
|
||||
"contributing-to-packages.md",
|
||||
FileType::REGULAR_FILE,
|
||||
0b110));
|
||||
FileType::REGULAR_FILE));
|
||||
serializer.addEntry(TreeEntry(
|
||||
Hash("44fcc63439371c8c829df00eec6aedbdc4d0e4cd"),
|
||||
"contributing.md",
|
||||
FileType::SYMLINK,
|
||||
0b111));
|
||||
FileType::SYMLINK));
|
||||
|
||||
auto buf = serializer.finalize();
|
||||
|
||||
// Make sure the tree hash is what we expect
|
||||
auto treeHash = Hash::sha1(&buf);
|
||||
EXPECT_EQ(Hash("013b7865a6da317bc8d82c7225eb93615f1b1eca"), treeHash);
|
||||
EXPECT_EQ(Hash("dc2c3be02dd3753c64c8f196a33522905c88c435"), treeHash);
|
||||
|
||||
// Make sure we can deserialize it and get back the expected entries.
|
||||
auto tree = deserializeGitTree(treeHash, &buf);
|
||||
EXPECT_EQ(5, tree->getTreeEntries().size());
|
||||
EXPECT_EQ("README.md", tree->getEntryAt(0).getName());
|
||||
EXPECT_EQ("apm-rest-api.md", tree->getEntryAt(1).getName());
|
||||
EXPECT_EQ("run-tests.sh", tree->getEntryAt(1).getName());
|
||||
EXPECT_EQ("build-instructions", tree->getEntryAt(2).getName());
|
||||
EXPECT_EQ("contributing-to-packages.md", tree->getEntryAt(3).getName());
|
||||
EXPECT_EQ("contributing.md", tree->getEntryAt(4).getName());
|
||||
@ -274,8 +266,7 @@ TEST(GitTree, moveSerializer) {
|
||||
serializer1.addEntry(TreeEntry(
|
||||
Hash("3b18e512dba79e4c8300dd08aeb37f8e728b8dad"),
|
||||
"README.md",
|
||||
FileType::REGULAR_FILE,
|
||||
0b110));
|
||||
FileType::REGULAR_FILE));
|
||||
|
||||
serializer2 = std::move(serializer1);
|
||||
}
|
||||
@ -283,8 +274,7 @@ TEST(GitTree, moveSerializer) {
|
||||
serializer2.addEntry(TreeEntry(
|
||||
Hash("43b71c903ff52b9885bd36f3866324ef60e27b9b"),
|
||||
"eden",
|
||||
FileType::DIRECTORY,
|
||||
0b111));
|
||||
FileType::DIRECTORY));
|
||||
|
||||
// Make sure the tree hash is what we expect
|
||||
auto buf = serializer2.finalize();
|
||||
|
@ -16,53 +16,33 @@ using namespace facebook::eden;
|
||||
|
||||
TEST(TreeEntry, modeAndLogString) {
|
||||
TreeEntry rwFile(
|
||||
makeTestHash("faceb00c"), "file.txt", FileType::REGULAR_FILE, 0b110);
|
||||
makeTestHash("faceb00c"), "file.txt", FileType::REGULAR_FILE);
|
||||
EXPECT_EQ(S_IFREG | S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH, rwFile.getMode());
|
||||
EXPECT_EQ(
|
||||
"(file.txt, 00000000000000000000000000000000faceb00c, frw-)",
|
||||
"(file.txt, 00000000000000000000000000000000faceb00c, f)",
|
||||
rwFile.toLogString());
|
||||
|
||||
TreeEntry rFile(makeTestHash("7"), "file.txt", FileType::REGULAR_FILE, 0b100);
|
||||
EXPECT_EQ(S_IFREG | S_IRUSR | S_IRGRP | S_IROTH, rFile.getMode());
|
||||
EXPECT_EQ(
|
||||
"(file.txt, 0000000000000000000000000000000000000007, fr--)",
|
||||
rFile.toLogString());
|
||||
|
||||
TreeEntry rwxFile(
|
||||
makeTestHash("789"), "file.exe", FileType::REGULAR_FILE, 0b111);
|
||||
TreeEntry rwxFile(makeTestHash("789"), "file.exe", FileType::EXECUTABLE_FILE);
|
||||
EXPECT_EQ(
|
||||
S_IFREG | S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH,
|
||||
rwxFile.getMode());
|
||||
EXPECT_EQ(
|
||||
"(file.exe, 0000000000000000000000000000000000000789, frwx)",
|
||||
"(file.exe, 0000000000000000000000000000000000000789, x)",
|
||||
rwxFile.toLogString());
|
||||
|
||||
TreeEntry nopermsFile(
|
||||
makeTestHash("13"), "secret.txt", FileType::REGULAR_FILE, 0b000);
|
||||
EXPECT_EQ(S_IFREG, nopermsFile.getMode());
|
||||
EXPECT_EQ(
|
||||
"(secret.txt, 0000000000000000000000000000000000000013, f---)",
|
||||
nopermsFile.toLogString());
|
||||
|
||||
TreeEntry rwLink(makeTestHash("a"), "to-file.txt", FileType::SYMLINK, 0b110);
|
||||
EXPECT_EQ(S_IFLNK | S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH, rwLink.getMode());
|
||||
EXPECT_EQ(
|
||||
"(to-file.txt, 000000000000000000000000000000000000000a, lrw-)",
|
||||
rwLink.toLogString());
|
||||
|
||||
TreeEntry rwxLink(makeTestHash("b"), "to-file.exe", FileType::SYMLINK, 0b111);
|
||||
TreeEntry rwxLink(makeTestHash("b"), "to-file.exe", FileType::SYMLINK);
|
||||
EXPECT_EQ(
|
||||
S_IFLNK | S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH,
|
||||
rwxLink.getMode());
|
||||
EXPECT_EQ(
|
||||
"(to-file.exe, 000000000000000000000000000000000000000b, lrwx)",
|
||||
"(to-file.exe, 000000000000000000000000000000000000000b, l)",
|
||||
rwxLink.toLogString());
|
||||
|
||||
TreeEntry directory(makeTestHash("abc"), "src", FileType::DIRECTORY, 0b111);
|
||||
TreeEntry directory(makeTestHash("abc"), "src", FileType::DIRECTORY);
|
||||
EXPECT_EQ(
|
||||
S_IFDIR | S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH,
|
||||
directory.getMode());
|
||||
EXPECT_EQ(
|
||||
"(src, 0000000000000000000000000000000000000abc, drwx)",
|
||||
"(src, 0000000000000000000000000000000000000abc, d)",
|
||||
directory.toLogString());
|
||||
}
|
||||
|
@ -36,9 +36,8 @@ Hash testHash(testHashHex);
|
||||
} // namespace
|
||||
|
||||
TEST(Tree, testGetEntryPtr) {
|
||||
uint8_t mode = 0b110;
|
||||
vector<TreeEntry> entries;
|
||||
entries.emplace_back(testHash, "a_file", FileType::REGULAR_FILE, mode);
|
||||
entries.emplace_back(testHash, "a_file", FileType::REGULAR_FILE);
|
||||
Tree tree(std::move(entries));
|
||||
|
||||
// Verify existent path.
|
||||
@ -48,7 +47,6 @@ TEST(Tree, testGetEntryPtr) {
|
||||
EXPECT_EQ("a_file", entry->getName());
|
||||
EXPECT_EQ(TreeEntryType::BLOB, entry->getType());
|
||||
EXPECT_EQ(FileType::REGULAR_FILE, entry->getFileType());
|
||||
EXPECT_EQ(mode, entry->getOwnerPermissions());
|
||||
|
||||
// Verify non-existent path.
|
||||
PathComponentPiece nonExistentPath("not_a_file");
|
||||
|
@ -98,19 +98,14 @@ unique_ptr<Tree> GitBackingStore::getTreeImpl(const Hash& id) {
|
||||
auto entryMode = git_tree_entry_filemode(gitEntry);
|
||||
StringPiece entryName(git_tree_entry_name(gitEntry));
|
||||
FileType fileType;
|
||||
uint8_t ownerPerms;
|
||||
if (entryMode == GIT_FILEMODE_TREE) {
|
||||
fileType = FileType::DIRECTORY;
|
||||
ownerPerms = 0b111;
|
||||
} else if (entryMode == GIT_FILEMODE_BLOB_EXECUTABLE) {
|
||||
fileType = FileType::REGULAR_FILE;
|
||||
ownerPerms = 0b111;
|
||||
fileType = FileType::EXECUTABLE_FILE;
|
||||
} else if (entryMode == GIT_FILEMODE_LINK) {
|
||||
fileType = FileType::SYMLINK;
|
||||
ownerPerms = 0b111;
|
||||
} else if (entryMode == GIT_FILEMODE_BLOB) {
|
||||
fileType = FileType::REGULAR_FILE;
|
||||
ownerPerms = 0b110;
|
||||
} else {
|
||||
// TODO: We currently don't handle GIT_FILEMODE_COMMIT
|
||||
throw std::runtime_error(folly::to<string>(
|
||||
@ -122,7 +117,7 @@ unique_ptr<Tree> GitBackingStore::getTreeImpl(const Hash& id) {
|
||||
id));
|
||||
}
|
||||
auto entryHash = oid2Hash(git_tree_entry_id(gitEntry));
|
||||
entries.emplace_back(entryHash, entryName, fileType, ownerPerms);
|
||||
entries.emplace_back(entryHash, entryName, fileType);
|
||||
}
|
||||
auto tree = make_unique<Tree>(std::move(entries), id);
|
||||
auto hash = localStore_->putTree(tree.get());
|
||||
|
@ -620,7 +620,6 @@ std::unique_ptr<Tree> HgImporter::importTreeImpl(
|
||||
StringPiece entryName(entry->filename, entry->filenamelen);
|
||||
|
||||
FileType fileType;
|
||||
uint8_t ownerPermissions;
|
||||
|
||||
StringPiece entryFlag;
|
||||
if (entry->flag) {
|
||||
@ -635,16 +634,13 @@ std::unique_ptr<Tree> HgImporter::importTreeImpl(
|
||||
|
||||
if (entry->isdirectory()) {
|
||||
fileType = FileType::DIRECTORY;
|
||||
ownerPermissions = 0b111;
|
||||
} else if (entry->flag) {
|
||||
switch (*entry->flag) {
|
||||
case 'x':
|
||||
fileType = FileType::REGULAR_FILE;
|
||||
ownerPermissions = 0b111;
|
||||
fileType = FileType::EXECUTABLE_FILE;
|
||||
break;
|
||||
case 'l':
|
||||
fileType = FileType::SYMLINK;
|
||||
ownerPermissions = 0b111;
|
||||
break;
|
||||
default:
|
||||
throw std::runtime_error(folly::to<string>(
|
||||
@ -657,13 +653,12 @@ std::unique_ptr<Tree> HgImporter::importTreeImpl(
|
||||
}
|
||||
} else {
|
||||
fileType = FileType::REGULAR_FILE;
|
||||
ownerPermissions = 0b110;
|
||||
}
|
||||
|
||||
auto proxyHash = HgProxyHash::store(
|
||||
path + RelativePathPiece(entryName), entryHash, writeBatch);
|
||||
|
||||
entries.emplace_back(proxyHash, entryName, fileType, ownerPermissions);
|
||||
entries.emplace_back(proxyHash, entryName, fileType);
|
||||
|
||||
iter.next();
|
||||
}
|
||||
@ -827,16 +822,12 @@ void HgImporter::readManifestEntry(
|
||||
auto pathStr = cursor.readTerminatedString();
|
||||
|
||||
FileType fileType;
|
||||
uint8_t ownerPermissions;
|
||||
if (flag == ' ') {
|
||||
fileType = FileType::REGULAR_FILE;
|
||||
ownerPermissions = 0b110;
|
||||
} else if (flag == 'x') {
|
||||
fileType = FileType::REGULAR_FILE;
|
||||
ownerPermissions = 0b111;
|
||||
fileType = FileType::EXECUTABLE_FILE;
|
||||
} else if (flag == 'l') {
|
||||
fileType = FileType::SYMLINK;
|
||||
ownerPermissions = 0b111;
|
||||
} else {
|
||||
throw std::runtime_error(folly::to<string>(
|
||||
"unsupported file flags for ", pathStr, ": ", static_cast<int>(flag)));
|
||||
@ -847,8 +838,7 @@ void HgImporter::readManifestEntry(
|
||||
// Generate a blob hash from the mercurial (path, fileRev) information
|
||||
auto blobHash = HgProxyHash::store(path, fileRevHash, writeBatch);
|
||||
|
||||
auto entry =
|
||||
TreeEntry(blobHash, path.basename().value(), fileType, ownerPermissions);
|
||||
auto entry = TreeEntry(blobHash, path.basename().value(), fileType);
|
||||
importer.processEntry(path.dirname(), std::move(entry));
|
||||
}
|
||||
|
||||
|
@ -231,9 +231,7 @@ void HgManifestImporter::popCurrentDir() {
|
||||
|
||||
auto dirHash = back.compute(store_);
|
||||
|
||||
uint8_t ownerPermissions = 0111;
|
||||
TreeEntry dirEntry(
|
||||
dirHash, entryName.stringPiece(), FileType::DIRECTORY, ownerPermissions);
|
||||
TreeEntry dirEntry{dirHash, entryName.stringPiece(), FileType::DIRECTORY};
|
||||
dirStack_.back().addEntry(std::move(dirEntry));
|
||||
dirStack_.back().addPartialTree(std::move(back));
|
||||
}
|
||||
|
@ -94,7 +94,6 @@ void HgImportTest::importTest(bool treemanifest) {
|
||||
// importer.importTree().
|
||||
auto fooEntry = rootTree->getEntryAt(PathComponentPiece{"foo"});
|
||||
ASSERT_EQ(FileType::DIRECTORY, fooEntry.getFileType());
|
||||
EXPECT_EQ(0b111, fooEntry.getOwnerPermissions());
|
||||
auto fooTree = treemanifest ? importer.importTree(fooEntry.getHash())
|
||||
: localStore_.getTree(fooEntry.getHash());
|
||||
ASSERT_TRUE(fooTree);
|
||||
@ -111,10 +110,8 @@ void HgImportTest::importTest(bool treemanifest) {
|
||||
|
||||
auto barEntry = fooTree->getEntryAt(PathComponentPiece{"bar.txt"});
|
||||
ASSERT_EQ(FileType::REGULAR_FILE, barEntry.getFileType());
|
||||
EXPECT_EQ(0b110, barEntry.getOwnerPermissions());
|
||||
auto testEntry = fooTree->getEntryAt(PathComponentPiece{"test.txt"});
|
||||
ASSERT_EQ(FileType::REGULAR_FILE, testEntry.getFileType());
|
||||
EXPECT_EQ(0b110, testEntry.getOwnerPermissions());
|
||||
|
||||
// The blobs should not have been imported yet, though
|
||||
EXPECT_FALSE(localStore_.getBlob(barEntry.getHash()));
|
||||
@ -123,7 +120,6 @@ void HgImportTest::importTest(bool treemanifest) {
|
||||
// Get the "src" tree from the LocalStore.
|
||||
auto srcEntry = rootTree->getEntryAt(PathComponentPiece{"src"});
|
||||
ASSERT_EQ(FileType::DIRECTORY, srcEntry.getFileType());
|
||||
EXPECT_EQ(0b111, srcEntry.getOwnerPermissions());
|
||||
auto srcTree = treemanifest ? importer.importTree(srcEntry.getHash())
|
||||
: localStore_.getTree(srcEntry.getHash());
|
||||
ASSERT_TRUE(srcTree);
|
||||
@ -138,12 +134,10 @@ void HgImportTest::importTest(bool treemanifest) {
|
||||
|
||||
auto somelinkEntry = srcTree->getEntryAt(PathComponentPiece{"somelink"});
|
||||
ASSERT_EQ(FileType::SYMLINK, somelinkEntry.getFileType());
|
||||
EXPECT_EQ(0b111, somelinkEntry.getOwnerPermissions());
|
||||
|
||||
// Get the "src/eden" tree from the LocalStore
|
||||
auto edenEntry = srcTree->getEntryAt(PathComponentPiece{"eden"});
|
||||
ASSERT_EQ(FileType::DIRECTORY, edenEntry.getFileType());
|
||||
EXPECT_EQ(0b111, edenEntry.getOwnerPermissions());
|
||||
auto edenTree = treemanifest ? importer.importTree(edenEntry.getHash())
|
||||
: localStore_.getTree(edenEntry.getHash());
|
||||
ASSERT_TRUE(edenTree);
|
||||
@ -156,8 +150,7 @@ void HgImportTest::importTest(bool treemanifest) {
|
||||
}
|
||||
|
||||
auto mainEntry = edenTree->getEntryAt(PathComponentPiece{"main.py"});
|
||||
ASSERT_EQ(FileType::REGULAR_FILE, mainEntry.getFileType());
|
||||
EXPECT_EQ(0b111, mainEntry.getOwnerPermissions());
|
||||
ASSERT_EQ(FileType::EXECUTABLE_FILE, mainEntry.getFileType());
|
||||
|
||||
// Import and check the blobs
|
||||
auto barBuf = importer.importFileContents(barEntry.getHash());
|
||||
|
@ -171,22 +171,18 @@ std::unique_ptr<Tree> convertBufToTree(
|
||||
auto hash = Hash(i->at("hash").asString());
|
||||
auto str_type = i->at("type").asString();
|
||||
FileType file_type;
|
||||
uint8_t owner_permissions = 0b110;
|
||||
if (str_type == "File") {
|
||||
file_type = FileType::REGULAR_FILE;
|
||||
} else if (str_type == "Tree") {
|
||||
file_type = FileType::DIRECTORY;
|
||||
owner_permissions = 0b111;
|
||||
} else if (str_type == "Executable") {
|
||||
file_type = FileType::REGULAR_FILE;
|
||||
owner_permissions = 0b111;
|
||||
file_type = FileType::EXECUTABLE_FILE;
|
||||
} else if (str_type == "Symlink") {
|
||||
file_type = FileType::SYMLINK;
|
||||
owner_permissions = 0b111;
|
||||
} else {
|
||||
throw std::runtime_error("unknown file type");
|
||||
}
|
||||
entries.push_back(TreeEntry(hash, path_elem, file_type, owner_permissions));
|
||||
entries.push_back(TreeEntry(hash, path_elem, file_type));
|
||||
}
|
||||
return std::make_unique<Tree>(std::move(entries), id);
|
||||
}
|
||||
|
@ -237,28 +237,23 @@ TEST_F(MononokeBackingStoreTest, testGetTree) {
|
||||
TreeEntry(
|
||||
Hash("b80de5d138758541c5f05265ad144ab9fa86d1db"),
|
||||
"a",
|
||||
FileType::REGULAR_FILE,
|
||||
0b110),
|
||||
FileType::REGULAR_FILE),
|
||||
TreeEntry(
|
||||
Hash("b8e02f6433738021a065f94175c7cd23db5f05be"),
|
||||
"b",
|
||||
FileType::REGULAR_FILE,
|
||||
0b110),
|
||||
FileType::REGULAR_FILE),
|
||||
TreeEntry(
|
||||
Hash("3333333333333333333333333333333333333333"),
|
||||
"dir",
|
||||
FileType::DIRECTORY,
|
||||
0b111),
|
||||
FileType::DIRECTORY),
|
||||
TreeEntry(
|
||||
Hash("4444444444444444444444444444444444444444"),
|
||||
"exec",
|
||||
FileType::REGULAR_FILE,
|
||||
0b111),
|
||||
FileType::EXECUTABLE_FILE),
|
||||
TreeEntry(
|
||||
Hash("5555555555555555555555555555555555555555"),
|
||||
"link",
|
||||
FileType::SYMLINK,
|
||||
0b111),
|
||||
FileType::SYMLINK),
|
||||
};
|
||||
|
||||
Tree expected_tree(std::move(expected_entries), treehash);
|
||||
@ -300,28 +295,23 @@ TEST_F(MononokeBackingStoreTest, testGetTreeForCommit) {
|
||||
TreeEntry(
|
||||
Hash("b80de5d138758541c5f05265ad144ab9fa86d1db"),
|
||||
"a",
|
||||
FileType::REGULAR_FILE,
|
||||
0b110),
|
||||
FileType::REGULAR_FILE),
|
||||
TreeEntry(
|
||||
Hash("b8e02f6433738021a065f94175c7cd23db5f05be"),
|
||||
"b",
|
||||
FileType::REGULAR_FILE,
|
||||
0b110),
|
||||
FileType::REGULAR_FILE),
|
||||
TreeEntry(
|
||||
Hash("3333333333333333333333333333333333333333"),
|
||||
"dir",
|
||||
FileType::DIRECTORY,
|
||||
0b111),
|
||||
FileType::DIRECTORY),
|
||||
TreeEntry(
|
||||
Hash("4444444444444444444444444444444444444444"),
|
||||
"exec",
|
||||
FileType::REGULAR_FILE,
|
||||
0b111),
|
||||
FileType::EXECUTABLE_FILE),
|
||||
TreeEntry(
|
||||
Hash("5555555555555555555555555555555555555555"),
|
||||
"link",
|
||||
FileType::SYMLINK,
|
||||
0b111),
|
||||
FileType::SYMLINK),
|
||||
};
|
||||
|
||||
Tree expected_tree(std::move(expected_entries), treehash);
|
||||
|
@ -146,7 +146,6 @@ TEST_P(LocalStoreTest, testReadsAndWriteTree) {
|
||||
EXPECT_EQ("README.md", readmeEntry.getName());
|
||||
EXPECT_EQ(TreeEntryType::BLOB, readmeEntry.getType());
|
||||
EXPECT_EQ(FileType::REGULAR_FILE, readmeEntry.getFileType());
|
||||
EXPECT_EQ(0b0110, readmeEntry.getOwnerPermissions());
|
||||
}
|
||||
|
||||
TEST_P(LocalStoreTest, testGetResult) {
|
||||
|
@ -137,41 +137,39 @@ std::pair<StoredBlob*, bool> FakeBackingStore::maybePutBlob(
|
||||
}
|
||||
}
|
||||
|
||||
static FileType fileTypeFromBlobType(FakeBlobType type) {
|
||||
switch (type) {
|
||||
case FakeBlobType::REGULAR_FILE:
|
||||
return FileType::REGULAR_FILE;
|
||||
case FakeBlobType::EXECUTABLE_FILE:
|
||||
return FileType::EXECUTABLE_FILE;
|
||||
case FakeBlobType::SYMLINK:
|
||||
return FileType::SYMLINK;
|
||||
}
|
||||
XLOG(FATAL) << "Unknown fake blob type " << static_cast<int>(type);
|
||||
}
|
||||
|
||||
FakeBackingStore::TreeEntryData::TreeEntryData(
|
||||
folly::StringPiece name,
|
||||
const Blob& blob,
|
||||
mode_t mode)
|
||||
: entry{blob.getHash(),
|
||||
name,
|
||||
FileType::REGULAR_FILE,
|
||||
TreeEntry::modeToOwnerPermissions(mode)} {}
|
||||
FakeBlobType type)
|
||||
: entry{blob.getHash(), name, fileTypeFromBlobType(type)} {}
|
||||
|
||||
FakeBackingStore::TreeEntryData::TreeEntryData(
|
||||
folly::StringPiece name,
|
||||
const StoredBlob* blob,
|
||||
mode_t mode)
|
||||
: entry{blob->get().getHash(),
|
||||
name,
|
||||
FileType::REGULAR_FILE,
|
||||
TreeEntry::modeToOwnerPermissions(mode)} {}
|
||||
FakeBlobType type)
|
||||
: entry{blob->get().getHash(), name, fileTypeFromBlobType(type)} {}
|
||||
|
||||
FakeBackingStore::TreeEntryData::TreeEntryData(
|
||||
folly::StringPiece name,
|
||||
const Tree& tree,
|
||||
mode_t mode)
|
||||
: entry{tree.getHash(),
|
||||
name,
|
||||
FileType::DIRECTORY,
|
||||
TreeEntry::modeToOwnerPermissions(mode)} {}
|
||||
const Tree& tree)
|
||||
: entry{tree.getHash(), name, FileType::DIRECTORY} {}
|
||||
|
||||
FakeBackingStore::TreeEntryData::TreeEntryData(
|
||||
folly::StringPiece name,
|
||||
const StoredTree* tree,
|
||||
mode_t mode)
|
||||
: entry{tree->get().getHash(),
|
||||
name,
|
||||
FileType::DIRECTORY,
|
||||
TreeEntry::modeToOwnerPermissions(mode)} {}
|
||||
const StoredTree* tree)
|
||||
: entry{tree->get().getHash(), name, FileType::DIRECTORY} {}
|
||||
|
||||
StoredTree* FakeBackingStore::putTree(
|
||||
const std::initializer_list<TreeEntryData>& entryArgs) {
|
||||
|
@ -144,6 +144,12 @@ class FakeBackingStore : public BackingStore {
|
||||
folly::Synchronized<Data> data_;
|
||||
};
|
||||
|
||||
enum class FakeBlobType {
|
||||
REGULAR_FILE,
|
||||
EXECUTABLE_FILE,
|
||||
SYMLINK,
|
||||
};
|
||||
|
||||
/**
|
||||
* A small helper struct for use with FakeBackingStore::putTree()
|
||||
*
|
||||
@ -151,16 +157,18 @@ class FakeBackingStore : public BackingStore {
|
||||
* initialier-list arguments.
|
||||
*/
|
||||
struct FakeBackingStore::TreeEntryData {
|
||||
TreeEntryData(folly::StringPiece name, const Blob& blob, mode_t mode = 0644);
|
||||
TreeEntryData(
|
||||
folly::StringPiece name,
|
||||
const Blob& blob,
|
||||
FakeBlobType type = FakeBlobType::REGULAR_FILE);
|
||||
TreeEntryData(
|
||||
folly::StringPiece name,
|
||||
const StoredBlob* blob,
|
||||
mode_t mode = 0644);
|
||||
TreeEntryData(folly::StringPiece name, const Tree& tree, mode_t mode = 0755);
|
||||
TreeEntryData(
|
||||
folly::StringPiece name,
|
||||
const StoredTree* tree,
|
||||
mode_t mode = 0755);
|
||||
FakeBlobType type = FakeBlobType::REGULAR_FILE);
|
||||
// tree
|
||||
TreeEntryData(folly::StringPiece name, const Tree& tree);
|
||||
// tree
|
||||
TreeEntryData(folly::StringPiece name, const StoredTree* tree);
|
||||
|
||||
TreeEntry entry;
|
||||
};
|
||||
|
@ -45,8 +45,7 @@ void FakeTreeBuilder::setFiles(
|
||||
arg.path,
|
||||
folly::ByteRange{folly::StringPiece{arg.contents}},
|
||||
false,
|
||||
FileType::REGULAR_FILE,
|
||||
arg.permissions);
|
||||
arg.executable ? FileType::EXECUTABLE_FILE : FileType::REGULAR_FILE);
|
||||
}
|
||||
}
|
||||
|
||||
@ -60,14 +59,13 @@ void FakeTreeBuilder::setFileImpl(
|
||||
RelativePathPiece path,
|
||||
folly::ByteRange contents,
|
||||
bool replace,
|
||||
FileType type,
|
||||
int permissions) {
|
||||
FileType type) {
|
||||
CHECK(!finalizedRoot_);
|
||||
|
||||
auto dir = getDirEntry(path.dirname(), true);
|
||||
auto name = path.basename();
|
||||
|
||||
auto info = EntryInfo{type, TreeEntry::modeToOwnerPermissions(permissions)};
|
||||
auto info = EntryInfo{type};
|
||||
info.contents = folly::StringPiece{contents}.str();
|
||||
|
||||
if (replace) {
|
||||
@ -199,8 +197,7 @@ FakeTreeBuilder::EntryInfo* FakeTreeBuilder::getDirEntry(
|
||||
throw std::runtime_error(folly::to<string>(
|
||||
"tried to look up non-existent directory ", path));
|
||||
}
|
||||
auto ret =
|
||||
parent->entries->emplace(name, EntryInfo{FileType::DIRECTORY, 0b111});
|
||||
auto ret = parent->entries->emplace(name, EntryInfo{FileType::DIRECTORY});
|
||||
CHECK(ret.second);
|
||||
parent = &ret.first->second;
|
||||
} else {
|
||||
@ -240,17 +237,14 @@ StoredTree* FakeTreeBuilder::getStoredTree(RelativePathPiece path) {
|
||||
return current;
|
||||
}
|
||||
|
||||
FakeTreeBuilder::EntryInfo::EntryInfo(FileType fileType, uint8_t perms)
|
||||
: type(fileType), ownerPermissions(perms) {
|
||||
FakeTreeBuilder::EntryInfo::EntryInfo(FileType fileType) : type(fileType) {
|
||||
if (type == FileType::DIRECTORY) {
|
||||
entries = make_unique<PathMap<EntryInfo>>();
|
||||
}
|
||||
}
|
||||
|
||||
FakeTreeBuilder::EntryInfo::EntryInfo(ExplicitClone, const EntryInfo& orig)
|
||||
: type(orig.type),
|
||||
ownerPermissions(orig.ownerPermissions),
|
||||
contents(orig.contents) {
|
||||
: type(orig.type), contents(orig.contents) {
|
||||
if (orig.entries) {
|
||||
entries = make_unique<PathMap<EntryInfo>>();
|
||||
for (const auto& e : *orig.entries) {
|
||||
@ -276,11 +270,7 @@ StoredTree* FakeTreeBuilder::EntryInfo::finalizeTree(
|
||||
auto* storedBlob = entryInfo.finalizeBlob(builder, setReady);
|
||||
hash = storedBlob->get().getHash();
|
||||
}
|
||||
treeEntries.emplace_back(
|
||||
hash,
|
||||
e.first.stringPiece(),
|
||||
entryInfo.type,
|
||||
entryInfo.ownerPermissions);
|
||||
treeEntries.emplace_back(hash, e.first.stringPiece(), entryInfo.type);
|
||||
}
|
||||
|
||||
auto* storedTree = builder->store_->maybePutTree(treeEntries).first;
|
||||
|
@ -69,26 +69,30 @@ class FakeTreeBuilder {
|
||||
void setFile(
|
||||
folly::StringPiece path,
|
||||
folly::StringPiece contents,
|
||||
int permissions = 0644) {
|
||||
setFile(RelativePathPiece{path}, folly::ByteRange{contents}, permissions);
|
||||
bool executable = false) {
|
||||
setFile(RelativePathPiece{path}, folly::ByteRange{contents}, executable);
|
||||
}
|
||||
void setFile(
|
||||
folly::StringPiece path,
|
||||
folly::ByteRange contents,
|
||||
int permissions = 0644) {
|
||||
setFile(RelativePathPiece{path}, contents, permissions);
|
||||
bool executable = false) {
|
||||
setFile(RelativePathPiece{path}, contents, executable);
|
||||
}
|
||||
void setFile(
|
||||
RelativePathPiece path,
|
||||
folly::StringPiece contents,
|
||||
int permissions = 0644) {
|
||||
setFile(path, folly::ByteRange{contents}, permissions);
|
||||
bool executable = false) {
|
||||
setFile(path, folly::ByteRange{contents}, executable);
|
||||
}
|
||||
void setFile(
|
||||
RelativePathPiece path,
|
||||
folly::ByteRange contents,
|
||||
int permissions = 0644) {
|
||||
setFileImpl(path, contents, false, FileType::REGULAR_FILE, permissions);
|
||||
bool executable = false) {
|
||||
setFileImpl(
|
||||
path,
|
||||
contents,
|
||||
false,
|
||||
executable ? FileType::EXECUTABLE_FILE : FileType::REGULAR_FILE);
|
||||
}
|
||||
|
||||
void setFiles(const std::initializer_list<FileInfo>& fileArgs);
|
||||
@ -99,27 +103,31 @@ class FakeTreeBuilder {
|
||||
void replaceFile(
|
||||
folly::StringPiece path,
|
||||
folly::StringPiece contents,
|
||||
int permissions = 0644) {
|
||||
bool executable = false) {
|
||||
replaceFile(
|
||||
RelativePathPiece{path}, folly::ByteRange{contents}, permissions);
|
||||
RelativePathPiece{path}, folly::ByteRange{contents}, executable);
|
||||
}
|
||||
void replaceFile(
|
||||
folly::StringPiece path,
|
||||
folly::ByteRange contents,
|
||||
int permissions = 0644) {
|
||||
replaceFile(RelativePathPiece{path}, contents, permissions);
|
||||
bool executable = false) {
|
||||
replaceFile(RelativePathPiece{path}, contents, executable);
|
||||
}
|
||||
void replaceFile(
|
||||
RelativePathPiece path,
|
||||
folly::StringPiece contents,
|
||||
int permissions = 0644) {
|
||||
replaceFile(path, folly::ByteRange{contents}, permissions);
|
||||
bool executable = false) {
|
||||
replaceFile(path, folly::ByteRange{contents}, executable);
|
||||
}
|
||||
void replaceFile(
|
||||
RelativePathPiece path,
|
||||
folly::ByteRange contents,
|
||||
int permissions = 0644) {
|
||||
setFileImpl(path, contents, true, FileType::REGULAR_FILE, permissions);
|
||||
bool executable = false) {
|
||||
setFileImpl(
|
||||
path,
|
||||
contents,
|
||||
true,
|
||||
executable ? FileType::EXECUTABLE_FILE : FileType::REGULAR_FILE);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -129,7 +137,7 @@ class FakeTreeBuilder {
|
||||
setSymlink(RelativePathPiece{path}, contents);
|
||||
}
|
||||
void setSymlink(RelativePathPiece path, folly::StringPiece contents) {
|
||||
setFileImpl(path, contents, false, FileType::SYMLINK, 0644);
|
||||
setFileImpl(path, contents, false, FileType::SYMLINK);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -139,7 +147,7 @@ class FakeTreeBuilder {
|
||||
replaceSymlink(RelativePathPiece{path}, contents);
|
||||
}
|
||||
void replaceSymlink(RelativePathPiece path, folly::StringPiece contents) {
|
||||
setFileImpl(path, contents, true, FileType::SYMLINK, 0644);
|
||||
setFileImpl(path, contents, true, FileType::SYMLINK);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -225,7 +233,7 @@ class FakeTreeBuilder {
|
||||
FakeTreeBuilder(ExplicitClone, const FakeTreeBuilder* orig);
|
||||
|
||||
struct EntryInfo {
|
||||
EntryInfo(FileType fileType, uint8_t perms);
|
||||
explicit EntryInfo(FileType fileType);
|
||||
|
||||
EntryInfo(EntryInfo&& other) = default;
|
||||
EntryInfo& operator=(EntryInfo&& other) = default;
|
||||
@ -239,7 +247,6 @@ class FakeTreeBuilder {
|
||||
StoredBlob* finalizeBlob(FakeTreeBuilder* builder, bool setReady) const;
|
||||
|
||||
FileType type;
|
||||
uint8_t ownerPermissions;
|
||||
std::unique_ptr<PathMap<EntryInfo>> entries;
|
||||
std::string contents;
|
||||
};
|
||||
@ -251,24 +258,23 @@ class FakeTreeBuilder {
|
||||
RelativePathPiece path,
|
||||
folly::ByteRange contents,
|
||||
bool replace,
|
||||
FileType type,
|
||||
int permissions);
|
||||
FileType type);
|
||||
EntryInfo* getEntry(RelativePathPiece path);
|
||||
EntryInfo* getDirEntry(RelativePathPiece path, bool create);
|
||||
StoredTree* getStoredTree(RelativePathPiece path);
|
||||
|
||||
std::shared_ptr<FakeBackingStore> store_{nullptr};
|
||||
EntryInfo root_{FileType::DIRECTORY, 0b111};
|
||||
EntryInfo root_{FileType::DIRECTORY};
|
||||
StoredTree* finalizedRoot_{nullptr};
|
||||
};
|
||||
|
||||
struct FakeTreeBuilder::FileInfo {
|
||||
RelativePath path;
|
||||
std::string contents;
|
||||
int permissions = 0644;
|
||||
bool executable;
|
||||
|
||||
FileInfo(folly::StringPiece p, folly::StringPiece c, int perms = 0644)
|
||||
: path(p), contents(c.str()), permissions(perms) {}
|
||||
FileInfo(folly::StringPiece p, folly::StringPiece c, bool exec = false)
|
||||
: path(p), contents(c.str()), executable(exec) {}
|
||||
};
|
||||
} // namespace eden
|
||||
} // namespace facebook
|
||||
|
@ -18,13 +18,13 @@
|
||||
/**
|
||||
* Check that a FileInode has the expected contents and permissions.
|
||||
*/
|
||||
#define EXPECT_FILE_INODE(fileInode, expectedData, expectedPerms) \
|
||||
do { \
|
||||
EXPECT_EQ( \
|
||||
expectedData, \
|
||||
folly::StringPiece{ \
|
||||
fileInode->readAll().get(std::chrono::milliseconds(20000))}); \
|
||||
EXPECT_EQ( \
|
||||
folly::sformat("{:#o}", (expectedPerms)), \
|
||||
folly::sformat("{:#o}", (fileInode)->getPermissions())); \
|
||||
#define EXPECT_FILE_INODE(fileInode, expectedData, expectedPerms) \
|
||||
do { \
|
||||
EXPECT_EQ( \
|
||||
expectedData, \
|
||||
folly::StringPiece{ \
|
||||
(fileInode)->readAll().get(std::chrono::seconds(20))}); \
|
||||
EXPECT_EQ( \
|
||||
folly::sformat("{:#o}", (expectedPerms)), \
|
||||
folly::sformat("{:#o}", (fileInode)->getPermissions())); \
|
||||
} while (0)
|
||||
|
@ -140,7 +140,7 @@ TEST_F(FakeBackingStoreTest, getTree) {
|
||||
makeTestHash("abc"),
|
||||
{
|
||||
{"foo", foo},
|
||||
{"runme", runme, 0755},
|
||||
{"runme", runme, FakeBlobType::EXECUTABLE_FILE},
|
||||
});
|
||||
EXPECT_EQ(makeTestHash("abc"), dir1->get().getHash());
|
||||
auto* dir2 = store_->putTree({{"README", store_->putBlob("docs go here")}});
|
||||
@ -149,9 +149,9 @@ TEST_F(FakeBackingStoreTest, getTree) {
|
||||
auto* rootDir = store_->putTree(
|
||||
rootHash,
|
||||
{
|
||||
{"zzz", foo, 0444},
|
||||
{"zzz", foo, FakeBlobType::REGULAR_FILE},
|
||||
{"dir1", dir1},
|
||||
{"readonly", dir2, 0500},
|
||||
{"readonly", dir2},
|
||||
{"bar", bar},
|
||||
});
|
||||
|
||||
@ -183,12 +183,12 @@ TEST_F(FakeBackingStoreTest, getTree) {
|
||||
EXPECT_EQ(S_IFDIR | 0755, tree2->getEntryAt(1).getMode());
|
||||
EXPECT_EQ(PathComponentPiece{"readonly"}, tree2->getEntryAt(2).getName());
|
||||
EXPECT_EQ(dir2->get().getHash(), tree2->getEntryAt(2).getHash());
|
||||
// TreeEntry objects only track owner permissions, so even though we input
|
||||
// the permissions as 0500 above this really ends up returning 0555
|
||||
EXPECT_EQ(S_IFDIR | 0555, tree2->getEntryAt(2).getMode());
|
||||
// TreeEntry objects only tracking the owner executable bit, so even though we
|
||||
// input the permissions as 0500 above this really ends up returning 0755
|
||||
EXPECT_EQ(S_IFDIR | 0755, tree2->getEntryAt(2).getMode());
|
||||
EXPECT_EQ(PathComponentPiece{"zzz"}, tree2->getEntryAt(3).getName());
|
||||
EXPECT_EQ(foo->get().getHash(), tree2->getEntryAt(3).getHash());
|
||||
EXPECT_EQ(S_IFREG | 0444, tree2->getEntryAt(3).getMode());
|
||||
EXPECT_EQ(S_IFREG | 0644, tree2->getEntryAt(3).getMode());
|
||||
|
||||
EXPECT_EQ(rootHash, future3.get()->getHash());
|
||||
|
||||
|
@ -34,8 +34,7 @@ TEST(FakeObjectStore, getObjectsOfAllTypesFromStore) {
|
||||
|
||||
// Test getTree().
|
||||
vector<TreeEntry> entries1;
|
||||
uint8_t rw_ = 0b110;
|
||||
entries1.emplace_back(fileHash, "a_file", FileType::REGULAR_FILE, rw_);
|
||||
entries1.emplace_back(fileHash, "a_file", FileType::REGULAR_FILE);
|
||||
Tree tree1(std::move(entries1), tree1Hash);
|
||||
store.addTree(std::move(tree1));
|
||||
auto foundTree = store.getTree(tree1Hash).get();
|
||||
@ -52,7 +51,7 @@ TEST(FakeObjectStore, getObjectsOfAllTypesFromStore) {
|
||||
|
||||
// Test getTreeForCommit().
|
||||
vector<TreeEntry> entries2;
|
||||
entries2.emplace_back(fileHash, "a_file", FileType::REGULAR_FILE, rw_);
|
||||
entries2.emplace_back(fileHash, "a_file", FileType::REGULAR_FILE);
|
||||
Tree tree2(std::move(entries2), tree2Hash);
|
||||
store.setTreeForCommit(commHash, std::move(tree2));
|
||||
auto foundTreeForCommit = store.getTreeForCommit(commHash).get();
|
||||
|
Loading…
Reference in New Issue
Block a user