180814287SRaphael Isemann //===-- FileSystemTest.cpp ------------------------------------------------===//
21408bf72SPavel Labath //
32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
61408bf72SPavel Labath //
71408bf72SPavel Labath //===----------------------------------------------------------------------===//
81408bf72SPavel Labath
946376966SJonas Devlieghere #include "gmock/gmock.h"
101408bf72SPavel Labath #include "gtest/gtest.h"
111408bf72SPavel Labath
121408bf72SPavel Labath #include "lldb/Host/FileSystem.h"
1346376966SJonas Devlieghere #include "llvm/Support/Errc.h"
141408bf72SPavel Labath
151408bf72SPavel Labath extern const char *TestMainArgv0;
161408bf72SPavel Labath
171408bf72SPavel Labath using namespace lldb_private;
1846376966SJonas Devlieghere using namespace llvm;
1946376966SJonas Devlieghere using llvm::sys::fs::UniqueID;
2046376966SJonas Devlieghere
2146376966SJonas Devlieghere // Modified from llvm/unittests/Support/VirtualFileSystemTest.cpp
2246376966SJonas Devlieghere namespace {
2346376966SJonas Devlieghere struct DummyFile : public vfs::File {
2446376966SJonas Devlieghere vfs::Status S;
DummyFile__anonef9ab2f10111::DummyFile2546376966SJonas Devlieghere explicit DummyFile(vfs::Status S) : S(S) {}
status__anonef9ab2f10111::DummyFile2646376966SJonas Devlieghere llvm::ErrorOr<vfs::Status> status() override { return S; }
2746376966SJonas Devlieghere llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
getBuffer__anonef9ab2f10111::DummyFile2846376966SJonas Devlieghere getBuffer(const Twine &Name, int64_t FileSize, bool RequiresNullTerminator,
2946376966SJonas Devlieghere bool IsVolatile) override {
3046376966SJonas Devlieghere llvm_unreachable("unimplemented");
3146376966SJonas Devlieghere }
close__anonef9ab2f10111::DummyFile3246376966SJonas Devlieghere std::error_code close() override { return std::error_code(); }
3346376966SJonas Devlieghere };
3446376966SJonas Devlieghere
3546376966SJonas Devlieghere class DummyFileSystem : public vfs::FileSystem {
3646376966SJonas Devlieghere int FSID; // used to produce UniqueIDs
37*28c878aeSShafik Yaghmour int FileID = 0; // used to produce UniqueIDs
3846376966SJonas Devlieghere std::string cwd;
3946376966SJonas Devlieghere std::map<std::string, vfs::Status> FilesAndDirs;
4046376966SJonas Devlieghere
getNextFSID()4146376966SJonas Devlieghere static int getNextFSID() {
4246376966SJonas Devlieghere static int Count = 0;
4346376966SJonas Devlieghere return Count++;
4446376966SJonas Devlieghere }
4546376966SJonas Devlieghere
4646376966SJonas Devlieghere public:
DummyFileSystem()47*28c878aeSShafik Yaghmour DummyFileSystem() : FSID(getNextFSID()) {}
4846376966SJonas Devlieghere
status(const Twine & Path)4946376966SJonas Devlieghere ErrorOr<vfs::Status> status(const Twine &Path) override {
5046376966SJonas Devlieghere std::map<std::string, vfs::Status>::iterator I =
5146376966SJonas Devlieghere FilesAndDirs.find(Path.str());
5246376966SJonas Devlieghere if (I == FilesAndDirs.end())
5346376966SJonas Devlieghere return make_error_code(llvm::errc::no_such_file_or_directory);
5446376966SJonas Devlieghere return I->second;
5546376966SJonas Devlieghere }
5646376966SJonas Devlieghere ErrorOr<std::unique_ptr<vfs::File>>
openFileForRead(const Twine & Path)5746376966SJonas Devlieghere openFileForRead(const Twine &Path) override {
5846376966SJonas Devlieghere auto S = status(Path);
5946376966SJonas Devlieghere if (S)
6046376966SJonas Devlieghere return std::unique_ptr<vfs::File>(new DummyFile{*S});
6146376966SJonas Devlieghere return S.getError();
6246376966SJonas Devlieghere }
getCurrentWorkingDirectory() const6346376966SJonas Devlieghere llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const override {
6446376966SJonas Devlieghere return cwd;
6546376966SJonas Devlieghere }
setCurrentWorkingDirectory(const Twine & Path)6646376966SJonas Devlieghere std::error_code setCurrentWorkingDirectory(const Twine &Path) override {
6746376966SJonas Devlieghere cwd = Path.str();
6846376966SJonas Devlieghere return std::error_code();
6946376966SJonas Devlieghere }
7046376966SJonas Devlieghere // Map any symlink to "/symlink".
getRealPath(const Twine & Path,SmallVectorImpl<char> & Output) const71f3cae3abSKadir Cetinkaya std::error_code getRealPath(const Twine &Path,
72f3cae3abSKadir Cetinkaya SmallVectorImpl<char> &Output) const override {
7346376966SJonas Devlieghere auto I = FilesAndDirs.find(Path.str());
7446376966SJonas Devlieghere if (I == FilesAndDirs.end())
7546376966SJonas Devlieghere return make_error_code(llvm::errc::no_such_file_or_directory);
7646376966SJonas Devlieghere if (I->second.isSymlink()) {
7746376966SJonas Devlieghere Output.clear();
7846376966SJonas Devlieghere Twine("/symlink").toVector(Output);
7946376966SJonas Devlieghere return std::error_code();
8046376966SJonas Devlieghere }
8146376966SJonas Devlieghere Output.clear();
8246376966SJonas Devlieghere Path.toVector(Output);
8346376966SJonas Devlieghere return std::error_code();
8446376966SJonas Devlieghere }
8546376966SJonas Devlieghere
8646376966SJonas Devlieghere struct DirIterImpl : public llvm::vfs::detail::DirIterImpl {
8746376966SJonas Devlieghere std::map<std::string, vfs::Status> &FilesAndDirs;
8846376966SJonas Devlieghere std::map<std::string, vfs::Status>::iterator I;
8946376966SJonas Devlieghere std::string Path;
isInPath__anonef9ab2f10111::DummyFileSystem::DirIterImpl9046376966SJonas Devlieghere bool isInPath(StringRef S) {
9146376966SJonas Devlieghere if (Path.size() < S.size() && S.find(Path) == 0) {
9246376966SJonas Devlieghere auto LastSep = S.find_last_of('/');
9346376966SJonas Devlieghere if (LastSep == Path.size() || LastSep == Path.size() - 1)
9446376966SJonas Devlieghere return true;
9546376966SJonas Devlieghere }
9646376966SJonas Devlieghere return false;
9746376966SJonas Devlieghere }
DirIterImpl__anonef9ab2f10111::DummyFileSystem::DirIterImpl9846376966SJonas Devlieghere DirIterImpl(std::map<std::string, vfs::Status> &FilesAndDirs,
9946376966SJonas Devlieghere const Twine &_Path)
10046376966SJonas Devlieghere : FilesAndDirs(FilesAndDirs), I(FilesAndDirs.begin()),
10146376966SJonas Devlieghere Path(_Path.str()) {
10246376966SJonas Devlieghere for (; I != FilesAndDirs.end(); ++I) {
10346376966SJonas Devlieghere if (isInPath(I->first)) {
104adcd0268SBenjamin Kramer CurrentEntry = vfs::directory_entry(std::string(I->second.getName()),
105adcd0268SBenjamin Kramer I->second.getType());
10646376966SJonas Devlieghere break;
10746376966SJonas Devlieghere }
10846376966SJonas Devlieghere }
10946376966SJonas Devlieghere }
increment__anonef9ab2f10111::DummyFileSystem::DirIterImpl11046376966SJonas Devlieghere std::error_code increment() override {
11146376966SJonas Devlieghere ++I;
11246376966SJonas Devlieghere for (; I != FilesAndDirs.end(); ++I) {
11346376966SJonas Devlieghere if (isInPath(I->first)) {
114adcd0268SBenjamin Kramer CurrentEntry = vfs::directory_entry(std::string(I->second.getName()),
115adcd0268SBenjamin Kramer I->second.getType());
11646376966SJonas Devlieghere break;
11746376966SJonas Devlieghere }
11846376966SJonas Devlieghere }
11946376966SJonas Devlieghere if (I == FilesAndDirs.end())
12046376966SJonas Devlieghere CurrentEntry = vfs::directory_entry();
12146376966SJonas Devlieghere return std::error_code();
12246376966SJonas Devlieghere }
12346376966SJonas Devlieghere };
12446376966SJonas Devlieghere
dir_begin(const Twine & Dir,std::error_code & EC)12546376966SJonas Devlieghere vfs::directory_iterator dir_begin(const Twine &Dir,
12646376966SJonas Devlieghere std::error_code &EC) override {
12746376966SJonas Devlieghere return vfs::directory_iterator(
12846376966SJonas Devlieghere std::make_shared<DirIterImpl>(FilesAndDirs, Dir));
12946376966SJonas Devlieghere }
13046376966SJonas Devlieghere
addEntry(StringRef Path,const vfs::Status & Status)13146376966SJonas Devlieghere void addEntry(StringRef Path, const vfs::Status &Status) {
132adcd0268SBenjamin Kramer FilesAndDirs[std::string(Path)] = Status;
13346376966SJonas Devlieghere }
13446376966SJonas Devlieghere
addRegularFile(StringRef Path,sys::fs::perms Perms=sys::fs::all_all)13546376966SJonas Devlieghere void addRegularFile(StringRef Path, sys::fs::perms Perms = sys::fs::all_all) {
13646376966SJonas Devlieghere vfs::Status S(Path, UniqueID(FSID, FileID++),
13746376966SJonas Devlieghere std::chrono::system_clock::now(), 0, 0, 1024,
13846376966SJonas Devlieghere sys::fs::file_type::regular_file, Perms);
13946376966SJonas Devlieghere addEntry(Path, S);
14046376966SJonas Devlieghere }
14146376966SJonas Devlieghere
addDirectory(StringRef Path,sys::fs::perms Perms=sys::fs::all_all)14246376966SJonas Devlieghere void addDirectory(StringRef Path, sys::fs::perms Perms = sys::fs::all_all) {
14346376966SJonas Devlieghere vfs::Status S(Path, UniqueID(FSID, FileID++),
14446376966SJonas Devlieghere std::chrono::system_clock::now(), 0, 0, 0,
14546376966SJonas Devlieghere sys::fs::file_type::directory_file, Perms);
14646376966SJonas Devlieghere addEntry(Path, S);
14746376966SJonas Devlieghere }
14846376966SJonas Devlieghere
addSymlink(StringRef Path)14946376966SJonas Devlieghere void addSymlink(StringRef Path) {
15046376966SJonas Devlieghere vfs::Status S(Path, UniqueID(FSID, FileID++),
15146376966SJonas Devlieghere std::chrono::system_clock::now(), 0, 0, 0,
15246376966SJonas Devlieghere sys::fs::file_type::symlink_file, sys::fs::all_all);
15346376966SJonas Devlieghere addEntry(Path, S);
15446376966SJonas Devlieghere }
15546376966SJonas Devlieghere };
15646376966SJonas Devlieghere } // namespace
1571408bf72SPavel Labath
TEST(FileSystemTest,FileAndDirectoryComponents)1581408bf72SPavel Labath TEST(FileSystemTest, FileAndDirectoryComponents) {
1591408bf72SPavel Labath using namespace std::chrono;
16046376966SJonas Devlieghere FileSystem fs;
16146376966SJonas Devlieghere
1628f3be7a3SJonas Devlieghere #ifdef _WIN32
1638f3be7a3SJonas Devlieghere FileSpec fs1("C:\\FILE\\THAT\\DOES\\NOT\\EXIST.TXT");
1648f3be7a3SJonas Devlieghere #else
1658f3be7a3SJonas Devlieghere FileSpec fs1("/file/that/does/not/exist.txt");
1668f3be7a3SJonas Devlieghere #endif
1678f3be7a3SJonas Devlieghere FileSpec fs2(TestMainArgv0);
1688f3be7a3SJonas Devlieghere
1698f3be7a3SJonas Devlieghere fs.Resolve(fs2);
1708f3be7a3SJonas Devlieghere
17146376966SJonas Devlieghere EXPECT_EQ(system_clock::time_point(), fs.GetModificationTime(fs1));
1721408bf72SPavel Labath EXPECT_LT(system_clock::time_point() + hours(24 * 365 * 20),
17346376966SJonas Devlieghere fs.GetModificationTime(fs2));
17446376966SJonas Devlieghere }
17546376966SJonas Devlieghere
GetSimpleDummyFS()17646376966SJonas Devlieghere static IntrusiveRefCntPtr<DummyFileSystem> GetSimpleDummyFS() {
17746376966SJonas Devlieghere IntrusiveRefCntPtr<DummyFileSystem> D(new DummyFileSystem());
17846376966SJonas Devlieghere D->addRegularFile("/foo");
17946376966SJonas Devlieghere D->addDirectory("/bar");
18046376966SJonas Devlieghere D->addSymlink("/baz");
18146376966SJonas Devlieghere D->addRegularFile("/qux", ~sys::fs::perms::all_read);
18246376966SJonas Devlieghere D->setCurrentWorkingDirectory("/");
18346376966SJonas Devlieghere return D;
18446376966SJonas Devlieghere }
18546376966SJonas Devlieghere
TEST(FileSystemTest,Exists)18646376966SJonas Devlieghere TEST(FileSystemTest, Exists) {
18746376966SJonas Devlieghere FileSystem fs(GetSimpleDummyFS());
18846376966SJonas Devlieghere
18946376966SJonas Devlieghere EXPECT_TRUE(fs.Exists("/foo"));
1908f3be7a3SJonas Devlieghere EXPECT_TRUE(fs.Exists(FileSpec("/foo", FileSpec::Style::posix)));
19146376966SJonas Devlieghere }
19246376966SJonas Devlieghere
TEST(FileSystemTest,Readable)19346376966SJonas Devlieghere TEST(FileSystemTest, Readable) {
19446376966SJonas Devlieghere FileSystem fs(GetSimpleDummyFS());
19546376966SJonas Devlieghere
19646376966SJonas Devlieghere EXPECT_TRUE(fs.Readable("/foo"));
1978f3be7a3SJonas Devlieghere EXPECT_TRUE(fs.Readable(FileSpec("/foo", FileSpec::Style::posix)));
19846376966SJonas Devlieghere
19946376966SJonas Devlieghere EXPECT_FALSE(fs.Readable("/qux"));
2008f3be7a3SJonas Devlieghere EXPECT_FALSE(fs.Readable(FileSpec("/qux", FileSpec::Style::posix)));
20146376966SJonas Devlieghere }
20246376966SJonas Devlieghere
TEST(FileSystemTest,GetByteSize)20346376966SJonas Devlieghere TEST(FileSystemTest, GetByteSize) {
20446376966SJonas Devlieghere FileSystem fs(GetSimpleDummyFS());
20546376966SJonas Devlieghere
20646376966SJonas Devlieghere EXPECT_EQ((uint64_t)1024, fs.GetByteSize("/foo"));
20746376966SJonas Devlieghere EXPECT_EQ((uint64_t)1024,
2088f3be7a3SJonas Devlieghere fs.GetByteSize(FileSpec("/foo", FileSpec::Style::posix)));
20946376966SJonas Devlieghere }
21046376966SJonas Devlieghere
TEST(FileSystemTest,GetPermissions)21146376966SJonas Devlieghere TEST(FileSystemTest, GetPermissions) {
21246376966SJonas Devlieghere FileSystem fs(GetSimpleDummyFS());
21346376966SJonas Devlieghere
21446376966SJonas Devlieghere EXPECT_EQ(sys::fs::all_all, fs.GetPermissions("/foo"));
21546376966SJonas Devlieghere EXPECT_EQ(sys::fs::all_all,
2168f3be7a3SJonas Devlieghere fs.GetPermissions(FileSpec("/foo", FileSpec::Style::posix)));
21746376966SJonas Devlieghere }
21846376966SJonas Devlieghere
TEST(FileSystemTest,MakeAbsolute)21946376966SJonas Devlieghere TEST(FileSystemTest, MakeAbsolute) {
22046376966SJonas Devlieghere FileSystem fs(GetSimpleDummyFS());
22146376966SJonas Devlieghere
22246376966SJonas Devlieghere {
22346376966SJonas Devlieghere StringRef foo_relative = "foo";
22446376966SJonas Devlieghere SmallString<16> foo(foo_relative);
22546376966SJonas Devlieghere auto EC = fs.MakeAbsolute(foo);
22646376966SJonas Devlieghere EXPECT_FALSE(EC);
22746376966SJonas Devlieghere EXPECT_TRUE(foo.equals("/foo"));
22846376966SJonas Devlieghere }
22946376966SJonas Devlieghere
23046376966SJonas Devlieghere {
2318f3be7a3SJonas Devlieghere FileSpec file_spec("foo");
23246376966SJonas Devlieghere auto EC = fs.MakeAbsolute(file_spec);
23346376966SJonas Devlieghere EXPECT_FALSE(EC);
234ee1eb528SJonas Devlieghere EXPECT_EQ(FileSpec("/foo"), file_spec);
23546376966SJonas Devlieghere }
23646376966SJonas Devlieghere }
23746376966SJonas Devlieghere
TEST(FileSystemTest,Resolve)23846376966SJonas Devlieghere TEST(FileSystemTest, Resolve) {
23946376966SJonas Devlieghere FileSystem fs(GetSimpleDummyFS());
24046376966SJonas Devlieghere
24146376966SJonas Devlieghere {
24246376966SJonas Devlieghere StringRef foo_relative = "foo";
24346376966SJonas Devlieghere SmallString<16> foo(foo_relative);
24446376966SJonas Devlieghere fs.Resolve(foo);
24546376966SJonas Devlieghere EXPECT_TRUE(foo.equals("/foo"));
24646376966SJonas Devlieghere }
24746376966SJonas Devlieghere
24846376966SJonas Devlieghere {
2498f3be7a3SJonas Devlieghere FileSpec file_spec("foo");
25046376966SJonas Devlieghere fs.Resolve(file_spec);
251ee1eb528SJonas Devlieghere EXPECT_EQ(FileSpec("/foo"), file_spec);
25246376966SJonas Devlieghere }
25346376966SJonas Devlieghere
25446376966SJonas Devlieghere {
25546376966SJonas Devlieghere StringRef foo_relative = "bogus";
25646376966SJonas Devlieghere SmallString<16> foo(foo_relative);
25746376966SJonas Devlieghere fs.Resolve(foo);
25846376966SJonas Devlieghere EXPECT_TRUE(foo.equals("bogus"));
25946376966SJonas Devlieghere }
26046376966SJonas Devlieghere
26146376966SJonas Devlieghere {
2628f3be7a3SJonas Devlieghere FileSpec file_spec("bogus");
26346376966SJonas Devlieghere fs.Resolve(file_spec);
264ee1eb528SJonas Devlieghere EXPECT_EQ(FileSpec("bogus"), file_spec);
26546376966SJonas Devlieghere }
26646376966SJonas Devlieghere }
2679ca491daSJonas Devlieghere
2689ca491daSJonas Devlieghere FileSystem::EnumerateDirectoryResult
VFSCallback(void * baton,llvm::sys::fs::file_type file_type,llvm::StringRef path)2699ca491daSJonas Devlieghere VFSCallback(void *baton, llvm::sys::fs::file_type file_type,
2709ca491daSJonas Devlieghere llvm::StringRef path) {
2719ca491daSJonas Devlieghere auto visited = static_cast<std::vector<std::string> *>(baton);
2729ca491daSJonas Devlieghere visited->push_back(path.str());
2739ca491daSJonas Devlieghere return FileSystem::eEnumerateDirectoryResultNext;
2749ca491daSJonas Devlieghere }
2759ca491daSJonas Devlieghere
TEST(FileSystemTest,EnumerateDirectory)2769ca491daSJonas Devlieghere TEST(FileSystemTest, EnumerateDirectory) {
2779ca491daSJonas Devlieghere FileSystem fs(GetSimpleDummyFS());
2789ca491daSJonas Devlieghere
2799ca491daSJonas Devlieghere std::vector<std::string> visited;
2809ca491daSJonas Devlieghere
2819ca491daSJonas Devlieghere constexpr bool find_directories = true;
2829ca491daSJonas Devlieghere constexpr bool find_files = true;
2839ca491daSJonas Devlieghere constexpr bool find_other = true;
2849ca491daSJonas Devlieghere
2859ca491daSJonas Devlieghere fs.EnumerateDirectory("/", find_directories, find_files, find_other,
2869ca491daSJonas Devlieghere VFSCallback, &visited);
2879ca491daSJonas Devlieghere
2889ca491daSJonas Devlieghere EXPECT_THAT(visited,
2899ca491daSJonas Devlieghere testing::UnorderedElementsAre("/foo", "/bar", "/baz", "/qux"));
2909ca491daSJonas Devlieghere }
2912fce1137SLawrence D'Anna
TEST(FileSystemTest,OpenErrno)2922fce1137SLawrence D'Anna TEST(FileSystemTest, OpenErrno) {
2932fce1137SLawrence D'Anna #ifdef _WIN32
2942fce1137SLawrence D'Anna FileSpec spec("C:\\FILE\\THAT\\DOES\\NOT\\EXIST.TXT");
2952fce1137SLawrence D'Anna #else
2962fce1137SLawrence D'Anna FileSpec spec("/file/that/does/not/exist.txt");
2972fce1137SLawrence D'Anna #endif
2982fce1137SLawrence D'Anna FileSystem fs;
29914735cabSMichał Górny auto file = fs.Open(spec, File::eOpenOptionReadOnly, 0, true);
3002fce1137SLawrence D'Anna ASSERT_FALSE(file);
3012fce1137SLawrence D'Anna std::error_code code = errorToErrorCode(file.takeError());
3022fce1137SLawrence D'Anna EXPECT_EQ(code.category(), std::system_category());
3032fce1137SLawrence D'Anna EXPECT_EQ(code.value(), ENOENT);
3042fce1137SLawrence D'Anna }
3052fce1137SLawrence D'Anna
TEST(FileSystemTest,EmptyTest)306d144087cSJonas Devlieghere TEST(FileSystemTest, EmptyTest) {
307d144087cSJonas Devlieghere FileSpec spec;
308d144087cSJonas Devlieghere FileSystem fs;
309d144087cSJonas Devlieghere
310d144087cSJonas Devlieghere {
311d144087cSJonas Devlieghere std::error_code ec;
312d144087cSJonas Devlieghere fs.DirBegin(spec, ec);
313d144087cSJonas Devlieghere EXPECT_EQ(ec.category(), std::system_category());
314d144087cSJonas Devlieghere EXPECT_EQ(ec.value(), ENOENT);
315d144087cSJonas Devlieghere }
316d144087cSJonas Devlieghere
317d144087cSJonas Devlieghere {
318d144087cSJonas Devlieghere llvm::ErrorOr<vfs::Status> status = fs.GetStatus(spec);
319d144087cSJonas Devlieghere ASSERT_FALSE(status);
320d144087cSJonas Devlieghere EXPECT_EQ(status.getError().category(), std::system_category());
321d144087cSJonas Devlieghere EXPECT_EQ(status.getError().value(), ENOENT);
322d144087cSJonas Devlieghere }
323d144087cSJonas Devlieghere
324d144087cSJonas Devlieghere EXPECT_EQ(sys::TimePoint<>(), fs.GetModificationTime(spec));
325d144087cSJonas Devlieghere EXPECT_EQ(static_cast<uint64_t>(0), fs.GetByteSize(spec));
326d144087cSJonas Devlieghere EXPECT_EQ(llvm::sys::fs::perms::perms_not_known, fs.GetPermissions(spec));
327d144087cSJonas Devlieghere EXPECT_FALSE(fs.Exists(spec));
328d144087cSJonas Devlieghere EXPECT_FALSE(fs.Readable(spec));
329d144087cSJonas Devlieghere EXPECT_FALSE(fs.IsDirectory(spec));
330d144087cSJonas Devlieghere EXPECT_FALSE(fs.IsLocal(spec));
331d144087cSJonas Devlieghere }
332