1 //===-- FileSystemTest.cpp ------------------------------------------------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 #include "gmock/gmock.h"
10 #include "gtest/gtest.h"
12 #include "lldb/Host/FileSystem.h"
13 #include "llvm/Support/Errc.h"
15 extern const char *TestMainArgv0
;
17 using namespace lldb_private
;
19 using llvm::sys::fs::UniqueID
;
21 // Modified from llvm/unittests/Support/VirtualFileSystemTest.cpp
23 struct DummyFile
: public vfs::File
{
25 explicit DummyFile(vfs::Status S
) : S(S
) {}
26 llvm::ErrorOr
<vfs::Status
> status() override
{ return S
; }
27 llvm::ErrorOr
<std::unique_ptr
<llvm::MemoryBuffer
>>
28 getBuffer(const Twine
&Name
, int64_t FileSize
, bool RequiresNullTerminator
,
29 bool IsVolatile
) override
{
30 llvm_unreachable("unimplemented");
32 std::error_code
close() override
{ return std::error_code(); }
35 class DummyFileSystem
: public vfs::FileSystem
{
36 int FSID
; // used to produce UniqueIDs
37 int FileID
= 0; // used to produce UniqueIDs
39 std::map
<std::string
, vfs::Status
> FilesAndDirs
;
41 static int getNextFSID() {
47 DummyFileSystem() : FSID(getNextFSID()) {}
49 ErrorOr
<vfs::Status
> status(const Twine
&Path
) override
{
50 std::map
<std::string
, vfs::Status
>::iterator I
=
51 FilesAndDirs
.find(Path
.str());
52 if (I
== FilesAndDirs
.end())
53 return make_error_code(llvm::errc::no_such_file_or_directory
);
54 // Simulate a broken symlink, where it points to a file/dir that
56 if (I
->second
.isSymlink() &&
57 I
->second
.getPermissions() == sys::fs::perms::no_perms
)
58 return std::error_code(ENOENT
, std::generic_category());
61 ErrorOr
<std::unique_ptr
<vfs::File
>>
62 openFileForRead(const Twine
&Path
) override
{
63 auto S
= status(Path
);
65 return std::unique_ptr
<vfs::File
>(new DummyFile
{*S
});
68 llvm::ErrorOr
<std::string
> getCurrentWorkingDirectory() const override
{
71 std::error_code
setCurrentWorkingDirectory(const Twine
&Path
) override
{
73 return std::error_code();
75 // Map any symlink to "/symlink".
76 std::error_code
getRealPath(const Twine
&Path
,
77 SmallVectorImpl
<char> &Output
) override
{
78 auto I
= FilesAndDirs
.find(Path
.str());
79 if (I
== FilesAndDirs
.end())
80 return make_error_code(llvm::errc::no_such_file_or_directory
);
81 if (I
->second
.isSymlink()) {
83 Twine("/symlink").toVector(Output
);
84 return std::error_code();
87 Path
.toVector(Output
);
88 return std::error_code();
91 struct DirIterImpl
: public llvm::vfs::detail::DirIterImpl
{
92 std::map
<std::string
, vfs::Status
> &FilesAndDirs
;
93 std::map
<std::string
, vfs::Status
>::iterator I
;
95 bool isInPath(StringRef S
) {
96 if (Path
.size() < S
.size() && S
.starts_with(Path
)) {
97 auto LastSep
= S
.find_last_of('/');
98 if (LastSep
== Path
.size() || LastSep
== Path
.size() - 1)
103 DirIterImpl(std::map
<std::string
, vfs::Status
> &FilesAndDirs
,
105 : FilesAndDirs(FilesAndDirs
), I(FilesAndDirs
.begin()),
107 for (; I
!= FilesAndDirs
.end(); ++I
) {
108 if (isInPath(I
->first
)) {
109 CurrentEntry
= vfs::directory_entry(std::string(I
->second
.getName()),
110 I
->second
.getType());
115 std::error_code
increment() override
{
117 for (; I
!= FilesAndDirs
.end(); ++I
) {
118 if (isInPath(I
->first
)) {
119 CurrentEntry
= vfs::directory_entry(std::string(I
->second
.getName()),
120 I
->second
.getType());
124 if (I
== FilesAndDirs
.end())
125 CurrentEntry
= vfs::directory_entry();
126 return std::error_code();
130 vfs::directory_iterator
dir_begin(const Twine
&Dir
,
131 std::error_code
&EC
) override
{
132 return vfs::directory_iterator(
133 std::make_shared
<DirIterImpl
>(FilesAndDirs
, Dir
));
136 void addEntry(StringRef Path
, const vfs::Status
&Status
) {
137 FilesAndDirs
[std::string(Path
)] = Status
;
140 void addRegularFile(StringRef Path
, sys::fs::perms Perms
= sys::fs::all_all
) {
141 vfs::Status
S(Path
, UniqueID(FSID
, FileID
++),
142 std::chrono::system_clock::now(), 0, 0, 1024,
143 sys::fs::file_type::regular_file
, Perms
);
147 void addDirectory(StringRef Path
, sys::fs::perms Perms
= sys::fs::all_all
) {
148 vfs::Status
S(Path
, UniqueID(FSID
, FileID
++),
149 std::chrono::system_clock::now(), 0, 0, 0,
150 sys::fs::file_type::directory_file
, Perms
);
154 void addSymlink(StringRef Path
) {
155 vfs::Status
S(Path
, UniqueID(FSID
, FileID
++),
156 std::chrono::system_clock::now(), 0, 0, 0,
157 sys::fs::file_type::symlink_file
, sys::fs::all_all
);
161 void addBrokenSymlink(StringRef Path
) {
162 vfs::Status
S(Path
, UniqueID(FSID
, FileID
++),
163 std::chrono::system_clock::now(), 0, 0, 0,
164 sys::fs::file_type::symlink_file
, sys::fs::no_perms
);
170 TEST(FileSystemTest
, FileAndDirectoryComponents
) {
171 using namespace std::chrono
;
175 FileSpec
fs1("C:\\FILE\\THAT\\DOES\\NOT\\EXIST.TXT");
177 FileSpec
fs1("/file/that/does/not/exist.txt");
179 FileSpec
fs2(TestMainArgv0
);
183 EXPECT_EQ(system_clock::time_point(), fs
.GetModificationTime(fs1
));
184 EXPECT_LT(system_clock::time_point() + hours(24 * 365 * 20),
185 fs
.GetModificationTime(fs2
));
188 static IntrusiveRefCntPtr
<DummyFileSystem
> GetSimpleDummyFS() {
189 IntrusiveRefCntPtr
<DummyFileSystem
> D(new DummyFileSystem());
190 D
->addRegularFile("/foo");
191 D
->addDirectory("/bar");
192 D
->addSymlink("/baz");
193 D
->addBrokenSymlink("/lux");
194 D
->addRegularFile("/qux", ~sys::fs::perms::all_read
);
195 D
->setCurrentWorkingDirectory("/");
199 TEST(FileSystemTest
, Exists
) {
200 FileSystem
fs(GetSimpleDummyFS());
202 EXPECT_TRUE(fs
.Exists("/foo"));
203 EXPECT_TRUE(fs
.Exists(FileSpec("/foo", FileSpec::Style::posix
)));
206 TEST(FileSystemTest
, Readable
) {
207 FileSystem
fs(GetSimpleDummyFS());
209 EXPECT_TRUE(fs
.Readable("/foo"));
210 EXPECT_TRUE(fs
.Readable(FileSpec("/foo", FileSpec::Style::posix
)));
212 EXPECT_FALSE(fs
.Readable("/qux"));
213 EXPECT_FALSE(fs
.Readable(FileSpec("/qux", FileSpec::Style::posix
)));
216 TEST(FileSystemTest
, GetByteSize
) {
217 FileSystem
fs(GetSimpleDummyFS());
219 EXPECT_EQ((uint64_t)1024, fs
.GetByteSize("/foo"));
220 EXPECT_EQ((uint64_t)1024,
221 fs
.GetByteSize(FileSpec("/foo", FileSpec::Style::posix
)));
224 TEST(FileSystemTest
, GetPermissions
) {
225 FileSystem
fs(GetSimpleDummyFS());
227 EXPECT_EQ(sys::fs::all_all
, fs
.GetPermissions("/foo"));
228 EXPECT_EQ(sys::fs::all_all
,
229 fs
.GetPermissions(FileSpec("/foo", FileSpec::Style::posix
)));
232 TEST(FileSystemTest
, MakeAbsolute
) {
233 FileSystem
fs(GetSimpleDummyFS());
236 StringRef foo_relative
= "foo";
237 SmallString
<16> foo(foo_relative
);
238 auto EC
= fs
.MakeAbsolute(foo
);
240 EXPECT_TRUE(foo
.equals("/foo"));
244 FileSpec
file_spec("foo");
245 auto EC
= fs
.MakeAbsolute(file_spec
);
247 EXPECT_EQ(FileSpec("/foo"), file_spec
);
251 TEST(FileSystemTest
, Resolve
) {
252 FileSystem
fs(GetSimpleDummyFS());
255 StringRef foo_relative
= "foo";
256 SmallString
<16> foo(foo_relative
);
258 EXPECT_TRUE(foo
.equals("/foo"));
262 FileSpec
file_spec("foo");
263 fs
.Resolve(file_spec
);
264 EXPECT_EQ(FileSpec("/foo"), file_spec
);
268 StringRef foo_relative
= "bogus";
269 SmallString
<16> foo(foo_relative
);
271 EXPECT_TRUE(foo
.equals("bogus"));
275 FileSpec
file_spec("bogus");
276 fs
.Resolve(file_spec
);
277 EXPECT_EQ(FileSpec("bogus"), file_spec
);
281 FileSystem::EnumerateDirectoryResult
282 VFSCallback(void *baton
, llvm::sys::fs::file_type file_type
,
283 llvm::StringRef path
) {
284 auto visited
= static_cast<std::vector
<std::string
> *>(baton
);
285 visited
->push_back(path
.str());
286 return FileSystem::eEnumerateDirectoryResultNext
;
289 TEST(FileSystemTest
, EnumerateDirectory
) {
290 FileSystem
fs(GetSimpleDummyFS());
292 std::vector
<std::string
> visited
;
294 constexpr bool find_directories
= true;
295 constexpr bool find_files
= true;
296 constexpr bool find_other
= true;
298 fs
.EnumerateDirectory("/", find_directories
, find_files
, find_other
,
299 VFSCallback
, &visited
);
302 testing::UnorderedElementsAre("/foo", "/bar", "/baz", "/qux"));
305 TEST(FileSystemTest
, OpenErrno
) {
307 FileSpec
spec("C:\\FILE\\THAT\\DOES\\NOT\\EXIST.TXT");
309 FileSpec
spec("/file/that/does/not/exist.txt");
312 auto file
= fs
.Open(spec
, File::eOpenOptionReadOnly
, 0, true);
314 std::error_code code
= errorToErrorCode(file
.takeError());
315 EXPECT_EQ(code
.category(), std::system_category());
316 EXPECT_EQ(code
.value(), ENOENT
);
319 TEST(FileSystemTest
, EmptyTest
) {
325 fs
.DirBegin(spec
, ec
);
326 EXPECT_EQ(ec
.category(), std::system_category());
327 EXPECT_EQ(ec
.value(), ENOENT
);
331 llvm::ErrorOr
<vfs::Status
> status
= fs
.GetStatus(spec
);
332 ASSERT_FALSE(status
);
333 EXPECT_EQ(status
.getError().category(), std::system_category());
334 EXPECT_EQ(status
.getError().value(), ENOENT
);
337 EXPECT_EQ(sys::TimePoint
<>(), fs
.GetModificationTime(spec
));
338 EXPECT_EQ(static_cast<uint64_t>(0), fs
.GetByteSize(spec
));
339 EXPECT_EQ(llvm::sys::fs::perms::perms_not_known
, fs
.GetPermissions(spec
));
340 EXPECT_FALSE(fs
.Exists(spec
));
341 EXPECT_FALSE(fs
.Readable(spec
));
342 EXPECT_FALSE(fs
.IsDirectory(spec
));
343 EXPECT_FALSE(fs
.IsLocal(spec
));