1 //===- unittests/Support/VirtualFileSystem.cpp -------------- VFS tests ---===//
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 "llvm/Support/VirtualFileSystem.h"
10 #include "llvm/ADT/Triple.h"
11 #include "llvm/Config/llvm-config.h"
12 #include "llvm/Support/Errc.h"
13 #include "llvm/Support/Host.h"
14 #include "llvm/Support/MemoryBuffer.h"
15 #include "llvm/Support/Path.h"
16 #include "llvm/Support/SourceMgr.h"
17 #include "gmock/gmock.h"
18 #include "gtest/gtest.h"
23 using llvm::sys::fs::UniqueID
;
24 using testing::ElementsAre
;
26 using testing::UnorderedElementsAre
;
29 struct DummyFile
: public vfs::File
{
31 explicit DummyFile(vfs::Status S
) : S(S
) {}
32 llvm::ErrorOr
<vfs::Status
> status() override
{ return S
; }
33 llvm::ErrorOr
<std::unique_ptr
<llvm::MemoryBuffer
>>
34 getBuffer(const Twine
&Name
, int64_t FileSize
, bool RequiresNullTerminator
,
35 bool IsVolatile
) override
{
36 llvm_unreachable("unimplemented");
38 std::error_code
close() override
{ return std::error_code(); }
41 class DummyFileSystem
: public vfs::FileSystem
{
42 int FSID
; // used to produce UniqueIDs
43 int FileID
; // used to produce UniqueIDs
44 std::map
<std::string
, vfs::Status
> FilesAndDirs
;
46 static int getNextFSID() {
52 DummyFileSystem() : FSID(getNextFSID()), FileID(0) {}
54 ErrorOr
<vfs::Status
> status(const Twine
&Path
) override
{
55 std::map
<std::string
, vfs::Status
>::iterator I
=
56 FilesAndDirs
.find(Path
.str());
57 if (I
== FilesAndDirs
.end())
58 return make_error_code(llvm::errc::no_such_file_or_directory
);
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
{
72 return std::error_code();
74 // Map any symlink to "/symlink".
75 std::error_code
getRealPath(const Twine
&Path
,
76 SmallVectorImpl
<char> &Output
) const override
{
77 auto I
= FilesAndDirs
.find(Path
.str());
78 if (I
== FilesAndDirs
.end())
79 return make_error_code(llvm::errc::no_such_file_or_directory
);
80 if (I
->second
.isSymlink()) {
82 Twine("/symlink").toVector(Output
);
83 return std::error_code();
86 Path
.toVector(Output
);
87 return std::error_code();
90 struct DirIterImpl
: public llvm::vfs::detail::DirIterImpl
{
91 std::map
<std::string
, vfs::Status
> &FilesAndDirs
;
92 std::map
<std::string
, vfs::Status
>::iterator I
;
94 bool isInPath(StringRef S
) {
95 if (Path
.size() < S
.size() && S
.find(Path
) == 0) {
96 auto LastSep
= S
.find_last_of('/');
97 if (LastSep
== Path
.size() || LastSep
== Path
.size() - 1)
102 DirIterImpl(std::map
<std::string
, vfs::Status
> &FilesAndDirs
,
104 : FilesAndDirs(FilesAndDirs
), I(FilesAndDirs
.begin()),
106 for (; I
!= FilesAndDirs
.end(); ++I
) {
107 if (isInPath(I
->first
)) {
109 vfs::directory_entry(I
->second
.getName(), I
->second
.getType());
114 std::error_code
increment() override
{
116 for (; I
!= FilesAndDirs
.end(); ++I
) {
117 if (isInPath(I
->first
)) {
119 vfs::directory_entry(I
->second
.getName(), I
->second
.getType());
123 if (I
== FilesAndDirs
.end())
124 CurrentEntry
= vfs::directory_entry();
125 return std::error_code();
129 vfs::directory_iterator
dir_begin(const Twine
&Dir
,
130 std::error_code
&EC
) override
{
131 return vfs::directory_iterator(
132 std::make_shared
<DirIterImpl
>(FilesAndDirs
, Dir
));
135 void addEntry(StringRef Path
, const vfs::Status
&Status
) {
136 FilesAndDirs
[Path
] = Status
;
139 void addRegularFile(StringRef Path
, sys::fs::perms Perms
= sys::fs::all_all
) {
140 vfs::Status
S(Path
, UniqueID(FSID
, FileID
++),
141 std::chrono::system_clock::now(), 0, 0, 1024,
142 sys::fs::file_type::regular_file
, Perms
);
146 void addDirectory(StringRef Path
, sys::fs::perms Perms
= sys::fs::all_all
) {
147 vfs::Status
S(Path
, UniqueID(FSID
, FileID
++),
148 std::chrono::system_clock::now(), 0, 0, 0,
149 sys::fs::file_type::directory_file
, Perms
);
153 void addSymlink(StringRef Path
) {
154 vfs::Status
S(Path
, UniqueID(FSID
, FileID
++),
155 std::chrono::system_clock::now(), 0, 0, 0,
156 sys::fs::file_type::symlink_file
, sys::fs::all_all
);
161 /// Replace back-slashes by front-slashes.
162 std::string
getPosixPath(std::string S
) {
163 SmallString
<128> Result
;
164 llvm::sys::path::native(S
, Result
, llvm::sys::path::Style::posix
);
167 } // end anonymous namespace
169 TEST(VirtualFileSystemTest
, StatusQueries
) {
170 IntrusiveRefCntPtr
<DummyFileSystem
> D(new DummyFileSystem());
171 ErrorOr
<vfs::Status
> Status((std::error_code()));
173 D
->addRegularFile("/foo");
174 Status
= D
->status("/foo");
175 ASSERT_FALSE(Status
.getError());
176 EXPECT_TRUE(Status
->isStatusKnown());
177 EXPECT_FALSE(Status
->isDirectory());
178 EXPECT_TRUE(Status
->isRegularFile());
179 EXPECT_FALSE(Status
->isSymlink());
180 EXPECT_FALSE(Status
->isOther());
181 EXPECT_TRUE(Status
->exists());
183 D
->addDirectory("/bar");
184 Status
= D
->status("/bar");
185 ASSERT_FALSE(Status
.getError());
186 EXPECT_TRUE(Status
->isStatusKnown());
187 EXPECT_TRUE(Status
->isDirectory());
188 EXPECT_FALSE(Status
->isRegularFile());
189 EXPECT_FALSE(Status
->isSymlink());
190 EXPECT_FALSE(Status
->isOther());
191 EXPECT_TRUE(Status
->exists());
193 D
->addSymlink("/baz");
194 Status
= D
->status("/baz");
195 ASSERT_FALSE(Status
.getError());
196 EXPECT_TRUE(Status
->isStatusKnown());
197 EXPECT_FALSE(Status
->isDirectory());
198 EXPECT_FALSE(Status
->isRegularFile());
199 EXPECT_TRUE(Status
->isSymlink());
200 EXPECT_FALSE(Status
->isOther());
201 EXPECT_TRUE(Status
->exists());
203 EXPECT_TRUE(Status
->equivalent(*Status
));
204 ErrorOr
<vfs::Status
> Status2
= D
->status("/foo");
205 ASSERT_FALSE(Status2
.getError());
206 EXPECT_FALSE(Status
->equivalent(*Status2
));
209 TEST(VirtualFileSystemTest
, BaseOnlyOverlay
) {
210 IntrusiveRefCntPtr
<DummyFileSystem
> D(new DummyFileSystem());
211 ErrorOr
<vfs::Status
> Status((std::error_code()));
212 EXPECT_FALSE(Status
= D
->status("/foo"));
214 IntrusiveRefCntPtr
<vfs::OverlayFileSystem
> O(new vfs::OverlayFileSystem(D
));
215 EXPECT_FALSE(Status
= O
->status("/foo"));
217 D
->addRegularFile("/foo");
218 Status
= D
->status("/foo");
219 EXPECT_FALSE(Status
.getError());
221 ErrorOr
<vfs::Status
> Status2((std::error_code()));
222 Status2
= O
->status("/foo");
223 EXPECT_FALSE(Status2
.getError());
224 EXPECT_TRUE(Status
->equivalent(*Status2
));
227 TEST(VirtualFileSystemTest
, GetRealPathInOverlay
) {
228 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
229 Lower
->addRegularFile("/foo");
230 Lower
->addSymlink("/lower_link");
231 IntrusiveRefCntPtr
<DummyFileSystem
> Upper(new DummyFileSystem());
233 IntrusiveRefCntPtr
<vfs::OverlayFileSystem
> O(
234 new vfs::OverlayFileSystem(Lower
));
235 O
->pushOverlay(Upper
);
238 SmallString
<16> RealPath
;
239 EXPECT_FALSE(O
->getRealPath("/foo", RealPath
));
240 EXPECT_EQ(RealPath
.str(), "/foo");
242 // Expect no error getting real path for symlink in lower overlay.
243 EXPECT_FALSE(O
->getRealPath("/lower_link", RealPath
));
244 EXPECT_EQ(RealPath
.str(), "/symlink");
246 // Try a non-existing link.
247 EXPECT_EQ(O
->getRealPath("/upper_link", RealPath
),
248 errc::no_such_file_or_directory
);
250 // Add a new symlink in upper.
251 Upper
->addSymlink("/upper_link");
252 EXPECT_FALSE(O
->getRealPath("/upper_link", RealPath
));
253 EXPECT_EQ(RealPath
.str(), "/symlink");
256 TEST(VirtualFileSystemTest
, OverlayFiles
) {
257 IntrusiveRefCntPtr
<DummyFileSystem
> Base(new DummyFileSystem());
258 IntrusiveRefCntPtr
<DummyFileSystem
> Middle(new DummyFileSystem());
259 IntrusiveRefCntPtr
<DummyFileSystem
> Top(new DummyFileSystem());
260 IntrusiveRefCntPtr
<vfs::OverlayFileSystem
> O(
261 new vfs::OverlayFileSystem(Base
));
262 O
->pushOverlay(Middle
);
265 ErrorOr
<vfs::Status
> Status1((std::error_code())),
266 Status2((std::error_code())), Status3((std::error_code())),
267 StatusB((std::error_code())), StatusM((std::error_code())),
268 StatusT((std::error_code()));
270 Base
->addRegularFile("/foo");
271 StatusB
= Base
->status("/foo");
272 ASSERT_FALSE(StatusB
.getError());
273 Status1
= O
->status("/foo");
274 ASSERT_FALSE(Status1
.getError());
275 Middle
->addRegularFile("/foo");
276 StatusM
= Middle
->status("/foo");
277 ASSERT_FALSE(StatusM
.getError());
278 Status2
= O
->status("/foo");
279 ASSERT_FALSE(Status2
.getError());
280 Top
->addRegularFile("/foo");
281 StatusT
= Top
->status("/foo");
282 ASSERT_FALSE(StatusT
.getError());
283 Status3
= O
->status("/foo");
284 ASSERT_FALSE(Status3
.getError());
286 EXPECT_TRUE(Status1
->equivalent(*StatusB
));
287 EXPECT_TRUE(Status2
->equivalent(*StatusM
));
288 EXPECT_TRUE(Status3
->equivalent(*StatusT
));
290 EXPECT_FALSE(Status1
->equivalent(*Status2
));
291 EXPECT_FALSE(Status2
->equivalent(*Status3
));
292 EXPECT_FALSE(Status1
->equivalent(*Status3
));
295 TEST(VirtualFileSystemTest
, OverlayDirsNonMerged
) {
296 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
297 IntrusiveRefCntPtr
<DummyFileSystem
> Upper(new DummyFileSystem());
298 IntrusiveRefCntPtr
<vfs::OverlayFileSystem
> O(
299 new vfs::OverlayFileSystem(Lower
));
300 O
->pushOverlay(Upper
);
302 Lower
->addDirectory("/lower-only");
303 Upper
->addDirectory("/upper-only");
305 // non-merged paths should be the same
306 ErrorOr
<vfs::Status
> Status1
= Lower
->status("/lower-only");
307 ASSERT_FALSE(Status1
.getError());
308 ErrorOr
<vfs::Status
> Status2
= O
->status("/lower-only");
309 ASSERT_FALSE(Status2
.getError());
310 EXPECT_TRUE(Status1
->equivalent(*Status2
));
312 Status1
= Upper
->status("/upper-only");
313 ASSERT_FALSE(Status1
.getError());
314 Status2
= O
->status("/upper-only");
315 ASSERT_FALSE(Status2
.getError());
316 EXPECT_TRUE(Status1
->equivalent(*Status2
));
319 TEST(VirtualFileSystemTest
, MergedDirPermissions
) {
320 // merged directories get the permissions of the upper dir
321 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
322 IntrusiveRefCntPtr
<DummyFileSystem
> Upper(new DummyFileSystem());
323 IntrusiveRefCntPtr
<vfs::OverlayFileSystem
> O(
324 new vfs::OverlayFileSystem(Lower
));
325 O
->pushOverlay(Upper
);
327 ErrorOr
<vfs::Status
> Status((std::error_code()));
328 Lower
->addDirectory("/both", sys::fs::owner_read
);
329 Upper
->addDirectory("/both", sys::fs::owner_all
| sys::fs::group_read
);
330 Status
= O
->status("/both");
331 ASSERT_FALSE(Status
.getError());
332 EXPECT_EQ(0740, Status
->getPermissions());
334 // permissions (as usual) are not recursively applied
335 Lower
->addRegularFile("/both/foo", sys::fs::owner_read
);
336 Upper
->addRegularFile("/both/bar", sys::fs::owner_write
);
337 Status
= O
->status("/both/foo");
338 ASSERT_FALSE(Status
.getError());
339 EXPECT_EQ(0400, Status
->getPermissions());
340 Status
= O
->status("/both/bar");
341 ASSERT_FALSE(Status
.getError());
342 EXPECT_EQ(0200, Status
->getPermissions());
345 TEST(VirtualFileSystemTest
, OverlayIterator
) {
346 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
347 Lower
->addRegularFile("/foo");
348 IntrusiveRefCntPtr
<DummyFileSystem
> Upper(new DummyFileSystem());
350 IntrusiveRefCntPtr
<vfs::OverlayFileSystem
> O(
351 new vfs::OverlayFileSystem(Lower
));
352 O
->pushOverlay(Upper
);
354 ErrorOr
<vfs::Status
> Status((std::error_code()));
356 auto it
= O
->overlays_begin();
357 auto end
= O
->overlays_end();
361 Status
= (*it
)->status("/foo");
362 ASSERT_TRUE(Status
.getError());
367 Status
= (*it
)->status("/foo");
368 ASSERT_FALSE(Status
.getError());
369 EXPECT_TRUE(Status
->exists());
376 auto it
= O
->overlays_rbegin();
377 auto end
= O
->overlays_rend();
381 Status
= (*it
)->status("/foo");
382 ASSERT_FALSE(Status
.getError());
383 EXPECT_TRUE(Status
->exists());
388 Status
= (*it
)->status("/foo");
389 ASSERT_TRUE(Status
.getError());
398 SmallString
<128> Path
;
399 ScopedDir(const Twine
&Name
, bool Unique
= false) {
402 EC
= llvm::sys::fs::createUniqueDirectory(Name
, Path
);
404 // Resolve any symlinks in the new directory.
405 std::string UnresolvedPath
= Path
.str();
406 EC
= llvm::sys::fs::real_path(UnresolvedPath
, Path
);
410 EC
= llvm::sys::fs::create_directory(Twine(Path
));
414 EXPECT_FALSE(EC
) << EC
.message();
418 EXPECT_FALSE(llvm::sys::fs::remove(Path
.str()));
421 operator StringRef() { return Path
.str(); }
425 SmallString
<128> Path
;
426 ScopedLink(const Twine
&To
, const Twine
&From
) {
428 std::error_code EC
= sys::fs::create_link(To
, From
);
435 EXPECT_FALSE(llvm::sys::fs::remove(Path
.str()));
438 operator StringRef() { return Path
.str(); }
442 SmallString
<128> Path
;
443 ScopedFile(const Twine
&Path
, StringRef Contents
) {
444 Path
.toVector(this->Path
);
446 raw_fd_ostream
OS(this->Path
, EC
);
450 EXPECT_FALSE(OS
.error());
451 if (EC
|| OS
.error())
456 EXPECT_FALSE(llvm::sys::fs::remove(Path
.str()));
460 } // end anonymous namespace
462 TEST(VirtualFileSystemTest
, BasicRealFSIteration
) {
463 ScopedDir
TestDirectory("virtual-file-system-test", /*Unique*/ true);
464 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= vfs::getRealFileSystem();
467 vfs::directory_iterator I
= FS
->dir_begin(Twine(TestDirectory
), EC
);
469 EXPECT_EQ(vfs::directory_iterator(), I
); // empty directory is empty
471 ScopedDir
_a(TestDirectory
+ "/a");
472 ScopedDir
_ab(TestDirectory
+ "/a/b");
473 ScopedDir
_c(TestDirectory
+ "/c");
474 ScopedDir
_cd(TestDirectory
+ "/c/d");
476 I
= FS
->dir_begin(Twine(TestDirectory
), EC
);
478 ASSERT_NE(vfs::directory_iterator(), I
);
479 // Check either a or c, since we can't rely on the iteration order.
480 EXPECT_TRUE(I
->path().endswith("a") || I
->path().endswith("c"));
483 ASSERT_NE(vfs::directory_iterator(), I
);
484 EXPECT_TRUE(I
->path().endswith("a") || I
->path().endswith("c"));
486 EXPECT_EQ(vfs::directory_iterator(), I
);
490 TEST(VirtualFileSystemTest
, MultipleWorkingDirs
) {
491 // Our root contains a/aa, b/bb, c, where c is a link to a/.
492 // Run tests both in root/b/ and root/c/ (to test "normal" and symlink dirs).
493 // Interleave operations to show the working directories are independent.
494 ScopedDir
Root("r", true), ADir(Root
.Path
+ "/a"), BDir(Root
.Path
+ "/b");
495 ScopedLink
C(ADir
.Path
, Root
.Path
+ "/c");
496 ScopedFile
AA(ADir
.Path
+ "/aa", "aaaa"), BB(BDir
.Path
+ "/bb", "bbbb");
497 std::unique_ptr
<vfs::FileSystem
> BFS
= vfs::createPhysicalFileSystem(),
498 CFS
= vfs::createPhysicalFileSystem();
500 ASSERT_FALSE(BFS
->setCurrentWorkingDirectory(BDir
.Path
));
501 ASSERT_FALSE(CFS
->setCurrentWorkingDirectory(C
.Path
));
502 EXPECT_EQ(BDir
.Path
, *BFS
->getCurrentWorkingDirectory());
503 EXPECT_EQ(C
.Path
, *CFS
->getCurrentWorkingDirectory());
505 // openFileForRead(), indirectly.
506 auto BBuf
= BFS
->getBufferForFile("bb");
508 EXPECT_EQ("bbbb", (*BBuf
)->getBuffer());
510 auto ABuf
= CFS
->getBufferForFile("aa");
512 EXPECT_EQ("aaaa", (*ABuf
)->getBuffer());
515 auto BStat
= BFS
->status("bb");
517 EXPECT_EQ("bb", BStat
->getName());
519 auto AStat
= CFS
->status("aa");
521 EXPECT_EQ("aa", AStat
->getName()); // unresolved name
524 SmallString
<128> BPath
;
525 ASSERT_FALSE(BFS
->getRealPath("bb", BPath
));
526 EXPECT_EQ(BB
.Path
, BPath
);
528 SmallString
<128> APath
;
529 ASSERT_FALSE(CFS
->getRealPath("aa", APath
));
530 EXPECT_EQ(AA
.Path
, APath
); // Reports resolved name.
534 auto BIt
= BFS
->dir_begin(".", EC
);
536 ASSERT_NE(BIt
, vfs::directory_iterator());
537 EXPECT_EQ((BDir
.Path
+ "/./bb").str(), BIt
->path());
540 ASSERT_EQ(BIt
, vfs::directory_iterator());
542 auto CIt
= CFS
->dir_begin(".", EC
);
544 ASSERT_NE(CIt
, vfs::directory_iterator());
545 EXPECT_EQ((ADir
.Path
+ "/./aa").str(), CIt
->path()); // Partly resolved name!
546 CIt
.increment(EC
); // Because likely to read through this path.
548 ASSERT_EQ(CIt
, vfs::directory_iterator());
551 TEST(VirtualFileSystemTest
, BrokenSymlinkRealFSIteration
) {
552 ScopedDir
TestDirectory("virtual-file-system-test", /*Unique*/ true);
553 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= vfs::getRealFileSystem();
555 ScopedLink
_a("no_such_file", TestDirectory
+ "/a");
556 ScopedDir
_b(TestDirectory
+ "/b");
557 ScopedLink
_c("no_such_file", TestDirectory
+ "/c");
559 // Should get no iteration error, but a stat error for the broken symlinks.
560 std::map
<std::string
, std::error_code
> StatResults
;
562 for (vfs::directory_iterator I
= FS
->dir_begin(Twine(TestDirectory
), EC
), E
;
563 I
!= E
; I
.increment(EC
)) {
565 StatResults
[sys::path::filename(I
->path())] =
566 FS
->status(I
->path()).getError();
571 Pair("a", std::make_error_code(std::errc::no_such_file_or_directory
)),
572 Pair("b", std::error_code()),
574 std::make_error_code(std::errc::no_such_file_or_directory
))));
578 TEST(VirtualFileSystemTest
, BasicRealFSRecursiveIteration
) {
579 ScopedDir
TestDirectory("virtual-file-system-test", /*Unique*/ true);
580 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= vfs::getRealFileSystem();
583 auto I
= vfs::recursive_directory_iterator(*FS
, Twine(TestDirectory
), EC
);
585 EXPECT_EQ(vfs::recursive_directory_iterator(), I
); // empty directory is empty
587 ScopedDir
_a(TestDirectory
+ "/a");
588 ScopedDir
_ab(TestDirectory
+ "/a/b");
589 ScopedDir
_c(TestDirectory
+ "/c");
590 ScopedDir
_cd(TestDirectory
+ "/c/d");
592 I
= vfs::recursive_directory_iterator(*FS
, Twine(TestDirectory
), EC
);
594 ASSERT_NE(vfs::recursive_directory_iterator(), I
);
596 std::vector
<std::string
> Contents
;
597 for (auto E
= vfs::recursive_directory_iterator(); !EC
&& I
!= E
;
599 Contents
.push_back(I
->path());
602 // Check contents, which may be in any order
603 EXPECT_EQ(4U, Contents
.size());
604 int Counts
[4] = {0, 0, 0, 0};
605 for (const std::string
&Name
: Contents
) {
606 ASSERT_FALSE(Name
.empty());
607 int Index
= Name
[Name
.size() - 1] - 'a';
608 ASSERT_TRUE(Index
>= 0 && Index
< 4);
611 EXPECT_EQ(1, Counts
[0]); // a
612 EXPECT_EQ(1, Counts
[1]); // b
613 EXPECT_EQ(1, Counts
[2]); // c
614 EXPECT_EQ(1, Counts
[3]); // d
617 TEST(VirtualFileSystemTest
, BasicRealFSRecursiveIterationNoPush
) {
618 ScopedDir
TestDirectory("virtual-file-system-test", /*Unique*/ true);
620 ScopedDir
_a(TestDirectory
+ "/a");
621 ScopedDir
_ab(TestDirectory
+ "/a/b");
622 ScopedDir
_c(TestDirectory
+ "/c");
623 ScopedDir
_cd(TestDirectory
+ "/c/d");
624 ScopedDir
_e(TestDirectory
+ "/e");
625 ScopedDir
_ef(TestDirectory
+ "/e/f");
626 ScopedDir
_g(TestDirectory
+ "/g");
628 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= vfs::getRealFileSystem();
630 // Test that calling no_push on entries without subdirectories has no effect.
633 auto I
= vfs::recursive_directory_iterator(*FS
, Twine(TestDirectory
), EC
);
636 std::vector
<std::string
> Contents
;
637 for (auto E
= vfs::recursive_directory_iterator(); !EC
&& I
!= E
;
639 Contents
.push_back(I
->path());
640 char last
= I
->path().back();
652 EXPECT_EQ(7U, Contents
.size());
655 // Test that calling no_push skips subdirectories.
658 auto I
= vfs::recursive_directory_iterator(*FS
, Twine(TestDirectory
), EC
);
661 std::vector
<std::string
> Contents
;
662 for (auto E
= vfs::recursive_directory_iterator(); !EC
&& I
!= E
;
664 Contents
.push_back(I
->path());
665 char last
= I
->path().back();
677 // Check contents, which may be in any order
678 EXPECT_EQ(4U, Contents
.size());
679 int Counts
[7] = {0, 0, 0, 0, 0, 0, 0};
680 for (const std::string
&Name
: Contents
) {
681 ASSERT_FALSE(Name
.empty());
682 int Index
= Name
[Name
.size() - 1] - 'a';
683 ASSERT_TRUE(Index
>= 0 && Index
< 7);
686 EXPECT_EQ(1, Counts
[0]); // a
687 EXPECT_EQ(0, Counts
[1]); // b
688 EXPECT_EQ(1, Counts
[2]); // c
689 EXPECT_EQ(0, Counts
[3]); // d
690 EXPECT_EQ(1, Counts
[4]); // e
691 EXPECT_EQ(0, Counts
[5]); // f
692 EXPECT_EQ(1, Counts
[6]); // g
697 TEST(VirtualFileSystemTest
, BrokenSymlinkRealFSRecursiveIteration
) {
698 ScopedDir
TestDirectory("virtual-file-system-test", /*Unique*/ true);
699 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= vfs::getRealFileSystem();
701 ScopedLink
_a("no_such_file", TestDirectory
+ "/a");
702 ScopedDir
_b(TestDirectory
+ "/b");
703 ScopedLink
_ba("no_such_file", TestDirectory
+ "/b/a");
704 ScopedDir
_bb(TestDirectory
+ "/b/b");
705 ScopedLink
_bc("no_such_file", TestDirectory
+ "/b/c");
706 ScopedLink
_c("no_such_file", TestDirectory
+ "/c");
707 ScopedDir
_d(TestDirectory
+ "/d");
708 ScopedDir
_dd(TestDirectory
+ "/d/d");
709 ScopedDir
_ddd(TestDirectory
+ "/d/d/d");
710 ScopedLink
_e("no_such_file", TestDirectory
+ "/e");
712 std::vector
<std::string
> VisitedBrokenSymlinks
;
713 std::vector
<std::string
> VisitedNonBrokenSymlinks
;
715 for (vfs::recursive_directory_iterator
I(*FS
, Twine(TestDirectory
), EC
), E
;
716 I
!= E
; I
.increment(EC
)) {
718 (FS
->status(I
->path()) ? VisitedNonBrokenSymlinks
: VisitedBrokenSymlinks
)
719 .push_back(I
->path());
722 // Check visited file names.
723 EXPECT_THAT(VisitedBrokenSymlinks
,
724 UnorderedElementsAre(StringRef(_a
), StringRef(_ba
),
725 StringRef(_bc
), StringRef(_c
),
727 EXPECT_THAT(VisitedNonBrokenSymlinks
,
728 UnorderedElementsAre(StringRef(_b
), StringRef(_bb
), StringRef(_d
),
729 StringRef(_dd
), StringRef(_ddd
)));
733 template <typename DirIter
>
734 static void checkContents(DirIter I
, ArrayRef
<StringRef
> ExpectedOut
) {
736 SmallVector
<StringRef
, 4> Expected(ExpectedOut
.begin(), ExpectedOut
.end());
737 SmallVector
<std::string
, 4> InputToCheck
;
739 // Do not rely on iteration order to check for contents, sort both
740 // content vectors before comparison.
741 for (DirIter E
; !EC
&& I
!= E
; I
.increment(EC
))
742 InputToCheck
.push_back(I
->path());
744 llvm::sort(InputToCheck
);
745 llvm::sort(Expected
);
746 EXPECT_EQ(InputToCheck
.size(), Expected
.size());
748 unsigned LastElt
= std::min(InputToCheck
.size(), Expected
.size());
749 for (unsigned Idx
= 0; Idx
!= LastElt
; ++Idx
)
750 EXPECT_EQ(StringRef(InputToCheck
[Idx
]), Expected
[Idx
]);
753 TEST(VirtualFileSystemTest
, OverlayIteration
) {
754 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
755 IntrusiveRefCntPtr
<DummyFileSystem
> Upper(new DummyFileSystem());
756 IntrusiveRefCntPtr
<vfs::OverlayFileSystem
> O(
757 new vfs::OverlayFileSystem(Lower
));
758 O
->pushOverlay(Upper
);
761 checkContents(O
->dir_begin("/", EC
), ArrayRef
<StringRef
>());
763 Lower
->addRegularFile("/file1");
764 checkContents(O
->dir_begin("/", EC
), ArrayRef
<StringRef
>("/file1"));
766 Upper
->addRegularFile("/file2");
767 checkContents(O
->dir_begin("/", EC
), {"/file2", "/file1"});
769 Lower
->addDirectory("/dir1");
770 Lower
->addRegularFile("/dir1/foo");
771 Upper
->addDirectory("/dir2");
772 Upper
->addRegularFile("/dir2/foo");
773 checkContents(O
->dir_begin("/dir2", EC
), ArrayRef
<StringRef
>("/dir2/foo"));
774 checkContents(O
->dir_begin("/", EC
), {"/dir2", "/file2", "/dir1", "/file1"});
777 TEST(VirtualFileSystemTest
, OverlayRecursiveIteration
) {
778 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
779 IntrusiveRefCntPtr
<DummyFileSystem
> Middle(new DummyFileSystem());
780 IntrusiveRefCntPtr
<DummyFileSystem
> Upper(new DummyFileSystem());
781 IntrusiveRefCntPtr
<vfs::OverlayFileSystem
> O(
782 new vfs::OverlayFileSystem(Lower
));
783 O
->pushOverlay(Middle
);
784 O
->pushOverlay(Upper
);
787 checkContents(vfs::recursive_directory_iterator(*O
, "/", EC
),
788 ArrayRef
<StringRef
>());
790 Lower
->addRegularFile("/file1");
791 checkContents(vfs::recursive_directory_iterator(*O
, "/", EC
),
792 ArrayRef
<StringRef
>("/file1"));
794 Upper
->addDirectory("/dir");
795 Upper
->addRegularFile("/dir/file2");
796 checkContents(vfs::recursive_directory_iterator(*O
, "/", EC
),
797 {"/dir", "/dir/file2", "/file1"});
799 Lower
->addDirectory("/dir1");
800 Lower
->addRegularFile("/dir1/foo");
801 Lower
->addDirectory("/dir1/a");
802 Lower
->addRegularFile("/dir1/a/b");
803 Middle
->addDirectory("/a");
804 Middle
->addDirectory("/a/b");
805 Middle
->addDirectory("/a/b/c");
806 Middle
->addRegularFile("/a/b/c/d");
807 Middle
->addRegularFile("/hiddenByUp");
808 Upper
->addDirectory("/dir2");
809 Upper
->addRegularFile("/dir2/foo");
810 Upper
->addRegularFile("/hiddenByUp");
811 checkContents(vfs::recursive_directory_iterator(*O
, "/dir2", EC
),
812 ArrayRef
<StringRef
>("/dir2/foo"));
813 checkContents(vfs::recursive_directory_iterator(*O
, "/", EC
),
814 {"/dir", "/dir/file2", "/dir2", "/dir2/foo", "/hiddenByUp",
815 "/a", "/a/b", "/a/b/c", "/a/b/c/d", "/dir1", "/dir1/a",
816 "/dir1/a/b", "/dir1/foo", "/file1"});
819 TEST(VirtualFileSystemTest
, ThreeLevelIteration
) {
820 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
821 IntrusiveRefCntPtr
<DummyFileSystem
> Middle(new DummyFileSystem());
822 IntrusiveRefCntPtr
<DummyFileSystem
> Upper(new DummyFileSystem());
823 IntrusiveRefCntPtr
<vfs::OverlayFileSystem
> O(
824 new vfs::OverlayFileSystem(Lower
));
825 O
->pushOverlay(Middle
);
826 O
->pushOverlay(Upper
);
829 checkContents(O
->dir_begin("/", EC
), ArrayRef
<StringRef
>());
831 Middle
->addRegularFile("/file2");
832 checkContents(O
->dir_begin("/", EC
), ArrayRef
<StringRef
>("/file2"));
834 Lower
->addRegularFile("/file1");
835 Upper
->addRegularFile("/file3");
836 checkContents(O
->dir_begin("/", EC
), {"/file3", "/file2", "/file1"});
839 TEST(VirtualFileSystemTest
, HiddenInIteration
) {
840 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
841 IntrusiveRefCntPtr
<DummyFileSystem
> Middle(new DummyFileSystem());
842 IntrusiveRefCntPtr
<DummyFileSystem
> Upper(new DummyFileSystem());
843 IntrusiveRefCntPtr
<vfs::OverlayFileSystem
> O(
844 new vfs::OverlayFileSystem(Lower
));
845 O
->pushOverlay(Middle
);
846 O
->pushOverlay(Upper
);
849 Lower
->addRegularFile("/onlyInLow");
850 Lower
->addDirectory("/hiddenByMid");
851 Lower
->addDirectory("/hiddenByUp");
852 Middle
->addRegularFile("/onlyInMid");
853 Middle
->addRegularFile("/hiddenByMid");
854 Middle
->addDirectory("/hiddenByUp");
855 Upper
->addRegularFile("/onlyInUp");
856 Upper
->addRegularFile("/hiddenByUp");
858 O
->dir_begin("/", EC
),
859 {"/hiddenByUp", "/onlyInUp", "/hiddenByMid", "/onlyInMid", "/onlyInLow"});
861 // Make sure we get the top-most entry
864 vfs::directory_iterator I
= O
->dir_begin("/", EC
), E
;
865 for (; !EC
&& I
!= E
; I
.increment(EC
))
866 if (I
->path() == "/hiddenByUp")
869 EXPECT_EQ(sys::fs::file_type::regular_file
, I
->type());
873 vfs::directory_iterator I
= O
->dir_begin("/", EC
), E
;
874 for (; !EC
&& I
!= E
; I
.increment(EC
))
875 if (I
->path() == "/hiddenByMid")
878 EXPECT_EQ(sys::fs::file_type::regular_file
, I
->type());
882 TEST(ProxyFileSystemTest
, Basic
) {
883 IntrusiveRefCntPtr
<vfs::InMemoryFileSystem
> Base(
884 new vfs::InMemoryFileSystem());
885 vfs::ProxyFileSystem
PFS(Base
);
887 Base
->addFile("/a", 0, MemoryBuffer::getMemBuffer("test"));
889 auto Stat
= PFS
.status("/a");
890 ASSERT_FALSE(Stat
.getError());
892 auto File
= PFS
.openFileForRead("/a");
893 ASSERT_FALSE(File
.getError());
894 EXPECT_EQ("test", (*(*File
)->getBuffer("ignored"))->getBuffer());
897 vfs::directory_iterator I
= PFS
.dir_begin("/", EC
);
899 ASSERT_EQ("/a", I
->path());
902 ASSERT_EQ(vfs::directory_iterator(), I
);
904 ASSERT_FALSE(PFS
.setCurrentWorkingDirectory("/"));
906 auto PWD
= PFS
.getCurrentWorkingDirectory();
907 ASSERT_FALSE(PWD
.getError());
908 ASSERT_EQ("/", *PWD
);
910 SmallString
<16> Path
;
911 ASSERT_FALSE(PFS
.getRealPath("a", Path
));
912 ASSERT_EQ("/a", Path
);
915 ASSERT_FALSE(PFS
.isLocal("/a", Local
));
919 class InMemoryFileSystemTest
: public ::testing::Test
{
921 llvm::vfs::InMemoryFileSystem FS
;
922 llvm::vfs::InMemoryFileSystem NormalizedFS
;
924 InMemoryFileSystemTest()
925 : FS(/*UseNormalizedPaths=*/false),
926 NormalizedFS(/*UseNormalizedPaths=*/true) {}
929 MATCHER_P2(IsHardLinkTo
, FS
, Target
, "") {
930 StringRef From
= arg
;
931 StringRef To
= Target
;
932 auto OpenedFrom
= FS
->openFileForRead(From
);
933 auto OpenedTo
= FS
->openFileForRead(To
);
934 return !OpenedFrom
.getError() && !OpenedTo
.getError() &&
935 (*OpenedFrom
)->status()->getUniqueID() ==
936 (*OpenedTo
)->status()->getUniqueID();
939 TEST_F(InMemoryFileSystemTest
, IsEmpty
) {
940 auto Stat
= FS
.status("/a");
941 ASSERT_EQ(Stat
.getError(), errc::no_such_file_or_directory
) << FS
.toString();
942 Stat
= FS
.status("/");
943 ASSERT_EQ(Stat
.getError(), errc::no_such_file_or_directory
) << FS
.toString();
946 TEST_F(InMemoryFileSystemTest
, WindowsPath
) {
947 FS
.addFile("c:/windows/system128/foo.cpp", 0, MemoryBuffer::getMemBuffer(""));
948 auto Stat
= FS
.status("c:");
950 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << FS
.toString();
952 Stat
= FS
.status("c:/windows/system128/foo.cpp");
953 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << FS
.toString();
954 FS
.addFile("d:/windows/foo.cpp", 0, MemoryBuffer::getMemBuffer(""));
955 Stat
= FS
.status("d:/windows/foo.cpp");
956 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << FS
.toString();
959 TEST_F(InMemoryFileSystemTest
, OverlayFile
) {
960 FS
.addFile("/a", 0, MemoryBuffer::getMemBuffer("a"));
961 NormalizedFS
.addFile("/a", 0, MemoryBuffer::getMemBuffer("a"));
962 auto Stat
= FS
.status("/");
963 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << FS
.toString();
964 Stat
= FS
.status("/.");
966 Stat
= NormalizedFS
.status("/.");
967 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << FS
.toString();
968 Stat
= FS
.status("/a");
969 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n" << FS
.toString();
970 ASSERT_EQ("/a", Stat
->getName());
973 TEST_F(InMemoryFileSystemTest
, OverlayFileNoOwn
) {
974 auto Buf
= MemoryBuffer::getMemBuffer("a");
975 FS
.addFileNoOwn("/a", 0, Buf
.get());
976 auto Stat
= FS
.status("/a");
977 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n" << FS
.toString();
978 ASSERT_EQ("/a", Stat
->getName());
981 TEST_F(InMemoryFileSystemTest
, OpenFileForRead
) {
982 FS
.addFile("/a", 0, MemoryBuffer::getMemBuffer("a"));
983 FS
.addFile("././c", 0, MemoryBuffer::getMemBuffer("c"));
984 FS
.addFile("./d/../d", 0, MemoryBuffer::getMemBuffer("d"));
985 NormalizedFS
.addFile("/a", 0, MemoryBuffer::getMemBuffer("a"));
986 NormalizedFS
.addFile("././c", 0, MemoryBuffer::getMemBuffer("c"));
987 NormalizedFS
.addFile("./d/../d", 0, MemoryBuffer::getMemBuffer("d"));
988 auto File
= FS
.openFileForRead("/a");
989 ASSERT_EQ("a", (*(*File
)->getBuffer("ignored"))->getBuffer());
990 File
= FS
.openFileForRead("/a"); // Open again.
991 ASSERT_EQ("a", (*(*File
)->getBuffer("ignored"))->getBuffer());
992 File
= NormalizedFS
.openFileForRead("/././a"); // Open again.
993 ASSERT_EQ("a", (*(*File
)->getBuffer("ignored"))->getBuffer());
994 File
= FS
.openFileForRead("/");
995 ASSERT_EQ(File
.getError(), errc::invalid_argument
) << FS
.toString();
996 File
= FS
.openFileForRead("/b");
997 ASSERT_EQ(File
.getError(), errc::no_such_file_or_directory
) << FS
.toString();
998 File
= FS
.openFileForRead("./c");
1000 File
= FS
.openFileForRead("e/../d");
1002 File
= NormalizedFS
.openFileForRead("./c");
1003 ASSERT_EQ("c", (*(*File
)->getBuffer("ignored"))->getBuffer());
1004 File
= NormalizedFS
.openFileForRead("e/../d");
1005 ASSERT_EQ("d", (*(*File
)->getBuffer("ignored"))->getBuffer());
1008 TEST_F(InMemoryFileSystemTest
, DuplicatedFile
) {
1009 ASSERT_TRUE(FS
.addFile("/a", 0, MemoryBuffer::getMemBuffer("a")));
1010 ASSERT_FALSE(FS
.addFile("/a/b", 0, MemoryBuffer::getMemBuffer("a")));
1011 ASSERT_TRUE(FS
.addFile("/a", 0, MemoryBuffer::getMemBuffer("a")));
1012 ASSERT_FALSE(FS
.addFile("/a", 0, MemoryBuffer::getMemBuffer("b")));
1015 TEST_F(InMemoryFileSystemTest
, DirectoryIteration
) {
1016 FS
.addFile("/a", 0, MemoryBuffer::getMemBuffer(""));
1017 FS
.addFile("/b/c", 0, MemoryBuffer::getMemBuffer(""));
1020 vfs::directory_iterator I
= FS
.dir_begin("/", EC
);
1022 ASSERT_EQ("/a", I
->path());
1025 ASSERT_EQ("/b", I
->path());
1028 ASSERT_EQ(vfs::directory_iterator(), I
);
1030 I
= FS
.dir_begin("/b", EC
);
1032 // When on Windows, we end up with "/b\\c" as the name. Convert to Posix
1033 // path for the sake of the comparison.
1034 ASSERT_EQ("/b/c", getPosixPath(I
->path()));
1037 ASSERT_EQ(vfs::directory_iterator(), I
);
1040 TEST_F(InMemoryFileSystemTest
, WorkingDirectory
) {
1041 FS
.setCurrentWorkingDirectory("/b");
1042 FS
.addFile("c", 0, MemoryBuffer::getMemBuffer(""));
1044 auto Stat
= FS
.status("/b/c");
1045 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n" << FS
.toString();
1046 ASSERT_EQ("/b/c", Stat
->getName());
1047 ASSERT_EQ("/b", *FS
.getCurrentWorkingDirectory());
1049 Stat
= FS
.status("c");
1050 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n" << FS
.toString();
1052 NormalizedFS
.setCurrentWorkingDirectory("/b/c");
1053 NormalizedFS
.setCurrentWorkingDirectory(".");
1055 getPosixPath(NormalizedFS
.getCurrentWorkingDirectory().get()));
1056 NormalizedFS
.setCurrentWorkingDirectory("..");
1058 getPosixPath(NormalizedFS
.getCurrentWorkingDirectory().get()));
1061 TEST_F(InMemoryFileSystemTest
, IsLocal
) {
1062 FS
.setCurrentWorkingDirectory("/b");
1063 FS
.addFile("c", 0, MemoryBuffer::getMemBuffer(""));
1066 bool IsLocal
= true;
1067 EC
= FS
.isLocal("c", IsLocal
);
1069 ASSERT_FALSE(IsLocal
);
1072 #if !defined(_WIN32)
1073 TEST_F(InMemoryFileSystemTest
, GetRealPath
) {
1074 SmallString
<16> Path
;
1075 EXPECT_EQ(FS
.getRealPath("b", Path
), errc::operation_not_permitted
);
1077 auto GetRealPath
= [this](StringRef P
) {
1078 SmallString
<16> Output
;
1079 auto EC
= FS
.getRealPath(P
, Output
);
1081 return Output
.str().str();
1084 FS
.setCurrentWorkingDirectory("a");
1085 EXPECT_EQ(GetRealPath("b"), "a/b");
1086 EXPECT_EQ(GetRealPath("../b"), "b");
1087 EXPECT_EQ(GetRealPath("b/./c"), "a/b/c");
1089 FS
.setCurrentWorkingDirectory("/a");
1090 EXPECT_EQ(GetRealPath("b"), "/a/b");
1091 EXPECT_EQ(GetRealPath("../b"), "/b");
1092 EXPECT_EQ(GetRealPath("b/./c"), "/a/b/c");
1096 TEST_F(InMemoryFileSystemTest
, AddFileWithUser
) {
1097 FS
.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"), 0xFEEDFACE);
1098 auto Stat
= FS
.status("/a");
1099 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n" << FS
.toString();
1100 ASSERT_TRUE(Stat
->isDirectory());
1101 ASSERT_EQ(0xFEEDFACE, Stat
->getUser());
1102 Stat
= FS
.status("/a/b");
1103 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n" << FS
.toString();
1104 ASSERT_TRUE(Stat
->isDirectory());
1105 ASSERT_EQ(0xFEEDFACE, Stat
->getUser());
1106 Stat
= FS
.status("/a/b/c");
1107 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n" << FS
.toString();
1108 ASSERT_TRUE(Stat
->isRegularFile());
1109 ASSERT_EQ(sys::fs::perms::all_all
, Stat
->getPermissions());
1110 ASSERT_EQ(0xFEEDFACE, Stat
->getUser());
1113 TEST_F(InMemoryFileSystemTest
, AddFileWithGroup
) {
1114 FS
.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"), None
, 0xDABBAD00);
1115 auto Stat
= FS
.status("/a");
1116 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n" << FS
.toString();
1117 ASSERT_TRUE(Stat
->isDirectory());
1118 ASSERT_EQ(0xDABBAD00, Stat
->getGroup());
1119 Stat
= FS
.status("/a/b");
1120 ASSERT_TRUE(Stat
->isDirectory());
1121 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n" << FS
.toString();
1122 ASSERT_EQ(0xDABBAD00, Stat
->getGroup());
1123 Stat
= FS
.status("/a/b/c");
1124 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n" << FS
.toString();
1125 ASSERT_TRUE(Stat
->isRegularFile());
1126 ASSERT_EQ(sys::fs::perms::all_all
, Stat
->getPermissions());
1127 ASSERT_EQ(0xDABBAD00, Stat
->getGroup());
1130 TEST_F(InMemoryFileSystemTest
, AddFileWithFileType
) {
1131 FS
.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"), None
, None
,
1132 sys::fs::file_type::socket_file
);
1133 auto Stat
= FS
.status("/a");
1134 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n" << FS
.toString();
1135 ASSERT_TRUE(Stat
->isDirectory());
1136 Stat
= FS
.status("/a/b");
1137 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n" << FS
.toString();
1138 ASSERT_TRUE(Stat
->isDirectory());
1139 Stat
= FS
.status("/a/b/c");
1140 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n" << FS
.toString();
1141 ASSERT_EQ(sys::fs::file_type::socket_file
, Stat
->getType());
1142 ASSERT_EQ(sys::fs::perms::all_all
, Stat
->getPermissions());
1145 TEST_F(InMemoryFileSystemTest
, AddFileWithPerms
) {
1146 FS
.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"), None
, None
, None
,
1147 sys::fs::perms::owner_read
| sys::fs::perms::owner_write
);
1148 auto Stat
= FS
.status("/a");
1149 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n" << FS
.toString();
1150 ASSERT_TRUE(Stat
->isDirectory());
1151 ASSERT_EQ(sys::fs::perms::owner_read
| sys::fs::perms::owner_write
|
1152 sys::fs::perms::owner_exe
,
1153 Stat
->getPermissions());
1154 Stat
= FS
.status("/a/b");
1155 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n" << FS
.toString();
1156 ASSERT_TRUE(Stat
->isDirectory());
1157 ASSERT_EQ(sys::fs::perms::owner_read
| sys::fs::perms::owner_write
|
1158 sys::fs::perms::owner_exe
,
1159 Stat
->getPermissions());
1160 Stat
= FS
.status("/a/b/c");
1161 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n" << FS
.toString();
1162 ASSERT_TRUE(Stat
->isRegularFile());
1163 ASSERT_EQ(sys::fs::perms::owner_read
| sys::fs::perms::owner_write
,
1164 Stat
->getPermissions());
1167 TEST_F(InMemoryFileSystemTest
, AddDirectoryThenAddChild
) {
1168 FS
.addFile("/a", 0, MemoryBuffer::getMemBuffer(""), /*User=*/None
,
1169 /*Group=*/None
, sys::fs::file_type::directory_file
);
1170 FS
.addFile("/a/b", 0, MemoryBuffer::getMemBuffer("abc"), /*User=*/None
,
1171 /*Group=*/None
, sys::fs::file_type::regular_file
);
1172 auto Stat
= FS
.status("/a");
1173 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n" << FS
.toString();
1174 ASSERT_TRUE(Stat
->isDirectory());
1175 Stat
= FS
.status("/a/b");
1176 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n" << FS
.toString();
1177 ASSERT_TRUE(Stat
->isRegularFile());
1180 // Test that the name returned by status() is in the same form as the path that
1181 // was requested (to match the behavior of RealFileSystem).
1182 TEST_F(InMemoryFileSystemTest
, StatusName
) {
1183 NormalizedFS
.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"),
1185 /*Group=*/None
, sys::fs::file_type::regular_file
);
1186 NormalizedFS
.setCurrentWorkingDirectory("/a/b");
1188 // Access using InMemoryFileSystem::status.
1189 auto Stat
= NormalizedFS
.status("../b/c");
1190 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n"
1191 << NormalizedFS
.toString();
1192 ASSERT_TRUE(Stat
->isRegularFile());
1193 ASSERT_EQ("../b/c", Stat
->getName());
1195 // Access using InMemoryFileAdaptor::status.
1196 auto File
= NormalizedFS
.openFileForRead("../b/c");
1197 ASSERT_FALSE(File
.getError()) << File
.getError() << "\n"
1198 << NormalizedFS
.toString();
1199 Stat
= (*File
)->status();
1200 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n"
1201 << NormalizedFS
.toString();
1202 ASSERT_TRUE(Stat
->isRegularFile());
1203 ASSERT_EQ("../b/c", Stat
->getName());
1205 // Access using a directory iterator.
1207 llvm::vfs::directory_iterator It
= NormalizedFS
.dir_begin("../b", EC
);
1208 // When on Windows, we end up with "../b\\c" as the name. Convert to Posix
1209 // path for the sake of the comparison.
1210 ASSERT_EQ("../b/c", getPosixPath(It
->path()));
1213 TEST_F(InMemoryFileSystemTest
, AddHardLinkToFile
) {
1214 StringRef FromLink
= "/path/to/FROM/link";
1215 StringRef Target
= "/path/to/TO/file";
1216 FS
.addFile(Target
, 0, MemoryBuffer::getMemBuffer("content of target"));
1217 EXPECT_TRUE(FS
.addHardLink(FromLink
, Target
));
1218 EXPECT_THAT(FromLink
, IsHardLinkTo(&FS
, Target
));
1219 EXPECT_TRUE(FS
.status(FromLink
)->getSize() == FS
.status(Target
)->getSize());
1220 EXPECT_TRUE(FS
.getBufferForFile(FromLink
)->get()->getBuffer() ==
1221 FS
.getBufferForFile(Target
)->get()->getBuffer());
1224 TEST_F(InMemoryFileSystemTest
, AddHardLinkInChainPattern
) {
1225 StringRef Link0
= "/path/to/0/link";
1226 StringRef Link1
= "/path/to/1/link";
1227 StringRef Link2
= "/path/to/2/link";
1228 StringRef Target
= "/path/to/target";
1229 FS
.addFile(Target
, 0, MemoryBuffer::getMemBuffer("content of target file"));
1230 EXPECT_TRUE(FS
.addHardLink(Link2
, Target
));
1231 EXPECT_TRUE(FS
.addHardLink(Link1
, Link2
));
1232 EXPECT_TRUE(FS
.addHardLink(Link0
, Link1
));
1233 EXPECT_THAT(Link0
, IsHardLinkTo(&FS
, Target
));
1234 EXPECT_THAT(Link1
, IsHardLinkTo(&FS
, Target
));
1235 EXPECT_THAT(Link2
, IsHardLinkTo(&FS
, Target
));
1238 TEST_F(InMemoryFileSystemTest
, AddHardLinkToAFileThatWasNotAddedBefore
) {
1239 EXPECT_FALSE(FS
.addHardLink("/path/to/link", "/path/to/target"));
1242 TEST_F(InMemoryFileSystemTest
, AddHardLinkFromAFileThatWasAddedBefore
) {
1243 StringRef Link
= "/path/to/link";
1244 StringRef Target
= "/path/to/target";
1245 FS
.addFile(Target
, 0, MemoryBuffer::getMemBuffer("content of target"));
1246 FS
.addFile(Link
, 0, MemoryBuffer::getMemBuffer("content of link"));
1247 EXPECT_FALSE(FS
.addHardLink(Link
, Target
));
1250 TEST_F(InMemoryFileSystemTest
, AddSameHardLinkMoreThanOnce
) {
1251 StringRef Link
= "/path/to/link";
1252 StringRef Target
= "/path/to/target";
1253 FS
.addFile(Target
, 0, MemoryBuffer::getMemBuffer("content of target"));
1254 EXPECT_TRUE(FS
.addHardLink(Link
, Target
));
1255 EXPECT_FALSE(FS
.addHardLink(Link
, Target
));
1258 TEST_F(InMemoryFileSystemTest
, AddFileInPlaceOfAHardLinkWithSameContent
) {
1259 StringRef Link
= "/path/to/link";
1260 StringRef Target
= "/path/to/target";
1261 StringRef Content
= "content of target";
1262 EXPECT_TRUE(FS
.addFile(Target
, 0, MemoryBuffer::getMemBuffer(Content
)));
1263 EXPECT_TRUE(FS
.addHardLink(Link
, Target
));
1264 EXPECT_TRUE(FS
.addFile(Link
, 0, MemoryBuffer::getMemBuffer(Content
)));
1267 TEST_F(InMemoryFileSystemTest
, AddFileInPlaceOfAHardLinkWithDifferentContent
) {
1268 StringRef Link
= "/path/to/link";
1269 StringRef Target
= "/path/to/target";
1270 StringRef Content
= "content of target";
1271 StringRef LinkContent
= "different content of link";
1272 EXPECT_TRUE(FS
.addFile(Target
, 0, MemoryBuffer::getMemBuffer(Content
)));
1273 EXPECT_TRUE(FS
.addHardLink(Link
, Target
));
1274 EXPECT_FALSE(FS
.addFile(Link
, 0, MemoryBuffer::getMemBuffer(LinkContent
)));
1277 TEST_F(InMemoryFileSystemTest
, AddHardLinkToADirectory
) {
1278 StringRef Dir
= "path/to/dummy/dir";
1279 StringRef Link
= "/path/to/link";
1280 StringRef File
= "path/to/dummy/dir/target";
1281 StringRef Content
= "content of target";
1282 EXPECT_TRUE(FS
.addFile(File
, 0, MemoryBuffer::getMemBuffer(Content
)));
1283 EXPECT_FALSE(FS
.addHardLink(Link
, Dir
));
1286 TEST_F(InMemoryFileSystemTest
, AddHardLinkFromADirectory
) {
1287 StringRef Dir
= "path/to/dummy/dir";
1288 StringRef Target
= "path/to/dummy/dir/target";
1289 StringRef Content
= "content of target";
1290 EXPECT_TRUE(FS
.addFile(Target
, 0, MemoryBuffer::getMemBuffer(Content
)));
1291 EXPECT_FALSE(FS
.addHardLink(Dir
, Target
));
1294 TEST_F(InMemoryFileSystemTest
, AddHardLinkUnderAFile
) {
1295 StringRef CommonContent
= "content string";
1296 FS
.addFile("/a/b", 0, MemoryBuffer::getMemBuffer(CommonContent
));
1297 FS
.addFile("/c/d", 0, MemoryBuffer::getMemBuffer(CommonContent
));
1298 EXPECT_FALSE(FS
.addHardLink("/c/d/e", "/a/b"));
1301 TEST_F(InMemoryFileSystemTest
, RecursiveIterationWithHardLink
) {
1303 FS
.addFile("/a/b", 0, MemoryBuffer::getMemBuffer("content string"));
1304 EXPECT_TRUE(FS
.addHardLink("/c/d", "/a/b"));
1305 auto I
= vfs::recursive_directory_iterator(FS
, "/", EC
);
1307 std::vector
<std::string
> Nodes
;
1308 for (auto E
= vfs::recursive_directory_iterator(); !EC
&& I
!= E
;
1310 Nodes
.push_back(getPosixPath(I
->path()));
1312 EXPECT_THAT(Nodes
, testing::UnorderedElementsAre("/a", "/a/b", "/c", "/c/d"));
1315 // NOTE: in the tests below, we use '//root/' as our root directory, since it is
1316 // a legal *absolute* path on Windows as well as *nix.
1317 class VFSFromYAMLTest
: public ::testing::Test
{
1321 void SetUp() override
{ NumDiagnostics
= 0; }
1323 static void CountingDiagHandler(const SMDiagnostic
&, void *Context
) {
1324 VFSFromYAMLTest
*Test
= static_cast<VFSFromYAMLTest
*>(Context
);
1325 ++Test
->NumDiagnostics
;
1328 IntrusiveRefCntPtr
<vfs::FileSystem
>
1329 getFromYAMLRawString(StringRef Content
,
1330 IntrusiveRefCntPtr
<vfs::FileSystem
> ExternalFS
) {
1331 std::unique_ptr
<MemoryBuffer
> Buffer
= MemoryBuffer::getMemBuffer(Content
);
1332 return getVFSFromYAML(std::move(Buffer
), CountingDiagHandler
, "", this,
1336 IntrusiveRefCntPtr
<vfs::FileSystem
> getFromYAMLString(
1338 IntrusiveRefCntPtr
<vfs::FileSystem
> ExternalFS
= new DummyFileSystem()) {
1339 std::string
VersionPlusContent("{\n 'version':0,\n");
1340 VersionPlusContent
+= Content
.slice(Content
.find('{') + 1, StringRef::npos
);
1341 return getFromYAMLRawString(VersionPlusContent
, ExternalFS
);
1344 // This is intended as a "XFAIL" for windows hosts.
1345 bool supportsSameDirMultipleYAMLEntries() {
1346 Triple
Host(Triple::normalize(sys::getProcessTriple()));
1347 return !Host
.isOSWindows();
1351 TEST_F(VFSFromYAMLTest
, BasicVFSFromYAML
) {
1352 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
;
1353 FS
= getFromYAMLString("");
1354 EXPECT_EQ(nullptr, FS
.get());
1355 FS
= getFromYAMLString("[]");
1356 EXPECT_EQ(nullptr, FS
.get());
1357 FS
= getFromYAMLString("'string'");
1358 EXPECT_EQ(nullptr, FS
.get());
1359 EXPECT_EQ(3, NumDiagnostics
);
1362 TEST_F(VFSFromYAMLTest
, MappedFiles
) {
1363 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
1364 Lower
->addRegularFile("//root/foo/bar/a");
1365 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= getFromYAMLString(
1368 " 'type': 'directory',\n"
1369 " 'name': '//root/',\n"
1370 " 'contents': [ {\n"
1371 " 'type': 'file',\n"
1372 " 'name': 'file1',\n"
1373 " 'external-contents': '//root/foo/bar/a'\n"
1376 " 'type': 'file',\n"
1377 " 'name': 'file2',\n"
1378 " 'external-contents': '//root/foo/b'\n"
1385 ASSERT_TRUE(FS
.get() != nullptr);
1387 IntrusiveRefCntPtr
<vfs::OverlayFileSystem
> O(
1388 new vfs::OverlayFileSystem(Lower
));
1392 ErrorOr
<vfs::Status
> S
= O
->status("//root/file1");
1393 ASSERT_FALSE(S
.getError());
1394 EXPECT_EQ("//root/foo/bar/a", S
->getName());
1395 EXPECT_TRUE(S
->IsVFSMapped
);
1397 ErrorOr
<vfs::Status
> SLower
= O
->status("//root/foo/bar/a");
1398 EXPECT_EQ("//root/foo/bar/a", SLower
->getName());
1399 EXPECT_TRUE(S
->equivalent(*SLower
));
1400 EXPECT_FALSE(SLower
->IsVFSMapped
);
1402 // file after opening
1403 auto OpenedF
= O
->openFileForRead("//root/file1");
1404 ASSERT_FALSE(OpenedF
.getError());
1405 auto OpenedS
= (*OpenedF
)->status();
1406 ASSERT_FALSE(OpenedS
.getError());
1407 EXPECT_EQ("//root/foo/bar/a", OpenedS
->getName());
1408 EXPECT_TRUE(OpenedS
->IsVFSMapped
);
1411 S
= O
->status("//root/");
1412 ASSERT_FALSE(S
.getError());
1413 EXPECT_TRUE(S
->isDirectory());
1414 EXPECT_TRUE(S
->equivalent(*O
->status("//root/"))); // non-volatile UniqueID
1417 EXPECT_EQ(O
->status("//root/file2").getError(),
1418 llvm::errc::no_such_file_or_directory
);
1419 EXPECT_EQ(0, NumDiagnostics
);
1422 TEST_F(VFSFromYAMLTest
, CaseInsensitive
) {
1423 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
1424 Lower
->addRegularFile("//root/foo/bar/a");
1425 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= getFromYAMLString(
1426 "{ 'case-sensitive': 'false',\n"
1429 " 'type': 'directory',\n"
1430 " 'name': '//root/',\n"
1431 " 'contents': [ {\n"
1432 " 'type': 'file',\n"
1434 " 'external-contents': '//root/foo/bar/a'\n"
1439 ASSERT_TRUE(FS
.get() != nullptr);
1441 IntrusiveRefCntPtr
<vfs::OverlayFileSystem
> O(
1442 new vfs::OverlayFileSystem(Lower
));
1445 ErrorOr
<vfs::Status
> S
= O
->status("//root/XX");
1446 ASSERT_FALSE(S
.getError());
1448 ErrorOr
<vfs::Status
> SS
= O
->status("//root/xx");
1449 ASSERT_FALSE(SS
.getError());
1450 EXPECT_TRUE(S
->equivalent(*SS
));
1451 SS
= O
->status("//root/xX");
1452 EXPECT_TRUE(S
->equivalent(*SS
));
1453 SS
= O
->status("//root/Xx");
1454 EXPECT_TRUE(S
->equivalent(*SS
));
1455 EXPECT_EQ(0, NumDiagnostics
);
1458 TEST_F(VFSFromYAMLTest
, CaseSensitive
) {
1459 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
1460 Lower
->addRegularFile("//root/foo/bar/a");
1461 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= getFromYAMLString(
1462 "{ 'case-sensitive': 'true',\n"
1465 " 'type': 'directory',\n"
1466 " 'name': '//root/',\n"
1467 " 'contents': [ {\n"
1468 " 'type': 'file',\n"
1470 " 'external-contents': '//root/foo/bar/a'\n"
1475 ASSERT_TRUE(FS
.get() != nullptr);
1477 IntrusiveRefCntPtr
<vfs::OverlayFileSystem
> O(
1478 new vfs::OverlayFileSystem(Lower
));
1481 ErrorOr
<vfs::Status
> SS
= O
->status("//root/xx");
1482 EXPECT_EQ(SS
.getError(), llvm::errc::no_such_file_or_directory
);
1483 SS
= O
->status("//root/xX");
1484 EXPECT_EQ(SS
.getError(), llvm::errc::no_such_file_or_directory
);
1485 SS
= O
->status("//root/Xx");
1486 EXPECT_EQ(SS
.getError(), llvm::errc::no_such_file_or_directory
);
1487 EXPECT_EQ(0, NumDiagnostics
);
1490 TEST_F(VFSFromYAMLTest
, IllegalVFSFile
) {
1491 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
1493 // invalid YAML at top-level
1494 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= getFromYAMLString("{]", Lower
);
1495 EXPECT_EQ(nullptr, FS
.get());
1496 // invalid YAML in roots
1497 FS
= getFromYAMLString("{ 'roots':[}", Lower
);
1498 // invalid YAML in directory
1499 FS
= getFromYAMLString(
1500 "{ 'roots':[ { 'name': 'foo', 'type': 'directory', 'contents': [}",
1502 EXPECT_EQ(nullptr, FS
.get());
1504 // invalid configuration
1505 FS
= getFromYAMLString("{ 'knobular': 'true', 'roots':[] }", Lower
);
1506 EXPECT_EQ(nullptr, FS
.get());
1507 FS
= getFromYAMLString("{ 'case-sensitive': 'maybe', 'roots':[] }", Lower
);
1508 EXPECT_EQ(nullptr, FS
.get());
1511 FS
= getFromYAMLString("{ 'roots':'' }", Lower
);
1512 EXPECT_EQ(nullptr, FS
.get());
1513 FS
= getFromYAMLString("{ 'roots':{} }", Lower
);
1514 EXPECT_EQ(nullptr, FS
.get());
1517 FS
= getFromYAMLString(
1518 "{ 'roots':[ { 'type': 'other', 'name': 'me', 'contents': '' }", Lower
);
1519 EXPECT_EQ(nullptr, FS
.get());
1520 FS
= getFromYAMLString("{ 'roots':[ { 'type': 'file', 'name': [], "
1521 "'external-contents': 'other' }",
1523 EXPECT_EQ(nullptr, FS
.get());
1524 FS
= getFromYAMLString(
1525 "{ 'roots':[ { 'type': 'file', 'name': 'me', 'external-contents': [] }",
1527 EXPECT_EQ(nullptr, FS
.get());
1528 FS
= getFromYAMLString(
1529 "{ 'roots':[ { 'type': 'file', 'name': 'me', 'external-contents': {} }",
1531 EXPECT_EQ(nullptr, FS
.get());
1532 FS
= getFromYAMLString(
1533 "{ 'roots':[ { 'type': 'directory', 'name': 'me', 'contents': {} }",
1535 EXPECT_EQ(nullptr, FS
.get());
1536 FS
= getFromYAMLString(
1537 "{ 'roots':[ { 'type': 'directory', 'name': 'me', 'contents': '' }",
1539 EXPECT_EQ(nullptr, FS
.get());
1540 FS
= getFromYAMLString(
1541 "{ 'roots':[ { 'thingy': 'directory', 'name': 'me', 'contents': [] }",
1543 EXPECT_EQ(nullptr, FS
.get());
1545 // missing mandatory fields
1546 FS
= getFromYAMLString("{ 'roots':[ { 'type': 'file', 'name': 'me' }", Lower
);
1547 EXPECT_EQ(nullptr, FS
.get());
1548 FS
= getFromYAMLString(
1549 "{ 'roots':[ { 'type': 'file', 'external-contents': 'other' }", Lower
);
1550 EXPECT_EQ(nullptr, FS
.get());
1551 FS
= getFromYAMLString("{ 'roots':[ { 'name': 'me', 'contents': [] }", Lower
);
1552 EXPECT_EQ(nullptr, FS
.get());
1555 FS
= getFromYAMLString("{ 'roots':[], 'roots':[] }", Lower
);
1556 EXPECT_EQ(nullptr, FS
.get());
1557 FS
= getFromYAMLString(
1558 "{ 'case-sensitive':'true', 'case-sensitive':'true', 'roots':[] }",
1560 EXPECT_EQ(nullptr, FS
.get());
1562 getFromYAMLString("{ 'roots':[{'name':'me', 'name':'you', 'type':'file', "
1563 "'external-contents':'blah' } ] }",
1565 EXPECT_EQ(nullptr, FS
.get());
1568 FS
= getFromYAMLRawString("{ 'roots':[] }", Lower
);
1569 EXPECT_EQ(nullptr, FS
.get());
1571 // bad version number
1572 FS
= getFromYAMLRawString("{ 'version':'foo', 'roots':[] }", Lower
);
1573 EXPECT_EQ(nullptr, FS
.get());
1574 FS
= getFromYAMLRawString("{ 'version':-1, 'roots':[] }", Lower
);
1575 EXPECT_EQ(nullptr, FS
.get());
1576 FS
= getFromYAMLRawString("{ 'version':100000, 'roots':[] }", Lower
);
1577 EXPECT_EQ(nullptr, FS
.get());
1578 EXPECT_EQ(24, NumDiagnostics
);
1581 TEST_F(VFSFromYAMLTest
, UseExternalName
) {
1582 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
1583 Lower
->addRegularFile("//root/external/file");
1585 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
=
1586 getFromYAMLString("{ 'roots': [\n"
1587 " { 'type': 'file', 'name': '//root/A',\n"
1588 " 'external-contents': '//root/external/file'\n"
1590 " { 'type': 'file', 'name': '//root/B',\n"
1591 " 'use-external-name': true,\n"
1592 " 'external-contents': '//root/external/file'\n"
1594 " { 'type': 'file', 'name': '//root/C',\n"
1595 " 'use-external-name': false,\n"
1596 " 'external-contents': '//root/external/file'\n"
1600 ASSERT_TRUE(nullptr != FS
.get());
1603 EXPECT_EQ("//root/external/file", FS
->status("//root/A")->getName());
1605 EXPECT_EQ("//root/external/file", FS
->status("//root/B")->getName());
1606 EXPECT_EQ("//root/C", FS
->status("//root/C")->getName());
1608 // global configuration
1609 FS
= getFromYAMLString("{ 'use-external-names': false,\n"
1611 " { 'type': 'file', 'name': '//root/A',\n"
1612 " 'external-contents': '//root/external/file'\n"
1614 " { 'type': 'file', 'name': '//root/B',\n"
1615 " 'use-external-name': true,\n"
1616 " 'external-contents': '//root/external/file'\n"
1618 " { 'type': 'file', 'name': '//root/C',\n"
1619 " 'use-external-name': false,\n"
1620 " 'external-contents': '//root/external/file'\n"
1624 ASSERT_TRUE(nullptr != FS
.get());
1627 EXPECT_EQ("//root/A", FS
->status("//root/A")->getName());
1629 EXPECT_EQ("//root/external/file", FS
->status("//root/B")->getName());
1630 EXPECT_EQ("//root/C", FS
->status("//root/C")->getName());
1633 TEST_F(VFSFromYAMLTest
, MultiComponentPath
) {
1634 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
1635 Lower
->addRegularFile("//root/other");
1638 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
=
1639 getFromYAMLString("{ 'roots': [\n"
1640 " { 'type': 'file', 'name': '//root/path/to/file',\n"
1641 " 'external-contents': '//root/other' }]\n"
1644 ASSERT_TRUE(nullptr != FS
.get());
1645 EXPECT_FALSE(FS
->status("//root/path/to/file").getError());
1646 EXPECT_FALSE(FS
->status("//root/path/to").getError());
1647 EXPECT_FALSE(FS
->status("//root/path").getError());
1648 EXPECT_FALSE(FS
->status("//root/").getError());
1651 FS
= getFromYAMLString(
1653 " { 'type': 'directory', 'name': '//root/path/to',\n"
1654 " 'contents': [ { 'type': 'file', 'name': 'file',\n"
1655 " 'external-contents': '//root/other' }]}]\n"
1658 ASSERT_TRUE(nullptr != FS
.get());
1659 EXPECT_FALSE(FS
->status("//root/path/to/file").getError());
1660 EXPECT_FALSE(FS
->status("//root/path/to").getError());
1661 EXPECT_FALSE(FS
->status("//root/path").getError());
1662 EXPECT_FALSE(FS
->status("//root/").getError());
1665 FS
= getFromYAMLString(
1667 " { 'type': 'directory', 'name': '//root/',\n"
1668 " 'contents': [ { 'type': 'file', 'name': 'path/to/file',\n"
1669 " 'external-contents': '//root/other' }]}]\n"
1672 ASSERT_TRUE(nullptr != FS
.get());
1673 EXPECT_FALSE(FS
->status("//root/path/to/file").getError());
1674 EXPECT_FALSE(FS
->status("//root/path/to").getError());
1675 EXPECT_FALSE(FS
->status("//root/path").getError());
1676 EXPECT_FALSE(FS
->status("//root/").getError());
1679 TEST_F(VFSFromYAMLTest
, TrailingSlashes
) {
1680 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
1681 Lower
->addRegularFile("//root/other");
1684 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= getFromYAMLString(
1686 " { 'type': 'directory', 'name': '//root/path/to////',\n"
1687 " 'contents': [ { 'type': 'file', 'name': 'file',\n"
1688 " 'external-contents': '//root/other' }]}]\n"
1691 ASSERT_TRUE(nullptr != FS
.get());
1692 EXPECT_FALSE(FS
->status("//root/path/to/file").getError());
1693 EXPECT_FALSE(FS
->status("//root/path/to").getError());
1694 EXPECT_FALSE(FS
->status("//root/path").getError());
1695 EXPECT_FALSE(FS
->status("//root/").getError());
1698 TEST_F(VFSFromYAMLTest
, DirectoryIteration
) {
1699 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
1700 Lower
->addDirectory("//root/");
1701 Lower
->addDirectory("//root/foo");
1702 Lower
->addDirectory("//root/foo/bar");
1703 Lower
->addRegularFile("//root/foo/bar/a");
1704 Lower
->addRegularFile("//root/foo/bar/b");
1705 Lower
->addRegularFile("//root/file3");
1706 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= getFromYAMLString(
1707 "{ 'use-external-names': false,\n"
1710 " 'type': 'directory',\n"
1711 " 'name': '//root/',\n"
1712 " 'contents': [ {\n"
1713 " 'type': 'file',\n"
1714 " 'name': 'file1',\n"
1715 " 'external-contents': '//root/foo/bar/a'\n"
1718 " 'type': 'file',\n"
1719 " 'name': 'file2',\n"
1720 " 'external-contents': '//root/foo/bar/b'\n"
1727 ASSERT_TRUE(FS
.get() != nullptr);
1729 IntrusiveRefCntPtr
<vfs::OverlayFileSystem
> O(
1730 new vfs::OverlayFileSystem(Lower
));
1734 checkContents(O
->dir_begin("//root/", EC
),
1735 {"//root/file1", "//root/file2", "//root/file3", "//root/foo"});
1737 checkContents(O
->dir_begin("//root/foo/bar", EC
),
1738 {"//root/foo/bar/a", "//root/foo/bar/b"});
1741 TEST_F(VFSFromYAMLTest
, DirectoryIterationSameDirMultipleEntries
) {
1742 // https://llvm.org/bugs/show_bug.cgi?id=27725
1743 if (!supportsSameDirMultipleYAMLEntries())
1746 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
1747 Lower
->addDirectory("//root/zab");
1748 Lower
->addDirectory("//root/baz");
1749 Lower
->addRegularFile("//root/zab/a");
1750 Lower
->addRegularFile("//root/zab/b");
1751 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= getFromYAMLString(
1752 "{ 'use-external-names': false,\n"
1755 " 'type': 'directory',\n"
1756 " 'name': '//root/baz/',\n"
1757 " 'contents': [ {\n"
1758 " 'type': 'file',\n"
1760 " 'external-contents': '//root/zab/a'\n"
1765 " 'type': 'directory',\n"
1766 " 'name': '//root/baz/',\n"
1767 " 'contents': [ {\n"
1768 " 'type': 'file',\n"
1770 " 'external-contents': '//root/zab/b'\n"
1777 ASSERT_TRUE(FS
.get() != nullptr);
1779 IntrusiveRefCntPtr
<vfs::OverlayFileSystem
> O(
1780 new vfs::OverlayFileSystem(Lower
));
1785 checkContents(O
->dir_begin("//root/baz/", EC
),
1786 {"//root/baz/x", "//root/baz/y"});
1789 TEST_F(VFSFromYAMLTest
, RecursiveDirectoryIterationLevel
) {
1791 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
1792 Lower
->addDirectory("//root/a");
1793 Lower
->addDirectory("//root/a/b");
1794 Lower
->addDirectory("//root/a/b/c");
1795 Lower
->addRegularFile("//root/a/b/c/file");
1796 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= getFromYAMLString(
1797 "{ 'use-external-names': false,\n"
1800 " 'type': 'directory',\n"
1801 " 'name': '//root/a/b/c/',\n"
1802 " 'contents': [ {\n"
1803 " 'type': 'file',\n"
1804 " 'name': 'file',\n"
1805 " 'external-contents': '//root/a/b/c/file'\n"
1812 ASSERT_TRUE(FS
.get() != nullptr);
1814 IntrusiveRefCntPtr
<vfs::OverlayFileSystem
> O(
1815 new vfs::OverlayFileSystem(Lower
));
1820 // Test recursive_directory_iterator level()
1821 vfs::recursive_directory_iterator I
= vfs::recursive_directory_iterator(
1825 for (int l
= 0; I
!= E
; I
.increment(EC
), ++l
) {
1827 EXPECT_EQ(I
.level(), l
);
1832 TEST_F(VFSFromYAMLTest
, RelativePaths
) {
1833 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
1834 // Filename at root level without a parent directory.
1835 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= getFromYAMLString(
1837 " { 'type': 'file', 'name': 'file-not-in-directory.h',\n"
1838 " 'external-contents': '//root/external/file'\n"
1842 EXPECT_EQ(nullptr, FS
.get());
1844 // Relative file path.
1845 FS
= getFromYAMLString("{ 'roots': [\n"
1846 " { 'type': 'file', 'name': 'relative/file/path.h',\n"
1847 " 'external-contents': '//root/external/file'\n"
1851 EXPECT_EQ(nullptr, FS
.get());
1853 // Relative directory path.
1854 FS
= getFromYAMLString(
1856 " { 'type': 'directory', 'name': 'relative/directory/path.h',\n"
1861 EXPECT_EQ(nullptr, FS
.get());
1863 EXPECT_EQ(3, NumDiagnostics
);
1866 TEST_F(VFSFromYAMLTest
, NonFallthroughDirectoryIteration
) {
1867 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
1868 Lower
->addDirectory("//root/");
1869 Lower
->addRegularFile("//root/a");
1870 Lower
->addRegularFile("//root/b");
1871 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= getFromYAMLString(
1872 "{ 'use-external-names': false,\n"
1873 " 'fallthrough': false,\n"
1876 " 'type': 'directory',\n"
1877 " 'name': '//root/',\n"
1878 " 'contents': [ {\n"
1879 " 'type': 'file',\n"
1881 " 'external-contents': '//root/a'\n"
1888 ASSERT_TRUE(FS
.get() != nullptr);
1891 checkContents(FS
->dir_begin("//root/", EC
),
1895 TEST_F(VFSFromYAMLTest
, DirectoryIterationWithDuplicates
) {
1896 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
1897 Lower
->addDirectory("//root/");
1898 Lower
->addRegularFile("//root/a");
1899 Lower
->addRegularFile("//root/b");
1900 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= getFromYAMLString(
1901 "{ 'use-external-names': false,\n"
1904 " 'type': 'directory',\n"
1905 " 'name': '//root/',\n"
1906 " 'contents': [ {\n"
1907 " 'type': 'file',\n"
1909 " 'external-contents': '//root/a'\n"
1916 ASSERT_TRUE(FS
.get() != nullptr);
1919 checkContents(FS
->dir_begin("//root/", EC
),
1920 {"//root/a", "//root/b"});
1923 TEST_F(VFSFromYAMLTest
, DirectoryIterationErrorInVFSLayer
) {
1924 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
1925 Lower
->addDirectory("//root/");
1926 Lower
->addDirectory("//root/foo");
1927 Lower
->addRegularFile("//root/foo/a");
1928 Lower
->addRegularFile("//root/foo/b");
1929 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= getFromYAMLString(
1930 "{ 'use-external-names': false,\n"
1933 " 'type': 'directory',\n"
1934 " 'name': '//root/',\n"
1935 " 'contents': [ {\n"
1936 " 'type': 'file',\n"
1937 " 'name': 'bar/a',\n"
1938 " 'external-contents': '//root/foo/a'\n"
1945 ASSERT_TRUE(FS
.get() != nullptr);
1948 checkContents(FS
->dir_begin("//root/foo", EC
),
1949 {"//root/foo/a", "//root/foo/b"});
1952 TEST_F(VFSFromYAMLTest
, GetRealPath
) {
1953 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
1954 Lower
->addDirectory("//dir/");
1955 Lower
->addRegularFile("/foo");
1956 Lower
->addSymlink("/link");
1957 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= getFromYAMLString(
1958 "{ 'use-external-names': false,\n"
1961 " 'type': 'directory',\n"
1962 " 'name': '//root/',\n"
1963 " 'contents': [ {\n"
1964 " 'type': 'file',\n"
1966 " 'external-contents': '/link'\n"
1971 " 'type': 'directory',\n"
1972 " 'name': '//dir/',\n"
1978 ASSERT_TRUE(FS
.get() != nullptr);
1980 // Regular file present in underlying file system.
1981 SmallString
<16> RealPath
;
1982 EXPECT_FALSE(FS
->getRealPath("/foo", RealPath
));
1983 EXPECT_EQ(RealPath
.str(), "/foo");
1985 // File present in YAML pointing to symlink in underlying file system.
1986 EXPECT_FALSE(FS
->getRealPath("//root/bar", RealPath
));
1987 EXPECT_EQ(RealPath
.str(), "/symlink");
1989 // Directories should fall back to the underlying file system is possible.
1990 EXPECT_FALSE(FS
->getRealPath("//dir/", RealPath
));
1991 EXPECT_EQ(RealPath
.str(), "//dir/");
1993 // Try a non-existing file.
1994 EXPECT_EQ(FS
->getRealPath("/non_existing", RealPath
),
1995 errc::no_such_file_or_directory
);