1 //===- unittests/Support/VirtualFileSystem.cpp -------------- VFS tests ---===//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
10 #include "llvm/Support/VirtualFileSystem.h"
11 #include "llvm/ADT/Triple.h"
12 #include "llvm/Config/llvm-config.h"
13 #include "llvm/Support/Errc.h"
14 #include "llvm/Support/Host.h"
15 #include "llvm/Support/MemoryBuffer.h"
16 #include "llvm/Support/Path.h"
17 #include "llvm/Support/SourceMgr.h"
18 #include "gmock/gmock.h"
19 #include "gtest/gtest.h"
24 using llvm::sys::fs::UniqueID
;
25 using testing::ElementsAre
;
27 using testing::UnorderedElementsAre
;
30 struct DummyFile
: public vfs::File
{
32 explicit DummyFile(vfs::Status S
) : S(S
) {}
33 llvm::ErrorOr
<vfs::Status
> status() override
{ return S
; }
34 llvm::ErrorOr
<std::unique_ptr
<llvm::MemoryBuffer
>>
35 getBuffer(const Twine
&Name
, int64_t FileSize
, bool RequiresNullTerminator
,
36 bool IsVolatile
) override
{
37 llvm_unreachable("unimplemented");
39 std::error_code
close() override
{ return std::error_code(); }
42 class DummyFileSystem
: public vfs::FileSystem
{
43 int FSID
; // used to produce UniqueIDs
44 int FileID
; // used to produce UniqueIDs
45 std::map
<std::string
, vfs::Status
> FilesAndDirs
;
47 static int getNextFSID() {
53 DummyFileSystem() : FSID(getNextFSID()), FileID(0) {}
55 ErrorOr
<vfs::Status
> status(const Twine
&Path
) override
{
56 std::map
<std::string
, vfs::Status
>::iterator I
=
57 FilesAndDirs
.find(Path
.str());
58 if (I
== FilesAndDirs
.end())
59 return make_error_code(llvm::errc::no_such_file_or_directory
);
62 ErrorOr
<std::unique_ptr
<vfs::File
>>
63 openFileForRead(const Twine
&Path
) override
{
64 auto S
= status(Path
);
66 return std::unique_ptr
<vfs::File
>(new DummyFile
{*S
});
69 llvm::ErrorOr
<std::string
> getCurrentWorkingDirectory() const override
{
72 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
) const 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
.find(Path
) == 0) {
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
)) {
110 vfs::directory_entry(I
->second
.getName(), I
->second
.getType());
115 std::error_code
increment() override
{
117 for (; I
!= FilesAndDirs
.end(); ++I
) {
118 if (isInPath(I
->first
)) {
120 vfs::directory_entry(I
->second
.getName(), 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
[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
);
162 /// Replace back-slashes by front-slashes.
163 std::string
getPosixPath(std::string S
) {
164 SmallString
<128> Result
;
165 llvm::sys::path::native(S
, Result
, llvm::sys::path::Style::posix
);
168 } // end anonymous namespace
170 TEST(VirtualFileSystemTest
, StatusQueries
) {
171 IntrusiveRefCntPtr
<DummyFileSystem
> D(new DummyFileSystem());
172 ErrorOr
<vfs::Status
> Status((std::error_code()));
174 D
->addRegularFile("/foo");
175 Status
= D
->status("/foo");
176 ASSERT_FALSE(Status
.getError());
177 EXPECT_TRUE(Status
->isStatusKnown());
178 EXPECT_FALSE(Status
->isDirectory());
179 EXPECT_TRUE(Status
->isRegularFile());
180 EXPECT_FALSE(Status
->isSymlink());
181 EXPECT_FALSE(Status
->isOther());
182 EXPECT_TRUE(Status
->exists());
184 D
->addDirectory("/bar");
185 Status
= D
->status("/bar");
186 ASSERT_FALSE(Status
.getError());
187 EXPECT_TRUE(Status
->isStatusKnown());
188 EXPECT_TRUE(Status
->isDirectory());
189 EXPECT_FALSE(Status
->isRegularFile());
190 EXPECT_FALSE(Status
->isSymlink());
191 EXPECT_FALSE(Status
->isOther());
192 EXPECT_TRUE(Status
->exists());
194 D
->addSymlink("/baz");
195 Status
= D
->status("/baz");
196 ASSERT_FALSE(Status
.getError());
197 EXPECT_TRUE(Status
->isStatusKnown());
198 EXPECT_FALSE(Status
->isDirectory());
199 EXPECT_FALSE(Status
->isRegularFile());
200 EXPECT_TRUE(Status
->isSymlink());
201 EXPECT_FALSE(Status
->isOther());
202 EXPECT_TRUE(Status
->exists());
204 EXPECT_TRUE(Status
->equivalent(*Status
));
205 ErrorOr
<vfs::Status
> Status2
= D
->status("/foo");
206 ASSERT_FALSE(Status2
.getError());
207 EXPECT_FALSE(Status
->equivalent(*Status2
));
210 TEST(VirtualFileSystemTest
, BaseOnlyOverlay
) {
211 IntrusiveRefCntPtr
<DummyFileSystem
> D(new DummyFileSystem());
212 ErrorOr
<vfs::Status
> Status((std::error_code()));
213 EXPECT_FALSE(Status
= D
->status("/foo"));
215 IntrusiveRefCntPtr
<vfs::OverlayFileSystem
> O(new vfs::OverlayFileSystem(D
));
216 EXPECT_FALSE(Status
= O
->status("/foo"));
218 D
->addRegularFile("/foo");
219 Status
= D
->status("/foo");
220 EXPECT_FALSE(Status
.getError());
222 ErrorOr
<vfs::Status
> Status2((std::error_code()));
223 Status2
= O
->status("/foo");
224 EXPECT_FALSE(Status2
.getError());
225 EXPECT_TRUE(Status
->equivalent(*Status2
));
228 TEST(VirtualFileSystemTest
, GetRealPathInOverlay
) {
229 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
230 Lower
->addRegularFile("/foo");
231 Lower
->addSymlink("/lower_link");
232 IntrusiveRefCntPtr
<DummyFileSystem
> Upper(new DummyFileSystem());
234 IntrusiveRefCntPtr
<vfs::OverlayFileSystem
> O(
235 new vfs::OverlayFileSystem(Lower
));
236 O
->pushOverlay(Upper
);
239 SmallString
<16> RealPath
;
240 EXPECT_FALSE(O
->getRealPath("/foo", RealPath
));
241 EXPECT_EQ(RealPath
.str(), "/foo");
243 // Expect no error getting real path for symlink in lower overlay.
244 EXPECT_FALSE(O
->getRealPath("/lower_link", RealPath
));
245 EXPECT_EQ(RealPath
.str(), "/symlink");
247 // Try a non-existing link.
248 EXPECT_EQ(O
->getRealPath("/upper_link", RealPath
),
249 errc::no_such_file_or_directory
);
251 // Add a new symlink in upper.
252 Upper
->addSymlink("/upper_link");
253 EXPECT_FALSE(O
->getRealPath("/upper_link", RealPath
));
254 EXPECT_EQ(RealPath
.str(), "/symlink");
257 TEST(VirtualFileSystemTest
, OverlayFiles
) {
258 IntrusiveRefCntPtr
<DummyFileSystem
> Base(new DummyFileSystem());
259 IntrusiveRefCntPtr
<DummyFileSystem
> Middle(new DummyFileSystem());
260 IntrusiveRefCntPtr
<DummyFileSystem
> Top(new DummyFileSystem());
261 IntrusiveRefCntPtr
<vfs::OverlayFileSystem
> O(
262 new vfs::OverlayFileSystem(Base
));
263 O
->pushOverlay(Middle
);
266 ErrorOr
<vfs::Status
> Status1((std::error_code())),
267 Status2((std::error_code())), Status3((std::error_code())),
268 StatusB((std::error_code())), StatusM((std::error_code())),
269 StatusT((std::error_code()));
271 Base
->addRegularFile("/foo");
272 StatusB
= Base
->status("/foo");
273 ASSERT_FALSE(StatusB
.getError());
274 Status1
= O
->status("/foo");
275 ASSERT_FALSE(Status1
.getError());
276 Middle
->addRegularFile("/foo");
277 StatusM
= Middle
->status("/foo");
278 ASSERT_FALSE(StatusM
.getError());
279 Status2
= O
->status("/foo");
280 ASSERT_FALSE(Status2
.getError());
281 Top
->addRegularFile("/foo");
282 StatusT
= Top
->status("/foo");
283 ASSERT_FALSE(StatusT
.getError());
284 Status3
= O
->status("/foo");
285 ASSERT_FALSE(Status3
.getError());
287 EXPECT_TRUE(Status1
->equivalent(*StatusB
));
288 EXPECT_TRUE(Status2
->equivalent(*StatusM
));
289 EXPECT_TRUE(Status3
->equivalent(*StatusT
));
291 EXPECT_FALSE(Status1
->equivalent(*Status2
));
292 EXPECT_FALSE(Status2
->equivalent(*Status3
));
293 EXPECT_FALSE(Status1
->equivalent(*Status3
));
296 TEST(VirtualFileSystemTest
, OverlayDirsNonMerged
) {
297 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
298 IntrusiveRefCntPtr
<DummyFileSystem
> Upper(new DummyFileSystem());
299 IntrusiveRefCntPtr
<vfs::OverlayFileSystem
> O(
300 new vfs::OverlayFileSystem(Lower
));
301 O
->pushOverlay(Upper
);
303 Lower
->addDirectory("/lower-only");
304 Upper
->addDirectory("/upper-only");
306 // non-merged paths should be the same
307 ErrorOr
<vfs::Status
> Status1
= Lower
->status("/lower-only");
308 ASSERT_FALSE(Status1
.getError());
309 ErrorOr
<vfs::Status
> Status2
= O
->status("/lower-only");
310 ASSERT_FALSE(Status2
.getError());
311 EXPECT_TRUE(Status1
->equivalent(*Status2
));
313 Status1
= Upper
->status("/upper-only");
314 ASSERT_FALSE(Status1
.getError());
315 Status2
= O
->status("/upper-only");
316 ASSERT_FALSE(Status2
.getError());
317 EXPECT_TRUE(Status1
->equivalent(*Status2
));
320 TEST(VirtualFileSystemTest
, MergedDirPermissions
) {
321 // merged directories get the permissions of the upper dir
322 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
323 IntrusiveRefCntPtr
<DummyFileSystem
> Upper(new DummyFileSystem());
324 IntrusiveRefCntPtr
<vfs::OverlayFileSystem
> O(
325 new vfs::OverlayFileSystem(Lower
));
326 O
->pushOverlay(Upper
);
328 ErrorOr
<vfs::Status
> Status((std::error_code()));
329 Lower
->addDirectory("/both", sys::fs::owner_read
);
330 Upper
->addDirectory("/both", sys::fs::owner_all
| sys::fs::group_read
);
331 Status
= O
->status("/both");
332 ASSERT_FALSE(Status
.getError());
333 EXPECT_EQ(0740, Status
->getPermissions());
335 // permissions (as usual) are not recursively applied
336 Lower
->addRegularFile("/both/foo", sys::fs::owner_read
);
337 Upper
->addRegularFile("/both/bar", sys::fs::owner_write
);
338 Status
= O
->status("/both/foo");
339 ASSERT_FALSE(Status
.getError());
340 EXPECT_EQ(0400, Status
->getPermissions());
341 Status
= O
->status("/both/bar");
342 ASSERT_FALSE(Status
.getError());
343 EXPECT_EQ(0200, Status
->getPermissions());
348 SmallString
<128> Path
;
349 ScopedDir(const Twine
&Name
, bool Unique
= false) {
352 EC
= llvm::sys::fs::createUniqueDirectory(Name
, Path
);
355 EC
= llvm::sys::fs::create_directory(Twine(Path
));
363 EXPECT_FALSE(llvm::sys::fs::remove(Path
.str()));
366 operator StringRef() { return Path
.str(); }
370 SmallString
<128> Path
;
371 ScopedLink(const Twine
&To
, const Twine
&From
) {
373 std::error_code EC
= sys::fs::create_link(To
, From
);
380 EXPECT_FALSE(llvm::sys::fs::remove(Path
.str()));
383 operator StringRef() { return Path
.str(); }
385 } // end anonymous namespace
387 TEST(VirtualFileSystemTest
, BasicRealFSIteration
) {
388 ScopedDir
TestDirectory("virtual-file-system-test", /*Unique*/ true);
389 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= vfs::getRealFileSystem();
392 vfs::directory_iterator I
= FS
->dir_begin(Twine(TestDirectory
), EC
);
394 EXPECT_EQ(vfs::directory_iterator(), I
); // empty directory is empty
396 ScopedDir
_a(TestDirectory
+ "/a");
397 ScopedDir
_ab(TestDirectory
+ "/a/b");
398 ScopedDir
_c(TestDirectory
+ "/c");
399 ScopedDir
_cd(TestDirectory
+ "/c/d");
401 I
= FS
->dir_begin(Twine(TestDirectory
), EC
);
403 ASSERT_NE(vfs::directory_iterator(), I
);
404 // Check either a or c, since we can't rely on the iteration order.
405 EXPECT_TRUE(I
->path().endswith("a") || I
->path().endswith("c"));
408 ASSERT_NE(vfs::directory_iterator(), I
);
409 EXPECT_TRUE(I
->path().endswith("a") || I
->path().endswith("c"));
411 EXPECT_EQ(vfs::directory_iterator(), I
);
415 TEST(VirtualFileSystemTest
, BrokenSymlinkRealFSIteration
) {
416 ScopedDir
TestDirectory("virtual-file-system-test", /*Unique*/ true);
417 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= vfs::getRealFileSystem();
419 ScopedLink
_a("no_such_file", TestDirectory
+ "/a");
420 ScopedDir
_b(TestDirectory
+ "/b");
421 ScopedLink
_c("no_such_file", TestDirectory
+ "/c");
423 // Should get no iteration error, but a stat error for the broken symlinks.
424 std::map
<std::string
, std::error_code
> StatResults
;
426 for (vfs::directory_iterator I
= FS
->dir_begin(Twine(TestDirectory
), EC
), E
;
427 I
!= E
; I
.increment(EC
)) {
429 StatResults
[sys::path::filename(I
->path())] =
430 FS
->status(I
->path()).getError();
435 Pair("a", std::make_error_code(std::errc::no_such_file_or_directory
)),
436 Pair("b", std::error_code()),
438 std::make_error_code(std::errc::no_such_file_or_directory
))));
442 TEST(VirtualFileSystemTest
, BasicRealFSRecursiveIteration
) {
443 ScopedDir
TestDirectory("virtual-file-system-test", /*Unique*/ true);
444 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= vfs::getRealFileSystem();
447 auto I
= vfs::recursive_directory_iterator(*FS
, Twine(TestDirectory
), EC
);
449 EXPECT_EQ(vfs::recursive_directory_iterator(), I
); // empty directory is empty
451 ScopedDir
_a(TestDirectory
+ "/a");
452 ScopedDir
_ab(TestDirectory
+ "/a/b");
453 ScopedDir
_c(TestDirectory
+ "/c");
454 ScopedDir
_cd(TestDirectory
+ "/c/d");
456 I
= vfs::recursive_directory_iterator(*FS
, Twine(TestDirectory
), EC
);
458 ASSERT_NE(vfs::recursive_directory_iterator(), I
);
460 std::vector
<std::string
> Contents
;
461 for (auto E
= vfs::recursive_directory_iterator(); !EC
&& I
!= E
;
463 Contents
.push_back(I
->path());
466 // Check contents, which may be in any order
467 EXPECT_EQ(4U, Contents
.size());
468 int Counts
[4] = {0, 0, 0, 0};
469 for (const std::string
&Name
: Contents
) {
470 ASSERT_FALSE(Name
.empty());
471 int Index
= Name
[Name
.size() - 1] - 'a';
472 ASSERT_TRUE(Index
>= 0 && Index
< 4);
475 EXPECT_EQ(1, Counts
[0]); // a
476 EXPECT_EQ(1, Counts
[1]); // b
477 EXPECT_EQ(1, Counts
[2]); // c
478 EXPECT_EQ(1, Counts
[3]); // d
482 TEST(VirtualFileSystemTest
, BrokenSymlinkRealFSRecursiveIteration
) {
483 ScopedDir
TestDirectory("virtual-file-system-test", /*Unique*/ true);
484 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= vfs::getRealFileSystem();
486 ScopedLink
_a("no_such_file", TestDirectory
+ "/a");
487 ScopedDir
_b(TestDirectory
+ "/b");
488 ScopedLink
_ba("no_such_file", TestDirectory
+ "/b/a");
489 ScopedDir
_bb(TestDirectory
+ "/b/b");
490 ScopedLink
_bc("no_such_file", TestDirectory
+ "/b/c");
491 ScopedLink
_c("no_such_file", TestDirectory
+ "/c");
492 ScopedDir
_d(TestDirectory
+ "/d");
493 ScopedDir
_dd(TestDirectory
+ "/d/d");
494 ScopedDir
_ddd(TestDirectory
+ "/d/d/d");
495 ScopedLink
_e("no_such_file", TestDirectory
+ "/e");
497 std::vector
<std::string
> VisitedBrokenSymlinks
;
498 std::vector
<std::string
> VisitedNonBrokenSymlinks
;
500 for (vfs::recursive_directory_iterator
I(*FS
, Twine(TestDirectory
), EC
), E
;
501 I
!= E
; I
.increment(EC
)) {
503 (FS
->status(I
->path()) ? VisitedNonBrokenSymlinks
: VisitedBrokenSymlinks
)
504 .push_back(I
->path());
507 // Check visited file names.
508 EXPECT_THAT(VisitedBrokenSymlinks
,
509 UnorderedElementsAre(StringRef(_a
), StringRef(_ba
),
510 StringRef(_bc
), StringRef(_c
),
512 EXPECT_THAT(VisitedNonBrokenSymlinks
,
513 UnorderedElementsAre(StringRef(_b
), StringRef(_bb
), StringRef(_d
),
514 StringRef(_dd
), StringRef(_ddd
)));
518 template <typename DirIter
>
519 static void checkContents(DirIter I
, ArrayRef
<StringRef
> ExpectedOut
) {
521 SmallVector
<StringRef
, 4> Expected(ExpectedOut
.begin(), ExpectedOut
.end());
522 SmallVector
<std::string
, 4> InputToCheck
;
524 // Do not rely on iteration order to check for contents, sort both
525 // content vectors before comparison.
526 for (DirIter E
; !EC
&& I
!= E
; I
.increment(EC
))
527 InputToCheck
.push_back(I
->path());
529 llvm::sort(InputToCheck
);
530 llvm::sort(Expected
);
531 EXPECT_EQ(InputToCheck
.size(), Expected
.size());
533 unsigned LastElt
= std::min(InputToCheck
.size(), Expected
.size());
534 for (unsigned Idx
= 0; Idx
!= LastElt
; ++Idx
)
535 EXPECT_EQ(StringRef(InputToCheck
[Idx
]), Expected
[Idx
]);
538 TEST(VirtualFileSystemTest
, OverlayIteration
) {
539 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
540 IntrusiveRefCntPtr
<DummyFileSystem
> Upper(new DummyFileSystem());
541 IntrusiveRefCntPtr
<vfs::OverlayFileSystem
> O(
542 new vfs::OverlayFileSystem(Lower
));
543 O
->pushOverlay(Upper
);
546 checkContents(O
->dir_begin("/", EC
), ArrayRef
<StringRef
>());
548 Lower
->addRegularFile("/file1");
549 checkContents(O
->dir_begin("/", EC
), ArrayRef
<StringRef
>("/file1"));
551 Upper
->addRegularFile("/file2");
552 checkContents(O
->dir_begin("/", EC
), {"/file2", "/file1"});
554 Lower
->addDirectory("/dir1");
555 Lower
->addRegularFile("/dir1/foo");
556 Upper
->addDirectory("/dir2");
557 Upper
->addRegularFile("/dir2/foo");
558 checkContents(O
->dir_begin("/dir2", EC
), ArrayRef
<StringRef
>("/dir2/foo"));
559 checkContents(O
->dir_begin("/", EC
), {"/dir2", "/file2", "/dir1", "/file1"});
562 TEST(VirtualFileSystemTest
, OverlayRecursiveIteration
) {
563 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
564 IntrusiveRefCntPtr
<DummyFileSystem
> Middle(new DummyFileSystem());
565 IntrusiveRefCntPtr
<DummyFileSystem
> Upper(new DummyFileSystem());
566 IntrusiveRefCntPtr
<vfs::OverlayFileSystem
> O(
567 new vfs::OverlayFileSystem(Lower
));
568 O
->pushOverlay(Middle
);
569 O
->pushOverlay(Upper
);
572 checkContents(vfs::recursive_directory_iterator(*O
, "/", EC
),
573 ArrayRef
<StringRef
>());
575 Lower
->addRegularFile("/file1");
576 checkContents(vfs::recursive_directory_iterator(*O
, "/", EC
),
577 ArrayRef
<StringRef
>("/file1"));
579 Upper
->addDirectory("/dir");
580 Upper
->addRegularFile("/dir/file2");
581 checkContents(vfs::recursive_directory_iterator(*O
, "/", EC
),
582 {"/dir", "/dir/file2", "/file1"});
584 Lower
->addDirectory("/dir1");
585 Lower
->addRegularFile("/dir1/foo");
586 Lower
->addDirectory("/dir1/a");
587 Lower
->addRegularFile("/dir1/a/b");
588 Middle
->addDirectory("/a");
589 Middle
->addDirectory("/a/b");
590 Middle
->addDirectory("/a/b/c");
591 Middle
->addRegularFile("/a/b/c/d");
592 Middle
->addRegularFile("/hiddenByUp");
593 Upper
->addDirectory("/dir2");
594 Upper
->addRegularFile("/dir2/foo");
595 Upper
->addRegularFile("/hiddenByUp");
596 checkContents(vfs::recursive_directory_iterator(*O
, "/dir2", EC
),
597 ArrayRef
<StringRef
>("/dir2/foo"));
598 checkContents(vfs::recursive_directory_iterator(*O
, "/", EC
),
599 {"/dir", "/dir/file2", "/dir2", "/dir2/foo", "/hiddenByUp",
600 "/a", "/a/b", "/a/b/c", "/a/b/c/d", "/dir1", "/dir1/a",
601 "/dir1/a/b", "/dir1/foo", "/file1"});
604 TEST(VirtualFileSystemTest
, ThreeLevelIteration
) {
605 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
606 IntrusiveRefCntPtr
<DummyFileSystem
> Middle(new DummyFileSystem());
607 IntrusiveRefCntPtr
<DummyFileSystem
> Upper(new DummyFileSystem());
608 IntrusiveRefCntPtr
<vfs::OverlayFileSystem
> O(
609 new vfs::OverlayFileSystem(Lower
));
610 O
->pushOverlay(Middle
);
611 O
->pushOverlay(Upper
);
614 checkContents(O
->dir_begin("/", EC
), ArrayRef
<StringRef
>());
616 Middle
->addRegularFile("/file2");
617 checkContents(O
->dir_begin("/", EC
), ArrayRef
<StringRef
>("/file2"));
619 Lower
->addRegularFile("/file1");
620 Upper
->addRegularFile("/file3");
621 checkContents(O
->dir_begin("/", EC
), {"/file3", "/file2", "/file1"});
624 TEST(VirtualFileSystemTest
, HiddenInIteration
) {
625 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
626 IntrusiveRefCntPtr
<DummyFileSystem
> Middle(new DummyFileSystem());
627 IntrusiveRefCntPtr
<DummyFileSystem
> Upper(new DummyFileSystem());
628 IntrusiveRefCntPtr
<vfs::OverlayFileSystem
> O(
629 new vfs::OverlayFileSystem(Lower
));
630 O
->pushOverlay(Middle
);
631 O
->pushOverlay(Upper
);
634 Lower
->addRegularFile("/onlyInLow");
635 Lower
->addDirectory("/hiddenByMid");
636 Lower
->addDirectory("/hiddenByUp");
637 Middle
->addRegularFile("/onlyInMid");
638 Middle
->addRegularFile("/hiddenByMid");
639 Middle
->addDirectory("/hiddenByUp");
640 Upper
->addRegularFile("/onlyInUp");
641 Upper
->addRegularFile("/hiddenByUp");
643 O
->dir_begin("/", EC
),
644 {"/hiddenByUp", "/onlyInUp", "/hiddenByMid", "/onlyInMid", "/onlyInLow"});
646 // Make sure we get the top-most entry
649 vfs::directory_iterator I
= O
->dir_begin("/", EC
), E
;
650 for (; !EC
&& I
!= E
; I
.increment(EC
))
651 if (I
->path() == "/hiddenByUp")
654 EXPECT_EQ(sys::fs::file_type::regular_file
, I
->type());
658 vfs::directory_iterator I
= O
->dir_begin("/", EC
), E
;
659 for (; !EC
&& I
!= E
; I
.increment(EC
))
660 if (I
->path() == "/hiddenByMid")
663 EXPECT_EQ(sys::fs::file_type::regular_file
, I
->type());
667 class InMemoryFileSystemTest
: public ::testing::Test
{
669 llvm::vfs::InMemoryFileSystem FS
;
670 llvm::vfs::InMemoryFileSystem NormalizedFS
;
672 InMemoryFileSystemTest()
673 : FS(/*UseNormalizedPaths=*/false),
674 NormalizedFS(/*UseNormalizedPaths=*/true) {}
677 MATCHER_P2(IsHardLinkTo
, FS
, Target
, "") {
678 StringRef From
= arg
;
679 StringRef To
= Target
;
680 auto OpenedFrom
= FS
->openFileForRead(From
);
681 auto OpenedTo
= FS
->openFileForRead(To
);
682 return !OpenedFrom
.getError() && !OpenedTo
.getError() &&
683 (*OpenedFrom
)->status()->getUniqueID() ==
684 (*OpenedTo
)->status()->getUniqueID();
687 TEST_F(InMemoryFileSystemTest
, IsEmpty
) {
688 auto Stat
= FS
.status("/a");
689 ASSERT_EQ(Stat
.getError(), errc::no_such_file_or_directory
) << FS
.toString();
690 Stat
= FS
.status("/");
691 ASSERT_EQ(Stat
.getError(), errc::no_such_file_or_directory
) << FS
.toString();
694 TEST_F(InMemoryFileSystemTest
, WindowsPath
) {
695 FS
.addFile("c:/windows/system128/foo.cpp", 0, MemoryBuffer::getMemBuffer(""));
696 auto Stat
= FS
.status("c:");
698 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << FS
.toString();
700 Stat
= FS
.status("c:/windows/system128/foo.cpp");
701 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << FS
.toString();
702 FS
.addFile("d:/windows/foo.cpp", 0, MemoryBuffer::getMemBuffer(""));
703 Stat
= FS
.status("d:/windows/foo.cpp");
704 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << FS
.toString();
707 TEST_F(InMemoryFileSystemTest
, OverlayFile
) {
708 FS
.addFile("/a", 0, MemoryBuffer::getMemBuffer("a"));
709 NormalizedFS
.addFile("/a", 0, MemoryBuffer::getMemBuffer("a"));
710 auto Stat
= FS
.status("/");
711 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << FS
.toString();
712 Stat
= FS
.status("/.");
714 Stat
= NormalizedFS
.status("/.");
715 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << FS
.toString();
716 Stat
= FS
.status("/a");
717 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n" << FS
.toString();
718 ASSERT_EQ("/a", Stat
->getName());
721 TEST_F(InMemoryFileSystemTest
, OverlayFileNoOwn
) {
722 auto Buf
= MemoryBuffer::getMemBuffer("a");
723 FS
.addFileNoOwn("/a", 0, Buf
.get());
724 auto Stat
= FS
.status("/a");
725 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n" << FS
.toString();
726 ASSERT_EQ("/a", Stat
->getName());
729 TEST_F(InMemoryFileSystemTest
, OpenFileForRead
) {
730 FS
.addFile("/a", 0, MemoryBuffer::getMemBuffer("a"));
731 FS
.addFile("././c", 0, MemoryBuffer::getMemBuffer("c"));
732 FS
.addFile("./d/../d", 0, MemoryBuffer::getMemBuffer("d"));
733 NormalizedFS
.addFile("/a", 0, MemoryBuffer::getMemBuffer("a"));
734 NormalizedFS
.addFile("././c", 0, MemoryBuffer::getMemBuffer("c"));
735 NormalizedFS
.addFile("./d/../d", 0, MemoryBuffer::getMemBuffer("d"));
736 auto File
= FS
.openFileForRead("/a");
737 ASSERT_EQ("a", (*(*File
)->getBuffer("ignored"))->getBuffer());
738 File
= FS
.openFileForRead("/a"); // Open again.
739 ASSERT_EQ("a", (*(*File
)->getBuffer("ignored"))->getBuffer());
740 File
= NormalizedFS
.openFileForRead("/././a"); // Open again.
741 ASSERT_EQ("a", (*(*File
)->getBuffer("ignored"))->getBuffer());
742 File
= FS
.openFileForRead("/");
743 ASSERT_EQ(File
.getError(), errc::invalid_argument
) << FS
.toString();
744 File
= FS
.openFileForRead("/b");
745 ASSERT_EQ(File
.getError(), errc::no_such_file_or_directory
) << FS
.toString();
746 File
= FS
.openFileForRead("./c");
748 File
= FS
.openFileForRead("e/../d");
750 File
= NormalizedFS
.openFileForRead("./c");
751 ASSERT_EQ("c", (*(*File
)->getBuffer("ignored"))->getBuffer());
752 File
= NormalizedFS
.openFileForRead("e/../d");
753 ASSERT_EQ("d", (*(*File
)->getBuffer("ignored"))->getBuffer());
756 TEST_F(InMemoryFileSystemTest
, DuplicatedFile
) {
757 ASSERT_TRUE(FS
.addFile("/a", 0, MemoryBuffer::getMemBuffer("a")));
758 ASSERT_FALSE(FS
.addFile("/a/b", 0, MemoryBuffer::getMemBuffer("a")));
759 ASSERT_TRUE(FS
.addFile("/a", 0, MemoryBuffer::getMemBuffer("a")));
760 ASSERT_FALSE(FS
.addFile("/a", 0, MemoryBuffer::getMemBuffer("b")));
763 TEST_F(InMemoryFileSystemTest
, DirectoryIteration
) {
764 FS
.addFile("/a", 0, MemoryBuffer::getMemBuffer(""));
765 FS
.addFile("/b/c", 0, MemoryBuffer::getMemBuffer(""));
768 vfs::directory_iterator I
= FS
.dir_begin("/", EC
);
770 ASSERT_EQ("/a", I
->path());
773 ASSERT_EQ("/b", I
->path());
776 ASSERT_EQ(vfs::directory_iterator(), I
);
778 I
= FS
.dir_begin("/b", EC
);
780 // When on Windows, we end up with "/b\\c" as the name. Convert to Posix
781 // path for the sake of the comparison.
782 ASSERT_EQ("/b/c", getPosixPath(I
->path()));
785 ASSERT_EQ(vfs::directory_iterator(), I
);
788 TEST_F(InMemoryFileSystemTest
, WorkingDirectory
) {
789 FS
.setCurrentWorkingDirectory("/b");
790 FS
.addFile("c", 0, MemoryBuffer::getMemBuffer(""));
792 auto Stat
= FS
.status("/b/c");
793 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n" << FS
.toString();
794 ASSERT_EQ("/b/c", Stat
->getName());
795 ASSERT_EQ("/b", *FS
.getCurrentWorkingDirectory());
797 Stat
= FS
.status("c");
798 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n" << FS
.toString();
800 NormalizedFS
.setCurrentWorkingDirectory("/b/c");
801 NormalizedFS
.setCurrentWorkingDirectory(".");
803 getPosixPath(NormalizedFS
.getCurrentWorkingDirectory().get()));
804 NormalizedFS
.setCurrentWorkingDirectory("..");
806 getPosixPath(NormalizedFS
.getCurrentWorkingDirectory().get()));
810 TEST_F(InMemoryFileSystemTest
, GetRealPath
) {
811 SmallString
<16> Path
;
812 EXPECT_EQ(FS
.getRealPath("b", Path
), errc::operation_not_permitted
);
814 auto GetRealPath
= [this](StringRef P
) {
815 SmallString
<16> Output
;
816 auto EC
= FS
.getRealPath(P
, Output
);
818 return Output
.str().str();
821 FS
.setCurrentWorkingDirectory("a");
822 EXPECT_EQ(GetRealPath("b"), "a/b");
823 EXPECT_EQ(GetRealPath("../b"), "b");
824 EXPECT_EQ(GetRealPath("b/./c"), "a/b/c");
826 FS
.setCurrentWorkingDirectory("/a");
827 EXPECT_EQ(GetRealPath("b"), "/a/b");
828 EXPECT_EQ(GetRealPath("../b"), "/b");
829 EXPECT_EQ(GetRealPath("b/./c"), "/a/b/c");
833 TEST_F(InMemoryFileSystemTest
, AddFileWithUser
) {
834 FS
.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"), 0xFEEDFACE);
835 auto Stat
= FS
.status("/a");
836 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n" << FS
.toString();
837 ASSERT_TRUE(Stat
->isDirectory());
838 ASSERT_EQ(0xFEEDFACE, Stat
->getUser());
839 Stat
= FS
.status("/a/b");
840 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n" << FS
.toString();
841 ASSERT_TRUE(Stat
->isDirectory());
842 ASSERT_EQ(0xFEEDFACE, Stat
->getUser());
843 Stat
= FS
.status("/a/b/c");
844 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n" << FS
.toString();
845 ASSERT_TRUE(Stat
->isRegularFile());
846 ASSERT_EQ(sys::fs::perms::all_all
, Stat
->getPermissions());
847 ASSERT_EQ(0xFEEDFACE, Stat
->getUser());
850 TEST_F(InMemoryFileSystemTest
, AddFileWithGroup
) {
851 FS
.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"), None
, 0xDABBAD00);
852 auto Stat
= FS
.status("/a");
853 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n" << FS
.toString();
854 ASSERT_TRUE(Stat
->isDirectory());
855 ASSERT_EQ(0xDABBAD00, Stat
->getGroup());
856 Stat
= FS
.status("/a/b");
857 ASSERT_TRUE(Stat
->isDirectory());
858 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n" << FS
.toString();
859 ASSERT_EQ(0xDABBAD00, Stat
->getGroup());
860 Stat
= FS
.status("/a/b/c");
861 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n" << FS
.toString();
862 ASSERT_TRUE(Stat
->isRegularFile());
863 ASSERT_EQ(sys::fs::perms::all_all
, Stat
->getPermissions());
864 ASSERT_EQ(0xDABBAD00, Stat
->getGroup());
867 TEST_F(InMemoryFileSystemTest
, AddFileWithFileType
) {
868 FS
.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"), None
, None
,
869 sys::fs::file_type::socket_file
);
870 auto Stat
= FS
.status("/a");
871 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n" << FS
.toString();
872 ASSERT_TRUE(Stat
->isDirectory());
873 Stat
= FS
.status("/a/b");
874 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n" << FS
.toString();
875 ASSERT_TRUE(Stat
->isDirectory());
876 Stat
= FS
.status("/a/b/c");
877 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n" << FS
.toString();
878 ASSERT_EQ(sys::fs::file_type::socket_file
, Stat
->getType());
879 ASSERT_EQ(sys::fs::perms::all_all
, Stat
->getPermissions());
882 TEST_F(InMemoryFileSystemTest
, AddFileWithPerms
) {
883 FS
.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"), None
, None
, None
,
884 sys::fs::perms::owner_read
| sys::fs::perms::owner_write
);
885 auto Stat
= FS
.status("/a");
886 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n" << FS
.toString();
887 ASSERT_TRUE(Stat
->isDirectory());
888 ASSERT_EQ(sys::fs::perms::owner_read
| sys::fs::perms::owner_write
|
889 sys::fs::perms::owner_exe
,
890 Stat
->getPermissions());
891 Stat
= FS
.status("/a/b");
892 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n" << FS
.toString();
893 ASSERT_TRUE(Stat
->isDirectory());
894 ASSERT_EQ(sys::fs::perms::owner_read
| sys::fs::perms::owner_write
|
895 sys::fs::perms::owner_exe
,
896 Stat
->getPermissions());
897 Stat
= FS
.status("/a/b/c");
898 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n" << FS
.toString();
899 ASSERT_TRUE(Stat
->isRegularFile());
900 ASSERT_EQ(sys::fs::perms::owner_read
| sys::fs::perms::owner_write
,
901 Stat
->getPermissions());
904 TEST_F(InMemoryFileSystemTest
, AddDirectoryThenAddChild
) {
905 FS
.addFile("/a", 0, MemoryBuffer::getMemBuffer(""), /*User=*/None
,
906 /*Group=*/None
, sys::fs::file_type::directory_file
);
907 FS
.addFile("/a/b", 0, MemoryBuffer::getMemBuffer("abc"), /*User=*/None
,
908 /*Group=*/None
, sys::fs::file_type::regular_file
);
909 auto Stat
= FS
.status("/a");
910 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n" << FS
.toString();
911 ASSERT_TRUE(Stat
->isDirectory());
912 Stat
= FS
.status("/a/b");
913 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n" << FS
.toString();
914 ASSERT_TRUE(Stat
->isRegularFile());
917 // Test that the name returned by status() is in the same form as the path that
918 // was requested (to match the behavior of RealFileSystem).
919 TEST_F(InMemoryFileSystemTest
, StatusName
) {
920 NormalizedFS
.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"),
922 /*Group=*/None
, sys::fs::file_type::regular_file
);
923 NormalizedFS
.setCurrentWorkingDirectory("/a/b");
925 // Access using InMemoryFileSystem::status.
926 auto Stat
= NormalizedFS
.status("../b/c");
927 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n"
928 << NormalizedFS
.toString();
929 ASSERT_TRUE(Stat
->isRegularFile());
930 ASSERT_EQ("../b/c", Stat
->getName());
932 // Access using InMemoryFileAdaptor::status.
933 auto File
= NormalizedFS
.openFileForRead("../b/c");
934 ASSERT_FALSE(File
.getError()) << File
.getError() << "\n"
935 << NormalizedFS
.toString();
936 Stat
= (*File
)->status();
937 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n"
938 << NormalizedFS
.toString();
939 ASSERT_TRUE(Stat
->isRegularFile());
940 ASSERT_EQ("../b/c", Stat
->getName());
942 // Access using a directory iterator.
944 llvm::vfs::directory_iterator It
= NormalizedFS
.dir_begin("../b", EC
);
945 // When on Windows, we end up with "../b\\c" as the name. Convert to Posix
946 // path for the sake of the comparison.
947 ASSERT_EQ("../b/c", getPosixPath(It
->path()));
950 TEST_F(InMemoryFileSystemTest
, AddHardLinkToFile
) {
951 StringRef FromLink
= "/path/to/FROM/link";
952 StringRef Target
= "/path/to/TO/file";
953 FS
.addFile(Target
, 0, MemoryBuffer::getMemBuffer("content of target"));
954 EXPECT_TRUE(FS
.addHardLink(FromLink
, Target
));
955 EXPECT_THAT(FromLink
, IsHardLinkTo(&FS
, Target
));
956 EXPECT_TRUE(FS
.status(FromLink
)->getSize() == FS
.status(Target
)->getSize());
957 EXPECT_TRUE(FS
.getBufferForFile(FromLink
)->get()->getBuffer() ==
958 FS
.getBufferForFile(Target
)->get()->getBuffer());
961 TEST_F(InMemoryFileSystemTest
, AddHardLinkInChainPattern
) {
962 StringRef Link0
= "/path/to/0/link";
963 StringRef Link1
= "/path/to/1/link";
964 StringRef Link2
= "/path/to/2/link";
965 StringRef Target
= "/path/to/target";
966 FS
.addFile(Target
, 0, MemoryBuffer::getMemBuffer("content of target file"));
967 EXPECT_TRUE(FS
.addHardLink(Link2
, Target
));
968 EXPECT_TRUE(FS
.addHardLink(Link1
, Link2
));
969 EXPECT_TRUE(FS
.addHardLink(Link0
, Link1
));
970 EXPECT_THAT(Link0
, IsHardLinkTo(&FS
, Target
));
971 EXPECT_THAT(Link1
, IsHardLinkTo(&FS
, Target
));
972 EXPECT_THAT(Link2
, IsHardLinkTo(&FS
, Target
));
975 TEST_F(InMemoryFileSystemTest
, AddHardLinkToAFileThatWasNotAddedBefore
) {
976 EXPECT_FALSE(FS
.addHardLink("/path/to/link", "/path/to/target"));
979 TEST_F(InMemoryFileSystemTest
, AddHardLinkFromAFileThatWasAddedBefore
) {
980 StringRef Link
= "/path/to/link";
981 StringRef Target
= "/path/to/target";
982 FS
.addFile(Target
, 0, MemoryBuffer::getMemBuffer("content of target"));
983 FS
.addFile(Link
, 0, MemoryBuffer::getMemBuffer("content of link"));
984 EXPECT_FALSE(FS
.addHardLink(Link
, Target
));
987 TEST_F(InMemoryFileSystemTest
, AddSameHardLinkMoreThanOnce
) {
988 StringRef Link
= "/path/to/link";
989 StringRef Target
= "/path/to/target";
990 FS
.addFile(Target
, 0, MemoryBuffer::getMemBuffer("content of target"));
991 EXPECT_TRUE(FS
.addHardLink(Link
, Target
));
992 EXPECT_FALSE(FS
.addHardLink(Link
, Target
));
995 TEST_F(InMemoryFileSystemTest
, AddFileInPlaceOfAHardLinkWithSameContent
) {
996 StringRef Link
= "/path/to/link";
997 StringRef Target
= "/path/to/target";
998 StringRef Content
= "content of target";
999 EXPECT_TRUE(FS
.addFile(Target
, 0, MemoryBuffer::getMemBuffer(Content
)));
1000 EXPECT_TRUE(FS
.addHardLink(Link
, Target
));
1001 EXPECT_TRUE(FS
.addFile(Link
, 0, MemoryBuffer::getMemBuffer(Content
)));
1004 TEST_F(InMemoryFileSystemTest
, AddFileInPlaceOfAHardLinkWithDifferentContent
) {
1005 StringRef Link
= "/path/to/link";
1006 StringRef Target
= "/path/to/target";
1007 StringRef Content
= "content of target";
1008 StringRef LinkContent
= "different content of link";
1009 EXPECT_TRUE(FS
.addFile(Target
, 0, MemoryBuffer::getMemBuffer(Content
)));
1010 EXPECT_TRUE(FS
.addHardLink(Link
, Target
));
1011 EXPECT_FALSE(FS
.addFile(Link
, 0, MemoryBuffer::getMemBuffer(LinkContent
)));
1014 TEST_F(InMemoryFileSystemTest
, AddHardLinkToADirectory
) {
1015 StringRef Dir
= "path/to/dummy/dir";
1016 StringRef Link
= "/path/to/link";
1017 StringRef File
= "path/to/dummy/dir/target";
1018 StringRef Content
= "content of target";
1019 EXPECT_TRUE(FS
.addFile(File
, 0, MemoryBuffer::getMemBuffer(Content
)));
1020 EXPECT_FALSE(FS
.addHardLink(Link
, Dir
));
1023 TEST_F(InMemoryFileSystemTest
, AddHardLinkFromADirectory
) {
1024 StringRef Dir
= "path/to/dummy/dir";
1025 StringRef Target
= "path/to/dummy/dir/target";
1026 StringRef Content
= "content of target";
1027 EXPECT_TRUE(FS
.addFile(Target
, 0, MemoryBuffer::getMemBuffer(Content
)));
1028 EXPECT_FALSE(FS
.addHardLink(Dir
, Target
));
1031 TEST_F(InMemoryFileSystemTest
, AddHardLinkUnderAFile
) {
1032 StringRef CommonContent
= "content string";
1033 FS
.addFile("/a/b", 0, MemoryBuffer::getMemBuffer(CommonContent
));
1034 FS
.addFile("/c/d", 0, MemoryBuffer::getMemBuffer(CommonContent
));
1035 EXPECT_FALSE(FS
.addHardLink("/c/d/e", "/a/b"));
1038 TEST_F(InMemoryFileSystemTest
, RecursiveIterationWithHardLink
) {
1040 FS
.addFile("/a/b", 0, MemoryBuffer::getMemBuffer("content string"));
1041 EXPECT_TRUE(FS
.addHardLink("/c/d", "/a/b"));
1042 auto I
= vfs::recursive_directory_iterator(FS
, "/", EC
);
1044 std::vector
<std::string
> Nodes
;
1045 for (auto E
= vfs::recursive_directory_iterator(); !EC
&& I
!= E
;
1047 Nodes
.push_back(getPosixPath(I
->path()));
1049 EXPECT_THAT(Nodes
, testing::UnorderedElementsAre("/a", "/a/b", "/c", "/c/d"));
1052 // NOTE: in the tests below, we use '//root/' as our root directory, since it is
1053 // a legal *absolute* path on Windows as well as *nix.
1054 class VFSFromYAMLTest
: public ::testing::Test
{
1058 void SetUp() override
{ NumDiagnostics
= 0; }
1060 static void CountingDiagHandler(const SMDiagnostic
&, void *Context
) {
1061 VFSFromYAMLTest
*Test
= static_cast<VFSFromYAMLTest
*>(Context
);
1062 ++Test
->NumDiagnostics
;
1065 IntrusiveRefCntPtr
<vfs::FileSystem
>
1066 getFromYAMLRawString(StringRef Content
,
1067 IntrusiveRefCntPtr
<vfs::FileSystem
> ExternalFS
) {
1068 std::unique_ptr
<MemoryBuffer
> Buffer
= MemoryBuffer::getMemBuffer(Content
);
1069 return getVFSFromYAML(std::move(Buffer
), CountingDiagHandler
, "", this,
1073 IntrusiveRefCntPtr
<vfs::FileSystem
> getFromYAMLString(
1075 IntrusiveRefCntPtr
<vfs::FileSystem
> ExternalFS
= new DummyFileSystem()) {
1076 std::string
VersionPlusContent("{\n 'version':0,\n");
1077 VersionPlusContent
+= Content
.slice(Content
.find('{') + 1, StringRef::npos
);
1078 return getFromYAMLRawString(VersionPlusContent
, ExternalFS
);
1081 // This is intended as a "XFAIL" for windows hosts.
1082 bool supportsSameDirMultipleYAMLEntries() {
1083 Triple
Host(Triple::normalize(sys::getProcessTriple()));
1084 return !Host
.isOSWindows();
1088 TEST_F(VFSFromYAMLTest
, BasicVFSFromYAML
) {
1089 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
;
1090 FS
= getFromYAMLString("");
1091 EXPECT_EQ(nullptr, FS
.get());
1092 FS
= getFromYAMLString("[]");
1093 EXPECT_EQ(nullptr, FS
.get());
1094 FS
= getFromYAMLString("'string'");
1095 EXPECT_EQ(nullptr, FS
.get());
1096 EXPECT_EQ(3, NumDiagnostics
);
1099 TEST_F(VFSFromYAMLTest
, MappedFiles
) {
1100 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
1101 Lower
->addRegularFile("//root/foo/bar/a");
1102 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= getFromYAMLString(
1105 " 'type': 'directory',\n"
1106 " 'name': '//root/',\n"
1107 " 'contents': [ {\n"
1108 " 'type': 'file',\n"
1109 " 'name': 'file1',\n"
1110 " 'external-contents': '//root/foo/bar/a'\n"
1113 " 'type': 'file',\n"
1114 " 'name': 'file2',\n"
1115 " 'external-contents': '//root/foo/b'\n"
1122 ASSERT_TRUE(FS
.get() != nullptr);
1124 IntrusiveRefCntPtr
<vfs::OverlayFileSystem
> O(
1125 new vfs::OverlayFileSystem(Lower
));
1129 ErrorOr
<vfs::Status
> S
= O
->status("//root/file1");
1130 ASSERT_FALSE(S
.getError());
1131 EXPECT_EQ("//root/foo/bar/a", S
->getName());
1132 EXPECT_TRUE(S
->IsVFSMapped
);
1134 ErrorOr
<vfs::Status
> SLower
= O
->status("//root/foo/bar/a");
1135 EXPECT_EQ("//root/foo/bar/a", SLower
->getName());
1136 EXPECT_TRUE(S
->equivalent(*SLower
));
1137 EXPECT_FALSE(SLower
->IsVFSMapped
);
1139 // file after opening
1140 auto OpenedF
= O
->openFileForRead("//root/file1");
1141 ASSERT_FALSE(OpenedF
.getError());
1142 auto OpenedS
= (*OpenedF
)->status();
1143 ASSERT_FALSE(OpenedS
.getError());
1144 EXPECT_EQ("//root/foo/bar/a", OpenedS
->getName());
1145 EXPECT_TRUE(OpenedS
->IsVFSMapped
);
1148 S
= O
->status("//root/");
1149 ASSERT_FALSE(S
.getError());
1150 EXPECT_TRUE(S
->isDirectory());
1151 EXPECT_TRUE(S
->equivalent(*O
->status("//root/"))); // non-volatile UniqueID
1154 EXPECT_EQ(O
->status("//root/file2").getError(),
1155 llvm::errc::no_such_file_or_directory
);
1156 EXPECT_EQ(0, NumDiagnostics
);
1159 TEST_F(VFSFromYAMLTest
, CaseInsensitive
) {
1160 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
1161 Lower
->addRegularFile("//root/foo/bar/a");
1162 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= getFromYAMLString(
1163 "{ 'case-sensitive': 'false',\n"
1166 " 'type': 'directory',\n"
1167 " 'name': '//root/',\n"
1168 " 'contents': [ {\n"
1169 " 'type': 'file',\n"
1171 " 'external-contents': '//root/foo/bar/a'\n"
1176 ASSERT_TRUE(FS
.get() != nullptr);
1178 IntrusiveRefCntPtr
<vfs::OverlayFileSystem
> O(
1179 new vfs::OverlayFileSystem(Lower
));
1182 ErrorOr
<vfs::Status
> S
= O
->status("//root/XX");
1183 ASSERT_FALSE(S
.getError());
1185 ErrorOr
<vfs::Status
> SS
= O
->status("//root/xx");
1186 ASSERT_FALSE(SS
.getError());
1187 EXPECT_TRUE(S
->equivalent(*SS
));
1188 SS
= O
->status("//root/xX");
1189 EXPECT_TRUE(S
->equivalent(*SS
));
1190 SS
= O
->status("//root/Xx");
1191 EXPECT_TRUE(S
->equivalent(*SS
));
1192 EXPECT_EQ(0, NumDiagnostics
);
1195 TEST_F(VFSFromYAMLTest
, CaseSensitive
) {
1196 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
1197 Lower
->addRegularFile("//root/foo/bar/a");
1198 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= getFromYAMLString(
1199 "{ 'case-sensitive': 'true',\n"
1202 " 'type': 'directory',\n"
1203 " 'name': '//root/',\n"
1204 " 'contents': [ {\n"
1205 " 'type': 'file',\n"
1207 " 'external-contents': '//root/foo/bar/a'\n"
1212 ASSERT_TRUE(FS
.get() != nullptr);
1214 IntrusiveRefCntPtr
<vfs::OverlayFileSystem
> O(
1215 new vfs::OverlayFileSystem(Lower
));
1218 ErrorOr
<vfs::Status
> SS
= O
->status("//root/xx");
1219 EXPECT_EQ(SS
.getError(), llvm::errc::no_such_file_or_directory
);
1220 SS
= O
->status("//root/xX");
1221 EXPECT_EQ(SS
.getError(), llvm::errc::no_such_file_or_directory
);
1222 SS
= O
->status("//root/Xx");
1223 EXPECT_EQ(SS
.getError(), llvm::errc::no_such_file_or_directory
);
1224 EXPECT_EQ(0, NumDiagnostics
);
1227 TEST_F(VFSFromYAMLTest
, IllegalVFSFile
) {
1228 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
1230 // invalid YAML at top-level
1231 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= getFromYAMLString("{]", Lower
);
1232 EXPECT_EQ(nullptr, FS
.get());
1233 // invalid YAML in roots
1234 FS
= getFromYAMLString("{ 'roots':[}", Lower
);
1235 // invalid YAML in directory
1236 FS
= getFromYAMLString(
1237 "{ 'roots':[ { 'name': 'foo', 'type': 'directory', 'contents': [}",
1239 EXPECT_EQ(nullptr, FS
.get());
1241 // invalid configuration
1242 FS
= getFromYAMLString("{ 'knobular': 'true', 'roots':[] }", Lower
);
1243 EXPECT_EQ(nullptr, FS
.get());
1244 FS
= getFromYAMLString("{ 'case-sensitive': 'maybe', 'roots':[] }", Lower
);
1245 EXPECT_EQ(nullptr, FS
.get());
1248 FS
= getFromYAMLString("{ 'roots':'' }", Lower
);
1249 EXPECT_EQ(nullptr, FS
.get());
1250 FS
= getFromYAMLString("{ 'roots':{} }", Lower
);
1251 EXPECT_EQ(nullptr, FS
.get());
1254 FS
= getFromYAMLString(
1255 "{ 'roots':[ { 'type': 'other', 'name': 'me', 'contents': '' }", Lower
);
1256 EXPECT_EQ(nullptr, FS
.get());
1257 FS
= getFromYAMLString("{ 'roots':[ { 'type': 'file', 'name': [], "
1258 "'external-contents': 'other' }",
1260 EXPECT_EQ(nullptr, FS
.get());
1261 FS
= getFromYAMLString(
1262 "{ 'roots':[ { 'type': 'file', 'name': 'me', 'external-contents': [] }",
1264 EXPECT_EQ(nullptr, FS
.get());
1265 FS
= getFromYAMLString(
1266 "{ 'roots':[ { 'type': 'file', 'name': 'me', 'external-contents': {} }",
1268 EXPECT_EQ(nullptr, FS
.get());
1269 FS
= getFromYAMLString(
1270 "{ 'roots':[ { 'type': 'directory', 'name': 'me', 'contents': {} }",
1272 EXPECT_EQ(nullptr, FS
.get());
1273 FS
= getFromYAMLString(
1274 "{ 'roots':[ { 'type': 'directory', 'name': 'me', 'contents': '' }",
1276 EXPECT_EQ(nullptr, FS
.get());
1277 FS
= getFromYAMLString(
1278 "{ 'roots':[ { 'thingy': 'directory', 'name': 'me', 'contents': [] }",
1280 EXPECT_EQ(nullptr, FS
.get());
1282 // missing mandatory fields
1283 FS
= getFromYAMLString("{ 'roots':[ { 'type': 'file', 'name': 'me' }", Lower
);
1284 EXPECT_EQ(nullptr, FS
.get());
1285 FS
= getFromYAMLString(
1286 "{ 'roots':[ { 'type': 'file', 'external-contents': 'other' }", Lower
);
1287 EXPECT_EQ(nullptr, FS
.get());
1288 FS
= getFromYAMLString("{ 'roots':[ { 'name': 'me', 'contents': [] }", Lower
);
1289 EXPECT_EQ(nullptr, FS
.get());
1292 FS
= getFromYAMLString("{ 'roots':[], 'roots':[] }", Lower
);
1293 EXPECT_EQ(nullptr, FS
.get());
1294 FS
= getFromYAMLString(
1295 "{ 'case-sensitive':'true', 'case-sensitive':'true', 'roots':[] }",
1297 EXPECT_EQ(nullptr, FS
.get());
1299 getFromYAMLString("{ 'roots':[{'name':'me', 'name':'you', 'type':'file', "
1300 "'external-contents':'blah' } ] }",
1302 EXPECT_EQ(nullptr, FS
.get());
1305 FS
= getFromYAMLRawString("{ 'roots':[] }", Lower
);
1306 EXPECT_EQ(nullptr, FS
.get());
1308 // bad version number
1309 FS
= getFromYAMLRawString("{ 'version':'foo', 'roots':[] }", Lower
);
1310 EXPECT_EQ(nullptr, FS
.get());
1311 FS
= getFromYAMLRawString("{ 'version':-1, 'roots':[] }", Lower
);
1312 EXPECT_EQ(nullptr, FS
.get());
1313 FS
= getFromYAMLRawString("{ 'version':100000, 'roots':[] }", Lower
);
1314 EXPECT_EQ(nullptr, FS
.get());
1315 EXPECT_EQ(24, NumDiagnostics
);
1318 TEST_F(VFSFromYAMLTest
, UseExternalName
) {
1319 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
1320 Lower
->addRegularFile("//root/external/file");
1322 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
=
1323 getFromYAMLString("{ 'roots': [\n"
1324 " { 'type': 'file', 'name': '//root/A',\n"
1325 " 'external-contents': '//root/external/file'\n"
1327 " { 'type': 'file', 'name': '//root/B',\n"
1328 " 'use-external-name': true,\n"
1329 " 'external-contents': '//root/external/file'\n"
1331 " { 'type': 'file', 'name': '//root/C',\n"
1332 " 'use-external-name': false,\n"
1333 " 'external-contents': '//root/external/file'\n"
1337 ASSERT_TRUE(nullptr != FS
.get());
1340 EXPECT_EQ("//root/external/file", FS
->status("//root/A")->getName());
1342 EXPECT_EQ("//root/external/file", FS
->status("//root/B")->getName());
1343 EXPECT_EQ("//root/C", FS
->status("//root/C")->getName());
1345 // global configuration
1346 FS
= getFromYAMLString("{ 'use-external-names': false,\n"
1348 " { 'type': 'file', 'name': '//root/A',\n"
1349 " 'external-contents': '//root/external/file'\n"
1351 " { 'type': 'file', 'name': '//root/B',\n"
1352 " 'use-external-name': true,\n"
1353 " 'external-contents': '//root/external/file'\n"
1355 " { 'type': 'file', 'name': '//root/C',\n"
1356 " 'use-external-name': false,\n"
1357 " 'external-contents': '//root/external/file'\n"
1361 ASSERT_TRUE(nullptr != FS
.get());
1364 EXPECT_EQ("//root/A", FS
->status("//root/A")->getName());
1366 EXPECT_EQ("//root/external/file", FS
->status("//root/B")->getName());
1367 EXPECT_EQ("//root/C", FS
->status("//root/C")->getName());
1370 TEST_F(VFSFromYAMLTest
, MultiComponentPath
) {
1371 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
1372 Lower
->addRegularFile("//root/other");
1375 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
=
1376 getFromYAMLString("{ 'roots': [\n"
1377 " { 'type': 'file', 'name': '//root/path/to/file',\n"
1378 " 'external-contents': '//root/other' }]\n"
1381 ASSERT_TRUE(nullptr != FS
.get());
1382 EXPECT_FALSE(FS
->status("//root/path/to/file").getError());
1383 EXPECT_FALSE(FS
->status("//root/path/to").getError());
1384 EXPECT_FALSE(FS
->status("//root/path").getError());
1385 EXPECT_FALSE(FS
->status("//root/").getError());
1388 FS
= getFromYAMLString(
1390 " { 'type': 'directory', 'name': '//root/path/to',\n"
1391 " 'contents': [ { 'type': 'file', 'name': 'file',\n"
1392 " 'external-contents': '//root/other' }]}]\n"
1395 ASSERT_TRUE(nullptr != FS
.get());
1396 EXPECT_FALSE(FS
->status("//root/path/to/file").getError());
1397 EXPECT_FALSE(FS
->status("//root/path/to").getError());
1398 EXPECT_FALSE(FS
->status("//root/path").getError());
1399 EXPECT_FALSE(FS
->status("//root/").getError());
1402 FS
= getFromYAMLString(
1404 " { 'type': 'directory', 'name': '//root/',\n"
1405 " 'contents': [ { 'type': 'file', 'name': 'path/to/file',\n"
1406 " 'external-contents': '//root/other' }]}]\n"
1409 ASSERT_TRUE(nullptr != FS
.get());
1410 EXPECT_FALSE(FS
->status("//root/path/to/file").getError());
1411 EXPECT_FALSE(FS
->status("//root/path/to").getError());
1412 EXPECT_FALSE(FS
->status("//root/path").getError());
1413 EXPECT_FALSE(FS
->status("//root/").getError());
1416 TEST_F(VFSFromYAMLTest
, TrailingSlashes
) {
1417 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
1418 Lower
->addRegularFile("//root/other");
1421 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= getFromYAMLString(
1423 " { 'type': 'directory', 'name': '//root/path/to////',\n"
1424 " 'contents': [ { 'type': 'file', 'name': 'file',\n"
1425 " 'external-contents': '//root/other' }]}]\n"
1428 ASSERT_TRUE(nullptr != FS
.get());
1429 EXPECT_FALSE(FS
->status("//root/path/to/file").getError());
1430 EXPECT_FALSE(FS
->status("//root/path/to").getError());
1431 EXPECT_FALSE(FS
->status("//root/path").getError());
1432 EXPECT_FALSE(FS
->status("//root/").getError());
1435 TEST_F(VFSFromYAMLTest
, DirectoryIteration
) {
1436 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
1437 Lower
->addDirectory("//root/");
1438 Lower
->addDirectory("//root/foo");
1439 Lower
->addDirectory("//root/foo/bar");
1440 Lower
->addRegularFile("//root/foo/bar/a");
1441 Lower
->addRegularFile("//root/foo/bar/b");
1442 Lower
->addRegularFile("//root/file3");
1443 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= getFromYAMLString(
1444 "{ 'use-external-names': false,\n"
1447 " 'type': 'directory',\n"
1448 " 'name': '//root/',\n"
1449 " 'contents': [ {\n"
1450 " 'type': 'file',\n"
1451 " 'name': 'file1',\n"
1452 " 'external-contents': '//root/foo/bar/a'\n"
1455 " 'type': 'file',\n"
1456 " 'name': 'file2',\n"
1457 " 'external-contents': '//root/foo/bar/b'\n"
1464 ASSERT_TRUE(FS
.get() != nullptr);
1466 IntrusiveRefCntPtr
<vfs::OverlayFileSystem
> O(
1467 new vfs::OverlayFileSystem(Lower
));
1471 checkContents(O
->dir_begin("//root/", EC
),
1472 {"//root/file1", "//root/file2", "//root/file3", "//root/foo"});
1474 checkContents(O
->dir_begin("//root/foo/bar", EC
),
1475 {"//root/foo/bar/a", "//root/foo/bar/b"});
1478 TEST_F(VFSFromYAMLTest
, DirectoryIterationSameDirMultipleEntries
) {
1479 // https://llvm.org/bugs/show_bug.cgi?id=27725
1480 if (!supportsSameDirMultipleYAMLEntries())
1483 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
1484 Lower
->addDirectory("//root/zab");
1485 Lower
->addDirectory("//root/baz");
1486 Lower
->addRegularFile("//root/zab/a");
1487 Lower
->addRegularFile("//root/zab/b");
1488 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= getFromYAMLString(
1489 "{ 'use-external-names': false,\n"
1492 " 'type': 'directory',\n"
1493 " 'name': '//root/baz/',\n"
1494 " 'contents': [ {\n"
1495 " 'type': 'file',\n"
1497 " 'external-contents': '//root/zab/a'\n"
1502 " 'type': 'directory',\n"
1503 " 'name': '//root/baz/',\n"
1504 " 'contents': [ {\n"
1505 " 'type': 'file',\n"
1507 " 'external-contents': '//root/zab/b'\n"
1514 ASSERT_TRUE(FS
.get() != nullptr);
1516 IntrusiveRefCntPtr
<vfs::OverlayFileSystem
> O(
1517 new vfs::OverlayFileSystem(Lower
));
1522 checkContents(O
->dir_begin("//root/baz/", EC
),
1523 {"//root/baz/x", "//root/baz/y"});
1526 TEST_F(VFSFromYAMLTest
, RecursiveDirectoryIterationLevel
) {
1528 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
1529 Lower
->addDirectory("//root/a");
1530 Lower
->addDirectory("//root/a/b");
1531 Lower
->addDirectory("//root/a/b/c");
1532 Lower
->addRegularFile("//root/a/b/c/file");
1533 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= getFromYAMLString(
1534 "{ 'use-external-names': false,\n"
1537 " 'type': 'directory',\n"
1538 " 'name': '//root/a/b/c/',\n"
1539 " 'contents': [ {\n"
1540 " 'type': 'file',\n"
1541 " 'name': 'file',\n"
1542 " 'external-contents': '//root/a/b/c/file'\n"
1549 ASSERT_TRUE(FS
.get() != nullptr);
1551 IntrusiveRefCntPtr
<vfs::OverlayFileSystem
> O(
1552 new vfs::OverlayFileSystem(Lower
));
1557 // Test recursive_directory_iterator level()
1558 vfs::recursive_directory_iterator I
= vfs::recursive_directory_iterator(
1562 for (int l
= 0; I
!= E
; I
.increment(EC
), ++l
) {
1564 EXPECT_EQ(I
.level(), l
);
1569 TEST_F(VFSFromYAMLTest
, RelativePaths
) {
1570 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
1571 // Filename at root level without a parent directory.
1572 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= getFromYAMLString(
1574 " { 'type': 'file', 'name': 'file-not-in-directory.h',\n"
1575 " 'external-contents': '//root/external/file'\n"
1579 EXPECT_EQ(nullptr, FS
.get());
1581 // Relative file path.
1582 FS
= getFromYAMLString("{ 'roots': [\n"
1583 " { 'type': 'file', 'name': 'relative/file/path.h',\n"
1584 " 'external-contents': '//root/external/file'\n"
1588 EXPECT_EQ(nullptr, FS
.get());
1590 // Relative directory path.
1591 FS
= getFromYAMLString(
1593 " { 'type': 'directory', 'name': 'relative/directory/path.h',\n"
1598 EXPECT_EQ(nullptr, FS
.get());
1600 EXPECT_EQ(3, NumDiagnostics
);