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 "llvm/Testing/Support/SupportHelpers.h"
18 #include "gmock/gmock.h"
19 #include "gtest/gtest.h"
24 using llvm::sys::fs::UniqueID
;
25 using llvm::unittest::TempDir
;
26 using llvm::unittest::TempFile
;
27 using llvm::unittest::TempLink
;
28 using testing::ElementsAre
;
30 using testing::UnorderedElementsAre
;
33 struct DummyFile
: public vfs::File
{
35 explicit DummyFile(vfs::Status S
) : S(S
) {}
36 llvm::ErrorOr
<vfs::Status
> status() override
{ return S
; }
37 llvm::ErrorOr
<std::unique_ptr
<llvm::MemoryBuffer
>>
38 getBuffer(const Twine
&Name
, int64_t FileSize
, bool RequiresNullTerminator
,
39 bool IsVolatile
) override
{
40 llvm_unreachable("unimplemented");
42 std::error_code
close() override
{ return std::error_code(); }
45 class DummyFileSystem
: public vfs::FileSystem
{
46 int FSID
; // used to produce UniqueIDs
47 int FileID
; // used to produce UniqueIDs
48 std::string WorkingDirectory
;
49 std::map
<std::string
, vfs::Status
> FilesAndDirs
;
50 typedef std::map
<std::string
, vfs::Status
>::const_iterator const_iterator
;
52 static int getNextFSID() {
58 DummyFileSystem() : FSID(getNextFSID()), FileID(0) {}
60 ErrorOr
<vfs::Status
> status(const Twine
&Path
) override
{
61 auto I
= findEntry(Path
);
62 if (I
== FilesAndDirs
.end())
63 return make_error_code(llvm::errc::no_such_file_or_directory
);
66 ErrorOr
<std::unique_ptr
<vfs::File
>>
67 openFileForRead(const Twine
&Path
) override
{
68 auto S
= status(Path
);
70 return std::unique_ptr
<vfs::File
>(new DummyFile
{*S
});
73 llvm::ErrorOr
<std::string
> getCurrentWorkingDirectory() const override
{
74 return WorkingDirectory
;
76 std::error_code
setCurrentWorkingDirectory(const Twine
&Path
) override
{
77 WorkingDirectory
= Path
.str();
78 return std::error_code();
80 // Map any symlink to "/symlink".
81 std::error_code
getRealPath(const Twine
&Path
,
82 SmallVectorImpl
<char> &Output
) const override
{
83 auto I
= findEntry(Path
);
84 if (I
== FilesAndDirs
.end())
85 return make_error_code(llvm::errc::no_such_file_or_directory
);
86 if (I
->second
.isSymlink()) {
88 Twine("/symlink").toVector(Output
);
89 return std::error_code();
92 Path
.toVector(Output
);
93 return std::error_code();
96 struct DirIterImpl
: public llvm::vfs::detail::DirIterImpl
{
97 std::map
<std::string
, vfs::Status
> &FilesAndDirs
;
98 std::map
<std::string
, vfs::Status
>::iterator I
;
100 bool isInPath(StringRef S
) {
101 if (Path
.size() < S
.size() && S
.find(Path
) == 0) {
102 auto LastSep
= S
.find_last_of('/');
103 if (LastSep
== Path
.size() || LastSep
== Path
.size() - 1)
108 DirIterImpl(std::map
<std::string
, vfs::Status
> &FilesAndDirs
,
110 : FilesAndDirs(FilesAndDirs
), I(FilesAndDirs
.begin()),
112 for (; I
!= FilesAndDirs
.end(); ++I
) {
113 if (isInPath(I
->first
)) {
114 CurrentEntry
= vfs::directory_entry(std::string(I
->second
.getName()),
115 I
->second
.getType());
120 std::error_code
increment() override
{
122 for (; I
!= FilesAndDirs
.end(); ++I
) {
123 if (isInPath(I
->first
)) {
124 CurrentEntry
= vfs::directory_entry(std::string(I
->second
.getName()),
125 I
->second
.getType());
129 if (I
== FilesAndDirs
.end())
130 CurrentEntry
= vfs::directory_entry();
131 return std::error_code();
135 vfs::directory_iterator
dir_begin(const Twine
&Dir
,
136 std::error_code
&EC
) override
{
137 return vfs::directory_iterator(
138 std::make_shared
<DirIterImpl
>(FilesAndDirs
, Dir
));
141 void addEntry(StringRef Path
, const vfs::Status
&Status
) {
142 FilesAndDirs
[std::string(Path
)] = Status
;
145 const_iterator
findEntry(const Twine
&Path
) const {
148 std::error_code EC
= makeAbsolute(P
);
151 return FilesAndDirs
.find(std::string(P
.str()));
154 void addRegularFile(StringRef Path
, sys::fs::perms Perms
= sys::fs::all_all
) {
155 vfs::Status
S(Path
, UniqueID(FSID
, FileID
++),
156 std::chrono::system_clock::now(), 0, 0, 1024,
157 sys::fs::file_type::regular_file
, Perms
);
161 void addDirectory(StringRef Path
, sys::fs::perms Perms
= sys::fs::all_all
) {
162 vfs::Status
S(Path
, UniqueID(FSID
, FileID
++),
163 std::chrono::system_clock::now(), 0, 0, 0,
164 sys::fs::file_type::directory_file
, Perms
);
168 void addSymlink(StringRef Path
) {
169 vfs::Status
S(Path
, UniqueID(FSID
, FileID
++),
170 std::chrono::system_clock::now(), 0, 0, 0,
171 sys::fs::file_type::symlink_file
, sys::fs::all_all
);
176 void printImpl(raw_ostream
&OS
, PrintType Type
,
177 unsigned IndentLevel
) const override
{
178 printIndent(OS
, IndentLevel
);
179 OS
<< "DummyFileSystem (";
181 case vfs::FileSystem::PrintType::Summary
:
184 case vfs::FileSystem::PrintType::Contents
:
187 case vfs::FileSystem::PrintType::RecursiveContents
:
188 OS
<< "RecursiveContents";
195 class ErrorDummyFileSystem
: public DummyFileSystem
{
196 std::error_code
setCurrentWorkingDirectory(const Twine
&Path
) override
{
197 return llvm::errc::no_such_file_or_directory
;
201 /// Replace back-slashes by front-slashes.
202 std::string
getPosixPath(const Twine
&S
) {
203 SmallString
<128> Result
;
204 llvm::sys::path::native(S
, Result
, llvm::sys::path::Style::posix
);
205 return std::string(Result
.str());
207 } // end anonymous namespace
209 TEST(VirtualFileSystemTest
, StatusQueries
) {
210 IntrusiveRefCntPtr
<DummyFileSystem
> D(new DummyFileSystem());
211 ErrorOr
<vfs::Status
> Status((std::error_code()));
213 D
->addRegularFile("/foo");
214 Status
= D
->status("/foo");
215 ASSERT_FALSE(Status
.getError());
216 EXPECT_TRUE(Status
->isStatusKnown());
217 EXPECT_FALSE(Status
->isDirectory());
218 EXPECT_TRUE(Status
->isRegularFile());
219 EXPECT_FALSE(Status
->isSymlink());
220 EXPECT_FALSE(Status
->isOther());
221 EXPECT_TRUE(Status
->exists());
223 D
->addDirectory("/bar");
224 Status
= D
->status("/bar");
225 ASSERT_FALSE(Status
.getError());
226 EXPECT_TRUE(Status
->isStatusKnown());
227 EXPECT_TRUE(Status
->isDirectory());
228 EXPECT_FALSE(Status
->isRegularFile());
229 EXPECT_FALSE(Status
->isSymlink());
230 EXPECT_FALSE(Status
->isOther());
231 EXPECT_TRUE(Status
->exists());
233 D
->addSymlink("/baz");
234 Status
= D
->status("/baz");
235 ASSERT_FALSE(Status
.getError());
236 EXPECT_TRUE(Status
->isStatusKnown());
237 EXPECT_FALSE(Status
->isDirectory());
238 EXPECT_FALSE(Status
->isRegularFile());
239 EXPECT_TRUE(Status
->isSymlink());
240 EXPECT_FALSE(Status
->isOther());
241 EXPECT_TRUE(Status
->exists());
243 EXPECT_TRUE(Status
->equivalent(*Status
));
244 ErrorOr
<vfs::Status
> Status2
= D
->status("/foo");
245 ASSERT_FALSE(Status2
.getError());
246 EXPECT_FALSE(Status
->equivalent(*Status2
));
249 TEST(VirtualFileSystemTest
, BaseOnlyOverlay
) {
250 IntrusiveRefCntPtr
<DummyFileSystem
> D(new DummyFileSystem());
251 ErrorOr
<vfs::Status
> Status((std::error_code()));
252 EXPECT_FALSE(Status
= D
->status("/foo"));
254 IntrusiveRefCntPtr
<vfs::OverlayFileSystem
> O(new vfs::OverlayFileSystem(D
));
255 EXPECT_FALSE(Status
= O
->status("/foo"));
257 D
->addRegularFile("/foo");
258 Status
= D
->status("/foo");
259 EXPECT_FALSE(Status
.getError());
261 ErrorOr
<vfs::Status
> Status2((std::error_code()));
262 Status2
= O
->status("/foo");
263 EXPECT_FALSE(Status2
.getError());
264 EXPECT_TRUE(Status
->equivalent(*Status2
));
267 TEST(VirtualFileSystemTest
, GetRealPathInOverlay
) {
268 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
269 Lower
->addRegularFile("/foo");
270 Lower
->addSymlink("/lower_link");
271 IntrusiveRefCntPtr
<DummyFileSystem
> Upper(new DummyFileSystem());
273 IntrusiveRefCntPtr
<vfs::OverlayFileSystem
> O(
274 new vfs::OverlayFileSystem(Lower
));
275 O
->pushOverlay(Upper
);
278 SmallString
<16> RealPath
;
279 EXPECT_FALSE(O
->getRealPath("/foo", RealPath
));
280 EXPECT_EQ(RealPath
.str(), "/foo");
282 // Expect no error getting real path for symlink in lower overlay.
283 EXPECT_FALSE(O
->getRealPath("/lower_link", RealPath
));
284 EXPECT_EQ(RealPath
.str(), "/symlink");
286 // Try a non-existing link.
287 EXPECT_EQ(O
->getRealPath("/upper_link", RealPath
),
288 errc::no_such_file_or_directory
);
290 // Add a new symlink in upper.
291 Upper
->addSymlink("/upper_link");
292 EXPECT_FALSE(O
->getRealPath("/upper_link", RealPath
));
293 EXPECT_EQ(RealPath
.str(), "/symlink");
296 TEST(VirtualFileSystemTest
, OverlayFiles
) {
297 IntrusiveRefCntPtr
<DummyFileSystem
> Base(new DummyFileSystem());
298 IntrusiveRefCntPtr
<DummyFileSystem
> Middle(new DummyFileSystem());
299 IntrusiveRefCntPtr
<DummyFileSystem
> Top(new DummyFileSystem());
300 IntrusiveRefCntPtr
<vfs::OverlayFileSystem
> O(
301 new vfs::OverlayFileSystem(Base
));
302 O
->pushOverlay(Middle
);
305 ErrorOr
<vfs::Status
> Status1((std::error_code())),
306 Status2((std::error_code())), Status3((std::error_code())),
307 StatusB((std::error_code())), StatusM((std::error_code())),
308 StatusT((std::error_code()));
310 Base
->addRegularFile("/foo");
311 StatusB
= Base
->status("/foo");
312 ASSERT_FALSE(StatusB
.getError());
313 Status1
= O
->status("/foo");
314 ASSERT_FALSE(Status1
.getError());
315 Middle
->addRegularFile("/foo");
316 StatusM
= Middle
->status("/foo");
317 ASSERT_FALSE(StatusM
.getError());
318 Status2
= O
->status("/foo");
319 ASSERT_FALSE(Status2
.getError());
320 Top
->addRegularFile("/foo");
321 StatusT
= Top
->status("/foo");
322 ASSERT_FALSE(StatusT
.getError());
323 Status3
= O
->status("/foo");
324 ASSERT_FALSE(Status3
.getError());
326 EXPECT_TRUE(Status1
->equivalent(*StatusB
));
327 EXPECT_TRUE(Status2
->equivalent(*StatusM
));
328 EXPECT_TRUE(Status3
->equivalent(*StatusT
));
330 EXPECT_FALSE(Status1
->equivalent(*Status2
));
331 EXPECT_FALSE(Status2
->equivalent(*Status3
));
332 EXPECT_FALSE(Status1
->equivalent(*Status3
));
335 TEST(VirtualFileSystemTest
, OverlayDirsNonMerged
) {
336 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
337 IntrusiveRefCntPtr
<DummyFileSystem
> Upper(new DummyFileSystem());
338 IntrusiveRefCntPtr
<vfs::OverlayFileSystem
> O(
339 new vfs::OverlayFileSystem(Lower
));
340 O
->pushOverlay(Upper
);
342 Lower
->addDirectory("/lower-only");
343 Upper
->addDirectory("/upper-only");
345 // non-merged paths should be the same
346 ErrorOr
<vfs::Status
> Status1
= Lower
->status("/lower-only");
347 ASSERT_FALSE(Status1
.getError());
348 ErrorOr
<vfs::Status
> Status2
= O
->status("/lower-only");
349 ASSERT_FALSE(Status2
.getError());
350 EXPECT_TRUE(Status1
->equivalent(*Status2
));
352 Status1
= Upper
->status("/upper-only");
353 ASSERT_FALSE(Status1
.getError());
354 Status2
= O
->status("/upper-only");
355 ASSERT_FALSE(Status2
.getError());
356 EXPECT_TRUE(Status1
->equivalent(*Status2
));
359 TEST(VirtualFileSystemTest
, MergedDirPermissions
) {
360 // merged directories get the permissions of the upper dir
361 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
362 IntrusiveRefCntPtr
<DummyFileSystem
> Upper(new DummyFileSystem());
363 IntrusiveRefCntPtr
<vfs::OverlayFileSystem
> O(
364 new vfs::OverlayFileSystem(Lower
));
365 O
->pushOverlay(Upper
);
367 ErrorOr
<vfs::Status
> Status((std::error_code()));
368 Lower
->addDirectory("/both", sys::fs::owner_read
);
369 Upper
->addDirectory("/both", sys::fs::owner_all
| sys::fs::group_read
);
370 Status
= O
->status("/both");
371 ASSERT_FALSE(Status
.getError());
372 EXPECT_EQ(0740, Status
->getPermissions());
374 // permissions (as usual) are not recursively applied
375 Lower
->addRegularFile("/both/foo", sys::fs::owner_read
);
376 Upper
->addRegularFile("/both/bar", sys::fs::owner_write
);
377 Status
= O
->status("/both/foo");
378 ASSERT_FALSE(Status
.getError());
379 EXPECT_EQ(0400, Status
->getPermissions());
380 Status
= O
->status("/both/bar");
381 ASSERT_FALSE(Status
.getError());
382 EXPECT_EQ(0200, Status
->getPermissions());
385 TEST(VirtualFileSystemTest
, OverlayIterator
) {
386 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
387 Lower
->addRegularFile("/foo");
388 IntrusiveRefCntPtr
<DummyFileSystem
> Upper(new DummyFileSystem());
390 IntrusiveRefCntPtr
<vfs::OverlayFileSystem
> O(
391 new vfs::OverlayFileSystem(Lower
));
392 O
->pushOverlay(Upper
);
394 ErrorOr
<vfs::Status
> Status((std::error_code()));
396 auto it
= O
->overlays_begin();
397 auto end
= O
->overlays_end();
401 Status
= (*it
)->status("/foo");
402 ASSERT_TRUE(Status
.getError());
407 Status
= (*it
)->status("/foo");
408 ASSERT_FALSE(Status
.getError());
409 EXPECT_TRUE(Status
->exists());
416 auto it
= O
->overlays_rbegin();
417 auto end
= O
->overlays_rend();
421 Status
= (*it
)->status("/foo");
422 ASSERT_FALSE(Status
.getError());
423 EXPECT_TRUE(Status
->exists());
428 Status
= (*it
)->status("/foo");
429 ASSERT_TRUE(Status
.getError());
436 TEST(VirtualFileSystemTest
, BasicRealFSIteration
) {
437 TempDir
TestDirectory("virtual-file-system-test", /*Unique*/ true);
438 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= vfs::getRealFileSystem();
441 vfs::directory_iterator I
= FS
->dir_begin(Twine(TestDirectory
.path()), EC
);
443 EXPECT_EQ(vfs::directory_iterator(), I
); // empty directory is empty
445 TempDir
_a(TestDirectory
.path("a"));
446 TempDir
_ab(TestDirectory
.path("a/b"));
447 TempDir
_c(TestDirectory
.path("c"));
448 TempDir
_cd(TestDirectory
.path("c/d"));
450 I
= FS
->dir_begin(Twine(TestDirectory
.path()), EC
);
452 ASSERT_NE(vfs::directory_iterator(), I
);
453 // Check either a or c, since we can't rely on the iteration order.
454 EXPECT_TRUE(I
->path().endswith("a") || I
->path().endswith("c"));
457 ASSERT_NE(vfs::directory_iterator(), I
);
458 EXPECT_TRUE(I
->path().endswith("a") || I
->path().endswith("c"));
460 EXPECT_EQ(vfs::directory_iterator(), I
);
464 TEST(VirtualFileSystemTest
, MultipleWorkingDirs
) {
465 // Our root contains a/aa, b/bb, c, where c is a link to a/.
466 // Run tests both in root/b/ and root/c/ (to test "normal" and symlink dirs).
467 // Interleave operations to show the working directories are independent.
468 TempDir
Root("r", /*Unique*/ true);
469 TempDir
ADir(Root
.path("a"));
470 TempDir
BDir(Root
.path("b"));
471 TempLink
C(ADir
.path(), Root
.path("c"));
472 TempFile
AA(ADir
.path("aa"), "", "aaaa");
473 TempFile
BB(BDir
.path("bb"), "", "bbbb");
474 std::unique_ptr
<vfs::FileSystem
> BFS
= vfs::createPhysicalFileSystem(),
475 CFS
= vfs::createPhysicalFileSystem();
477 ASSERT_FALSE(BFS
->setCurrentWorkingDirectory(BDir
.path()));
478 ASSERT_FALSE(CFS
->setCurrentWorkingDirectory(C
.path()));
479 EXPECT_EQ(BDir
.path(), *BFS
->getCurrentWorkingDirectory());
480 EXPECT_EQ(C
.path(), *CFS
->getCurrentWorkingDirectory());
482 // openFileForRead(), indirectly.
483 auto BBuf
= BFS
->getBufferForFile("bb");
485 EXPECT_EQ("bbbb", (*BBuf
)->getBuffer());
487 auto ABuf
= CFS
->getBufferForFile("aa");
489 EXPECT_EQ("aaaa", (*ABuf
)->getBuffer());
492 auto BStat
= BFS
->status("bb");
494 EXPECT_EQ("bb", BStat
->getName());
496 auto AStat
= CFS
->status("aa");
498 EXPECT_EQ("aa", AStat
->getName()); // unresolved name
501 SmallString
<128> BPath
;
502 ASSERT_FALSE(BFS
->getRealPath("bb", BPath
));
503 EXPECT_EQ(BB
.path(), BPath
);
505 SmallString
<128> APath
;
506 ASSERT_FALSE(CFS
->getRealPath("aa", APath
));
507 EXPECT_EQ(AA
.path(), APath
); // Reports resolved name.
511 auto BIt
= BFS
->dir_begin(".", EC
);
513 ASSERT_NE(BIt
, vfs::directory_iterator());
514 EXPECT_EQ((BDir
.path() + "/./bb").str(), BIt
->path());
517 ASSERT_EQ(BIt
, vfs::directory_iterator());
519 auto CIt
= CFS
->dir_begin(".", EC
);
521 ASSERT_NE(CIt
, vfs::directory_iterator());
522 EXPECT_EQ((ADir
.path() + "/./aa").str(),
523 CIt
->path()); // Partly resolved name!
524 CIt
.increment(EC
); // Because likely to read through this path.
526 ASSERT_EQ(CIt
, vfs::directory_iterator());
529 TEST(VirtualFileSystemTest
, BrokenSymlinkRealFSIteration
) {
530 TempDir
TestDirectory("virtual-file-system-test", /*Unique*/ true);
531 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= vfs::getRealFileSystem();
533 TempLink
_a("no_such_file", TestDirectory
.path("a"));
534 TempDir
_b(TestDirectory
.path("b"));
535 TempLink
_c("no_such_file", TestDirectory
.path("c"));
537 // Should get no iteration error, but a stat error for the broken symlinks.
538 std::map
<std::string
, std::error_code
> StatResults
;
540 for (vfs::directory_iterator
541 I
= FS
->dir_begin(Twine(TestDirectory
.path()), EC
),
543 I
!= E
; I
.increment(EC
)) {
545 StatResults
[std::string(sys::path::filename(I
->path()))] =
546 FS
->status(I
->path()).getError();
551 Pair("a", std::make_error_code(std::errc::no_such_file_or_directory
)),
552 Pair("b", std::error_code()),
554 std::make_error_code(std::errc::no_such_file_or_directory
))));
558 TEST(VirtualFileSystemTest
, BasicRealFSRecursiveIteration
) {
559 TempDir
TestDirectory("virtual-file-system-test", /*Unique*/ true);
560 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= vfs::getRealFileSystem();
564 vfs::recursive_directory_iterator(*FS
, Twine(TestDirectory
.path()), EC
);
566 EXPECT_EQ(vfs::recursive_directory_iterator(), I
); // empty directory is empty
568 TempDir
_a(TestDirectory
.path("a"));
569 TempDir
_ab(TestDirectory
.path("a/b"));
570 TempDir
_c(TestDirectory
.path("c"));
571 TempDir
_cd(TestDirectory
.path("c/d"));
573 I
= vfs::recursive_directory_iterator(*FS
, Twine(TestDirectory
.path()), EC
);
575 ASSERT_NE(vfs::recursive_directory_iterator(), I
);
577 std::vector
<std::string
> Contents
;
578 for (auto E
= vfs::recursive_directory_iterator(); !EC
&& I
!= E
;
580 Contents
.push_back(std::string(I
->path()));
583 // Check contents, which may be in any order
584 EXPECT_EQ(4U, Contents
.size());
585 int Counts
[4] = {0, 0, 0, 0};
586 for (const std::string
&Name
: Contents
) {
587 ASSERT_FALSE(Name
.empty());
588 int Index
= Name
[Name
.size() - 1] - 'a';
593 EXPECT_EQ(1, Counts
[0]); // a
594 EXPECT_EQ(1, Counts
[1]); // b
595 EXPECT_EQ(1, Counts
[2]); // c
596 EXPECT_EQ(1, Counts
[3]); // d
599 TEST(VirtualFileSystemTest
, BasicRealFSRecursiveIterationNoPush
) {
600 TempDir
TestDirectory("virtual-file-system-test", /*Unique*/ true);
602 TempDir
_a(TestDirectory
.path("a"));
603 TempDir
_ab(TestDirectory
.path("a/b"));
604 TempDir
_c(TestDirectory
.path("c"));
605 TempDir
_cd(TestDirectory
.path("c/d"));
606 TempDir
_e(TestDirectory
.path("e"));
607 TempDir
_ef(TestDirectory
.path("e/f"));
608 TempDir
_g(TestDirectory
.path("g"));
610 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= vfs::getRealFileSystem();
612 // Test that calling no_push on entries without subdirectories has no effect.
616 vfs::recursive_directory_iterator(*FS
, Twine(TestDirectory
.path()), EC
);
619 std::vector
<std::string
> Contents
;
620 for (auto E
= vfs::recursive_directory_iterator(); !EC
&& I
!= E
;
622 Contents
.push_back(std::string(I
->path()));
623 char last
= I
->path().back();
635 EXPECT_EQ(7U, Contents
.size());
638 // Test that calling no_push skips subdirectories.
642 vfs::recursive_directory_iterator(*FS
, Twine(TestDirectory
.path()), EC
);
645 std::vector
<std::string
> Contents
;
646 for (auto E
= vfs::recursive_directory_iterator(); !EC
&& I
!= E
;
648 Contents
.push_back(std::string(I
->path()));
649 char last
= I
->path().back();
661 // Check contents, which may be in any order
662 EXPECT_EQ(4U, Contents
.size());
663 int Counts
[7] = {0, 0, 0, 0, 0, 0, 0};
664 for (const std::string
&Name
: Contents
) {
665 ASSERT_FALSE(Name
.empty());
666 int Index
= Name
[Name
.size() - 1] - 'a';
671 EXPECT_EQ(1, Counts
[0]); // a
672 EXPECT_EQ(0, Counts
[1]); // b
673 EXPECT_EQ(1, Counts
[2]); // c
674 EXPECT_EQ(0, Counts
[3]); // d
675 EXPECT_EQ(1, Counts
[4]); // e
676 EXPECT_EQ(0, Counts
[5]); // f
677 EXPECT_EQ(1, Counts
[6]); // g
682 TEST(VirtualFileSystemTest
, BrokenSymlinkRealFSRecursiveIteration
) {
683 TempDir
TestDirectory("virtual-file-system-test", /*Unique*/ true);
684 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= vfs::getRealFileSystem();
686 TempLink
_a("no_such_file", TestDirectory
.path("a"));
687 TempDir
_b(TestDirectory
.path("b"));
688 TempLink
_ba("no_such_file", TestDirectory
.path("b/a"));
689 TempDir
_bb(TestDirectory
.path("b/b"));
690 TempLink
_bc("no_such_file", TestDirectory
.path("b/c"));
691 TempLink
_c("no_such_file", TestDirectory
.path("c"));
692 TempDir
_d(TestDirectory
.path("d"));
693 TempDir
_dd(TestDirectory
.path("d/d"));
694 TempDir
_ddd(TestDirectory
.path("d/d/d"));
695 TempLink
_e("no_such_file", TestDirectory
.path("e"));
697 std::vector
<std::string
> VisitedBrokenSymlinks
;
698 std::vector
<std::string
> VisitedNonBrokenSymlinks
;
700 for (vfs::recursive_directory_iterator
701 I(*FS
, Twine(TestDirectory
.path()), EC
),
703 I
!= E
; I
.increment(EC
)) {
705 (FS
->status(I
->path()) ? VisitedNonBrokenSymlinks
: VisitedBrokenSymlinks
)
706 .push_back(std::string(I
->path()));
709 // Check visited file names.
710 EXPECT_THAT(VisitedBrokenSymlinks
,
711 UnorderedElementsAre(_a
.path().str(), _ba
.path().str(),
712 _bc
.path().str(), _c
.path().str(),
714 EXPECT_THAT(VisitedNonBrokenSymlinks
,
715 UnorderedElementsAre(_b
.path().str(), _bb
.path().str(),
716 _d
.path().str(), _dd
.path().str(),
721 template <typename DirIter
>
722 static void checkContents(DirIter I
, ArrayRef
<StringRef
> ExpectedOut
) {
724 SmallVector
<StringRef
, 4> Expected(ExpectedOut
.begin(), ExpectedOut
.end());
725 SmallVector
<std::string
, 4> InputToCheck
;
727 // Do not rely on iteration order to check for contents, sort both
728 // content vectors before comparison.
729 for (DirIter E
; !EC
&& I
!= E
; I
.increment(EC
))
730 InputToCheck
.push_back(std::string(I
->path()));
732 llvm::sort(InputToCheck
);
733 llvm::sort(Expected
);
734 EXPECT_EQ(InputToCheck
.size(), Expected
.size());
736 unsigned LastElt
= std::min(InputToCheck
.size(), Expected
.size());
737 for (unsigned Idx
= 0; Idx
!= LastElt
; ++Idx
)
738 EXPECT_EQ(StringRef(InputToCheck
[Idx
]), Expected
[Idx
]);
741 TEST(VirtualFileSystemTest
, OverlayIteration
) {
742 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
743 IntrusiveRefCntPtr
<DummyFileSystem
> Upper(new DummyFileSystem());
744 IntrusiveRefCntPtr
<vfs::OverlayFileSystem
> O(
745 new vfs::OverlayFileSystem(Lower
));
746 O
->pushOverlay(Upper
);
749 checkContents(O
->dir_begin("/", EC
), ArrayRef
<StringRef
>());
751 Lower
->addRegularFile("/file1");
752 checkContents(O
->dir_begin("/", EC
), ArrayRef
<StringRef
>("/file1"));
754 Upper
->addRegularFile("/file2");
755 checkContents(O
->dir_begin("/", EC
), {"/file2", "/file1"});
757 Lower
->addDirectory("/dir1");
758 Lower
->addRegularFile("/dir1/foo");
759 Upper
->addDirectory("/dir2");
760 Upper
->addRegularFile("/dir2/foo");
761 checkContents(O
->dir_begin("/dir2", EC
), ArrayRef
<StringRef
>("/dir2/foo"));
762 checkContents(O
->dir_begin("/", EC
), {"/dir2", "/file2", "/dir1", "/file1"});
765 TEST(VirtualFileSystemTest
, OverlayRecursiveIteration
) {
766 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
767 IntrusiveRefCntPtr
<DummyFileSystem
> Middle(new DummyFileSystem());
768 IntrusiveRefCntPtr
<DummyFileSystem
> Upper(new DummyFileSystem());
769 IntrusiveRefCntPtr
<vfs::OverlayFileSystem
> O(
770 new vfs::OverlayFileSystem(Lower
));
771 O
->pushOverlay(Middle
);
772 O
->pushOverlay(Upper
);
775 checkContents(vfs::recursive_directory_iterator(*O
, "/", EC
),
776 ArrayRef
<StringRef
>());
778 Lower
->addRegularFile("/file1");
779 checkContents(vfs::recursive_directory_iterator(*O
, "/", EC
),
780 ArrayRef
<StringRef
>("/file1"));
782 Upper
->addDirectory("/dir");
783 Upper
->addRegularFile("/dir/file2");
784 checkContents(vfs::recursive_directory_iterator(*O
, "/", EC
),
785 {"/dir", "/dir/file2", "/file1"});
787 Lower
->addDirectory("/dir1");
788 Lower
->addRegularFile("/dir1/foo");
789 Lower
->addDirectory("/dir1/a");
790 Lower
->addRegularFile("/dir1/a/b");
791 Middle
->addDirectory("/a");
792 Middle
->addDirectory("/a/b");
793 Middle
->addDirectory("/a/b/c");
794 Middle
->addRegularFile("/a/b/c/d");
795 Middle
->addRegularFile("/hiddenByUp");
796 Upper
->addDirectory("/dir2");
797 Upper
->addRegularFile("/dir2/foo");
798 Upper
->addRegularFile("/hiddenByUp");
799 checkContents(vfs::recursive_directory_iterator(*O
, "/dir2", EC
),
800 ArrayRef
<StringRef
>("/dir2/foo"));
801 checkContents(vfs::recursive_directory_iterator(*O
, "/", EC
),
802 {"/dir", "/dir/file2", "/dir2", "/dir2/foo", "/hiddenByUp",
803 "/a", "/a/b", "/a/b/c", "/a/b/c/d", "/dir1", "/dir1/a",
804 "/dir1/a/b", "/dir1/foo", "/file1"});
807 TEST(VirtualFileSystemTest
, ThreeLevelIteration
) {
808 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
809 IntrusiveRefCntPtr
<DummyFileSystem
> Middle(new DummyFileSystem());
810 IntrusiveRefCntPtr
<DummyFileSystem
> Upper(new DummyFileSystem());
811 IntrusiveRefCntPtr
<vfs::OverlayFileSystem
> O(
812 new vfs::OverlayFileSystem(Lower
));
813 O
->pushOverlay(Middle
);
814 O
->pushOverlay(Upper
);
817 checkContents(O
->dir_begin("/", EC
), ArrayRef
<StringRef
>());
819 Middle
->addRegularFile("/file2");
820 checkContents(O
->dir_begin("/", EC
), ArrayRef
<StringRef
>("/file2"));
822 Lower
->addRegularFile("/file1");
823 Upper
->addRegularFile("/file3");
824 checkContents(O
->dir_begin("/", EC
), {"/file3", "/file2", "/file1"});
827 TEST(VirtualFileSystemTest
, HiddenInIteration
) {
828 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
829 IntrusiveRefCntPtr
<DummyFileSystem
> Middle(new DummyFileSystem());
830 IntrusiveRefCntPtr
<DummyFileSystem
> Upper(new DummyFileSystem());
831 IntrusiveRefCntPtr
<vfs::OverlayFileSystem
> O(
832 new vfs::OverlayFileSystem(Lower
));
833 O
->pushOverlay(Middle
);
834 O
->pushOverlay(Upper
);
837 Lower
->addRegularFile("/onlyInLow");
838 Lower
->addDirectory("/hiddenByMid");
839 Lower
->addDirectory("/hiddenByUp");
840 Middle
->addRegularFile("/onlyInMid");
841 Middle
->addRegularFile("/hiddenByMid");
842 Middle
->addDirectory("/hiddenByUp");
843 Upper
->addRegularFile("/onlyInUp");
844 Upper
->addRegularFile("/hiddenByUp");
846 O
->dir_begin("/", EC
),
847 {"/hiddenByUp", "/onlyInUp", "/hiddenByMid", "/onlyInMid", "/onlyInLow"});
849 // Make sure we get the top-most entry
852 vfs::directory_iterator I
= O
->dir_begin("/", EC
), E
;
853 for (; !EC
&& I
!= E
; I
.increment(EC
))
854 if (I
->path() == "/hiddenByUp")
857 EXPECT_EQ(sys::fs::file_type::regular_file
, I
->type());
861 vfs::directory_iterator I
= O
->dir_begin("/", EC
), E
;
862 for (; !EC
&& I
!= E
; I
.increment(EC
))
863 if (I
->path() == "/hiddenByMid")
866 EXPECT_EQ(sys::fs::file_type::regular_file
, I
->type());
870 TEST(OverlayFileSystemTest
, PrintOutput
) {
871 auto Dummy
= makeIntrusiveRefCnt
<DummyFileSystem
>();
872 auto Overlay1
= makeIntrusiveRefCnt
<vfs::OverlayFileSystem
>(Dummy
);
873 Overlay1
->pushOverlay(Dummy
);
874 auto Overlay2
= makeIntrusiveRefCnt
<vfs::OverlayFileSystem
>(Overlay1
);
875 Overlay2
->pushOverlay(Dummy
);
877 SmallString
<0> Output
;
878 raw_svector_ostream OuputStream
{Output
};
880 Overlay2
->print(OuputStream
, vfs::FileSystem::PrintType::Summary
);
881 ASSERT_EQ("OverlayFileSystem\n", Output
);
884 Overlay2
->print(OuputStream
, vfs::FileSystem::PrintType::Contents
);
885 ASSERT_EQ("OverlayFileSystem\n"
886 " DummyFileSystem (Summary)\n"
887 " OverlayFileSystem\n",
891 Overlay2
->print(OuputStream
, vfs::FileSystem::PrintType::RecursiveContents
);
892 ASSERT_EQ("OverlayFileSystem\n"
893 " DummyFileSystem (RecursiveContents)\n"
894 " OverlayFileSystem\n"
895 " DummyFileSystem (RecursiveContents)\n"
896 " DummyFileSystem (RecursiveContents)\n",
900 TEST(ProxyFileSystemTest
, Basic
) {
901 IntrusiveRefCntPtr
<vfs::InMemoryFileSystem
> Base(
902 new vfs::InMemoryFileSystem());
903 vfs::ProxyFileSystem
PFS(Base
);
905 Base
->addFile("/a", 0, MemoryBuffer::getMemBuffer("test"));
907 auto Stat
= PFS
.status("/a");
908 ASSERT_FALSE(Stat
.getError());
910 auto File
= PFS
.openFileForRead("/a");
911 ASSERT_FALSE(File
.getError());
912 EXPECT_EQ("test", (*(*File
)->getBuffer("ignored"))->getBuffer());
915 vfs::directory_iterator I
= PFS
.dir_begin("/", EC
);
917 ASSERT_EQ("/a", I
->path());
920 ASSERT_EQ(vfs::directory_iterator(), I
);
922 ASSERT_FALSE(PFS
.setCurrentWorkingDirectory("/"));
924 auto PWD
= PFS
.getCurrentWorkingDirectory();
925 ASSERT_FALSE(PWD
.getError());
926 ASSERT_EQ("/", getPosixPath(*PWD
));
928 SmallString
<16> Path
;
929 ASSERT_FALSE(PFS
.getRealPath("a", Path
));
930 ASSERT_EQ("/a", getPosixPath(Path
));
933 ASSERT_FALSE(PFS
.isLocal("/a", Local
));
937 class InMemoryFileSystemTest
: public ::testing::Test
{
939 llvm::vfs::InMemoryFileSystem FS
;
940 llvm::vfs::InMemoryFileSystem NormalizedFS
;
942 InMemoryFileSystemTest()
943 : FS(/*UseNormalizedPaths=*/false),
944 NormalizedFS(/*UseNormalizedPaths=*/true) {}
947 MATCHER_P2(IsHardLinkTo
, FS
, Target
, "") {
948 StringRef From
= arg
;
949 StringRef To
= Target
;
950 auto OpenedFrom
= FS
->openFileForRead(From
);
951 auto OpenedTo
= FS
->openFileForRead(To
);
952 return !OpenedFrom
.getError() && !OpenedTo
.getError() &&
953 (*OpenedFrom
)->status()->getUniqueID() ==
954 (*OpenedTo
)->status()->getUniqueID();
957 TEST_F(InMemoryFileSystemTest
, IsEmpty
) {
958 auto Stat
= FS
.status("/a");
959 ASSERT_EQ(Stat
.getError(), errc::no_such_file_or_directory
) << FS
.toString();
960 Stat
= FS
.status("/");
961 ASSERT_EQ(Stat
.getError(), errc::no_such_file_or_directory
) << FS
.toString();
964 TEST_F(InMemoryFileSystemTest
, WindowsPath
) {
965 FS
.addFile("c:/windows/system128/foo.cpp", 0, MemoryBuffer::getMemBuffer(""));
966 auto Stat
= FS
.status("c:");
968 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << FS
.toString();
970 Stat
= FS
.status("c:/windows/system128/foo.cpp");
971 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << FS
.toString();
972 FS
.addFile("d:/windows/foo.cpp", 0, MemoryBuffer::getMemBuffer(""));
973 Stat
= FS
.status("d:/windows/foo.cpp");
974 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << FS
.toString();
977 TEST_F(InMemoryFileSystemTest
, OverlayFile
) {
978 FS
.addFile("/a", 0, MemoryBuffer::getMemBuffer("a"));
979 NormalizedFS
.addFile("/a", 0, MemoryBuffer::getMemBuffer("a"));
980 auto Stat
= FS
.status("/");
981 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << FS
.toString();
982 Stat
= FS
.status("/.");
984 Stat
= NormalizedFS
.status("/.");
985 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << FS
.toString();
986 Stat
= FS
.status("/a");
987 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n" << FS
.toString();
988 ASSERT_EQ("/a", Stat
->getName());
991 TEST_F(InMemoryFileSystemTest
, OverlayFileNoOwn
) {
992 auto Buf
= MemoryBuffer::getMemBuffer("a");
993 FS
.addFileNoOwn("/a", 0, *Buf
);
994 auto Stat
= FS
.status("/a");
995 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n" << FS
.toString();
996 ASSERT_EQ("/a", Stat
->getName());
999 TEST_F(InMemoryFileSystemTest
, OpenFileForRead
) {
1000 FS
.addFile("/a", 0, MemoryBuffer::getMemBuffer("a"));
1001 FS
.addFile("././c", 0, MemoryBuffer::getMemBuffer("c"));
1002 FS
.addFile("./d/../d", 0, MemoryBuffer::getMemBuffer("d"));
1003 NormalizedFS
.addFile("/a", 0, MemoryBuffer::getMemBuffer("a"));
1004 NormalizedFS
.addFile("././c", 0, MemoryBuffer::getMemBuffer("c"));
1005 NormalizedFS
.addFile("./d/../d", 0, MemoryBuffer::getMemBuffer("d"));
1006 auto File
= FS
.openFileForRead("/a");
1007 ASSERT_EQ("a", (*(*File
)->getBuffer("ignored"))->getBuffer());
1008 File
= FS
.openFileForRead("/a"); // Open again.
1009 ASSERT_EQ("a", (*(*File
)->getBuffer("ignored"))->getBuffer());
1010 File
= NormalizedFS
.openFileForRead("/././a"); // Open again.
1011 ASSERT_EQ("a", (*(*File
)->getBuffer("ignored"))->getBuffer());
1012 File
= FS
.openFileForRead("/");
1013 ASSERT_EQ(File
.getError(), errc::invalid_argument
) << FS
.toString();
1014 File
= FS
.openFileForRead("/b");
1015 ASSERT_EQ(File
.getError(), errc::no_such_file_or_directory
) << FS
.toString();
1016 File
= FS
.openFileForRead("./c");
1018 File
= FS
.openFileForRead("e/../d");
1020 File
= NormalizedFS
.openFileForRead("./c");
1021 ASSERT_EQ("c", (*(*File
)->getBuffer("ignored"))->getBuffer());
1022 File
= NormalizedFS
.openFileForRead("e/../d");
1023 ASSERT_EQ("d", (*(*File
)->getBuffer("ignored"))->getBuffer());
1026 TEST_F(InMemoryFileSystemTest
, DuplicatedFile
) {
1027 ASSERT_TRUE(FS
.addFile("/a", 0, MemoryBuffer::getMemBuffer("a")));
1028 ASSERT_FALSE(FS
.addFile("/a/b", 0, MemoryBuffer::getMemBuffer("a")));
1029 ASSERT_TRUE(FS
.addFile("/a", 0, MemoryBuffer::getMemBuffer("a")));
1030 ASSERT_FALSE(FS
.addFile("/a", 0, MemoryBuffer::getMemBuffer("b")));
1033 TEST_F(InMemoryFileSystemTest
, DirectoryIteration
) {
1034 FS
.addFile("/a", 0, MemoryBuffer::getMemBuffer(""));
1035 FS
.addFile("/b/c", 0, MemoryBuffer::getMemBuffer(""));
1038 vfs::directory_iterator I
= FS
.dir_begin("/", EC
);
1040 ASSERT_EQ("/a", I
->path());
1043 ASSERT_EQ("/b", I
->path());
1046 ASSERT_EQ(vfs::directory_iterator(), I
);
1048 I
= FS
.dir_begin("/b", EC
);
1050 // When on Windows, we end up with "/b\\c" as the name. Convert to Posix
1051 // path for the sake of the comparison.
1052 ASSERT_EQ("/b/c", getPosixPath(std::string(I
->path())));
1055 ASSERT_EQ(vfs::directory_iterator(), I
);
1058 TEST_F(InMemoryFileSystemTest
, WorkingDirectory
) {
1059 FS
.setCurrentWorkingDirectory("/b");
1060 FS
.addFile("c", 0, MemoryBuffer::getMemBuffer(""));
1062 auto Stat
= FS
.status("/b/c");
1063 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n" << FS
.toString();
1064 ASSERT_EQ("/b/c", Stat
->getName());
1065 ASSERT_EQ("/b", *FS
.getCurrentWorkingDirectory());
1067 Stat
= FS
.status("c");
1068 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n" << FS
.toString();
1070 NormalizedFS
.setCurrentWorkingDirectory("/b/c");
1071 NormalizedFS
.setCurrentWorkingDirectory(".");
1073 getPosixPath(NormalizedFS
.getCurrentWorkingDirectory().get()));
1074 NormalizedFS
.setCurrentWorkingDirectory("..");
1076 getPosixPath(NormalizedFS
.getCurrentWorkingDirectory().get()));
1079 TEST_F(InMemoryFileSystemTest
, IsLocal
) {
1080 FS
.setCurrentWorkingDirectory("/b");
1081 FS
.addFile("c", 0, MemoryBuffer::getMemBuffer(""));
1084 bool IsLocal
= true;
1085 EC
= FS
.isLocal("c", IsLocal
);
1087 ASSERT_FALSE(IsLocal
);
1090 #if !defined(_WIN32)
1091 TEST_F(InMemoryFileSystemTest
, GetRealPath
) {
1092 SmallString
<16> Path
;
1093 EXPECT_EQ(FS
.getRealPath("b", Path
), errc::operation_not_permitted
);
1095 auto GetRealPath
= [this](StringRef P
) {
1096 SmallString
<16> Output
;
1097 auto EC
= FS
.getRealPath(P
, Output
);
1099 return std::string(Output
);
1102 FS
.setCurrentWorkingDirectory("a");
1103 EXPECT_EQ(GetRealPath("b"), "a/b");
1104 EXPECT_EQ(GetRealPath("../b"), "b");
1105 EXPECT_EQ(GetRealPath("b/./c"), "a/b/c");
1107 FS
.setCurrentWorkingDirectory("/a");
1108 EXPECT_EQ(GetRealPath("b"), "/a/b");
1109 EXPECT_EQ(GetRealPath("../b"), "/b");
1110 EXPECT_EQ(GetRealPath("b/./c"), "/a/b/c");
1114 TEST_F(InMemoryFileSystemTest
, AddFileWithUser
) {
1115 FS
.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"), 0xFEEDFACE);
1116 auto Stat
= FS
.status("/a");
1117 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n" << FS
.toString();
1118 ASSERT_TRUE(Stat
->isDirectory());
1119 ASSERT_EQ(0xFEEDFACE, Stat
->getUser());
1120 Stat
= FS
.status("/a/b");
1121 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n" << FS
.toString();
1122 ASSERT_TRUE(Stat
->isDirectory());
1123 ASSERT_EQ(0xFEEDFACE, Stat
->getUser());
1124 Stat
= FS
.status("/a/b/c");
1125 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n" << FS
.toString();
1126 ASSERT_TRUE(Stat
->isRegularFile());
1127 ASSERT_EQ(sys::fs::perms::all_all
, Stat
->getPermissions());
1128 ASSERT_EQ(0xFEEDFACE, Stat
->getUser());
1131 TEST_F(InMemoryFileSystemTest
, AddFileWithGroup
) {
1132 FS
.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"), None
, 0xDABBAD00);
1133 auto Stat
= FS
.status("/a");
1134 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n" << FS
.toString();
1135 ASSERT_TRUE(Stat
->isDirectory());
1136 ASSERT_EQ(0xDABBAD00, Stat
->getGroup());
1137 Stat
= FS
.status("/a/b");
1138 ASSERT_TRUE(Stat
->isDirectory());
1139 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n" << FS
.toString();
1140 ASSERT_EQ(0xDABBAD00, Stat
->getGroup());
1141 Stat
= FS
.status("/a/b/c");
1142 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n" << FS
.toString();
1143 ASSERT_TRUE(Stat
->isRegularFile());
1144 ASSERT_EQ(sys::fs::perms::all_all
, Stat
->getPermissions());
1145 ASSERT_EQ(0xDABBAD00, Stat
->getGroup());
1148 TEST_F(InMemoryFileSystemTest
, AddFileWithFileType
) {
1149 FS
.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"), None
, None
,
1150 sys::fs::file_type::socket_file
);
1151 auto Stat
= FS
.status("/a");
1152 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n" << FS
.toString();
1153 ASSERT_TRUE(Stat
->isDirectory());
1154 Stat
= FS
.status("/a/b");
1155 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n" << FS
.toString();
1156 ASSERT_TRUE(Stat
->isDirectory());
1157 Stat
= FS
.status("/a/b/c");
1158 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n" << FS
.toString();
1159 ASSERT_EQ(sys::fs::file_type::socket_file
, Stat
->getType());
1160 ASSERT_EQ(sys::fs::perms::all_all
, Stat
->getPermissions());
1163 TEST_F(InMemoryFileSystemTest
, AddFileWithPerms
) {
1164 FS
.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"), None
, None
, None
,
1165 sys::fs::perms::owner_read
| sys::fs::perms::owner_write
);
1166 auto Stat
= FS
.status("/a");
1167 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n" << FS
.toString();
1168 ASSERT_TRUE(Stat
->isDirectory());
1169 ASSERT_EQ(sys::fs::perms::owner_read
| sys::fs::perms::owner_write
|
1170 sys::fs::perms::owner_exe
,
1171 Stat
->getPermissions());
1172 Stat
= FS
.status("/a/b");
1173 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n" << FS
.toString();
1174 ASSERT_TRUE(Stat
->isDirectory());
1175 ASSERT_EQ(sys::fs::perms::owner_read
| sys::fs::perms::owner_write
|
1176 sys::fs::perms::owner_exe
,
1177 Stat
->getPermissions());
1178 Stat
= FS
.status("/a/b/c");
1179 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n" << FS
.toString();
1180 ASSERT_TRUE(Stat
->isRegularFile());
1181 ASSERT_EQ(sys::fs::perms::owner_read
| sys::fs::perms::owner_write
,
1182 Stat
->getPermissions());
1185 TEST_F(InMemoryFileSystemTest
, AddDirectoryThenAddChild
) {
1186 FS
.addFile("/a", 0, MemoryBuffer::getMemBuffer(""), /*User=*/None
,
1187 /*Group=*/None
, sys::fs::file_type::directory_file
);
1188 FS
.addFile("/a/b", 0, MemoryBuffer::getMemBuffer("abc"), /*User=*/None
,
1189 /*Group=*/None
, sys::fs::file_type::regular_file
);
1190 auto Stat
= FS
.status("/a");
1191 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n" << FS
.toString();
1192 ASSERT_TRUE(Stat
->isDirectory());
1193 Stat
= FS
.status("/a/b");
1194 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n" << FS
.toString();
1195 ASSERT_TRUE(Stat
->isRegularFile());
1198 // Test that the name returned by status() is in the same form as the path that
1199 // was requested (to match the behavior of RealFileSystem).
1200 TEST_F(InMemoryFileSystemTest
, StatusName
) {
1201 NormalizedFS
.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"),
1203 /*Group=*/None
, sys::fs::file_type::regular_file
);
1204 NormalizedFS
.setCurrentWorkingDirectory("/a/b");
1206 // Access using InMemoryFileSystem::status.
1207 auto Stat
= NormalizedFS
.status("../b/c");
1208 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n"
1209 << NormalizedFS
.toString();
1210 ASSERT_TRUE(Stat
->isRegularFile());
1211 ASSERT_EQ("../b/c", Stat
->getName());
1213 // Access using InMemoryFileAdaptor::status.
1214 auto File
= NormalizedFS
.openFileForRead("../b/c");
1215 ASSERT_FALSE(File
.getError()) << File
.getError() << "\n"
1216 << NormalizedFS
.toString();
1217 Stat
= (*File
)->status();
1218 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n"
1219 << NormalizedFS
.toString();
1220 ASSERT_TRUE(Stat
->isRegularFile());
1221 ASSERT_EQ("../b/c", Stat
->getName());
1223 // Access using a directory iterator.
1225 llvm::vfs::directory_iterator It
= NormalizedFS
.dir_begin("../b", EC
);
1226 // When on Windows, we end up with "../b\\c" as the name. Convert to Posix
1227 // path for the sake of the comparison.
1228 ASSERT_EQ("../b/c", getPosixPath(std::string(It
->path())));
1231 TEST_F(InMemoryFileSystemTest
, AddHardLinkToFile
) {
1232 StringRef FromLink
= "/path/to/FROM/link";
1233 StringRef Target
= "/path/to/TO/file";
1234 FS
.addFile(Target
, 0, MemoryBuffer::getMemBuffer("content of target"));
1235 EXPECT_TRUE(FS
.addHardLink(FromLink
, Target
));
1236 EXPECT_THAT(FromLink
, IsHardLinkTo(&FS
, Target
));
1237 EXPECT_EQ(FS
.status(FromLink
)->getSize(), FS
.status(Target
)->getSize());
1238 EXPECT_EQ(FS
.getBufferForFile(FromLink
)->get()->getBuffer(),
1239 FS
.getBufferForFile(Target
)->get()->getBuffer());
1242 TEST_F(InMemoryFileSystemTest
, AddHardLinkInChainPattern
) {
1243 StringRef Link0
= "/path/to/0/link";
1244 StringRef Link1
= "/path/to/1/link";
1245 StringRef Link2
= "/path/to/2/link";
1246 StringRef Target
= "/path/to/target";
1247 FS
.addFile(Target
, 0, MemoryBuffer::getMemBuffer("content of target file"));
1248 EXPECT_TRUE(FS
.addHardLink(Link2
, Target
));
1249 EXPECT_TRUE(FS
.addHardLink(Link1
, Link2
));
1250 EXPECT_TRUE(FS
.addHardLink(Link0
, Link1
));
1251 EXPECT_THAT(Link0
, IsHardLinkTo(&FS
, Target
));
1252 EXPECT_THAT(Link1
, IsHardLinkTo(&FS
, Target
));
1253 EXPECT_THAT(Link2
, IsHardLinkTo(&FS
, Target
));
1256 TEST_F(InMemoryFileSystemTest
, AddHardLinkToAFileThatWasNotAddedBefore
) {
1257 EXPECT_FALSE(FS
.addHardLink("/path/to/link", "/path/to/target"));
1260 TEST_F(InMemoryFileSystemTest
, AddHardLinkFromAFileThatWasAddedBefore
) {
1261 StringRef Link
= "/path/to/link";
1262 StringRef Target
= "/path/to/target";
1263 FS
.addFile(Target
, 0, MemoryBuffer::getMemBuffer("content of target"));
1264 FS
.addFile(Link
, 0, MemoryBuffer::getMemBuffer("content of link"));
1265 EXPECT_FALSE(FS
.addHardLink(Link
, Target
));
1268 TEST_F(InMemoryFileSystemTest
, AddSameHardLinkMoreThanOnce
) {
1269 StringRef Link
= "/path/to/link";
1270 StringRef Target
= "/path/to/target";
1271 FS
.addFile(Target
, 0, MemoryBuffer::getMemBuffer("content of target"));
1272 EXPECT_TRUE(FS
.addHardLink(Link
, Target
));
1273 EXPECT_FALSE(FS
.addHardLink(Link
, Target
));
1276 TEST_F(InMemoryFileSystemTest
, AddFileInPlaceOfAHardLinkWithSameContent
) {
1277 StringRef Link
= "/path/to/link";
1278 StringRef Target
= "/path/to/target";
1279 StringRef Content
= "content of target";
1280 EXPECT_TRUE(FS
.addFile(Target
, 0, MemoryBuffer::getMemBuffer(Content
)));
1281 EXPECT_TRUE(FS
.addHardLink(Link
, Target
));
1282 EXPECT_TRUE(FS
.addFile(Link
, 0, MemoryBuffer::getMemBuffer(Content
)));
1285 TEST_F(InMemoryFileSystemTest
, AddFileInPlaceOfAHardLinkWithDifferentContent
) {
1286 StringRef Link
= "/path/to/link";
1287 StringRef Target
= "/path/to/target";
1288 StringRef Content
= "content of target";
1289 StringRef LinkContent
= "different content of link";
1290 EXPECT_TRUE(FS
.addFile(Target
, 0, MemoryBuffer::getMemBuffer(Content
)));
1291 EXPECT_TRUE(FS
.addHardLink(Link
, Target
));
1292 EXPECT_FALSE(FS
.addFile(Link
, 0, MemoryBuffer::getMemBuffer(LinkContent
)));
1295 TEST_F(InMemoryFileSystemTest
, AddHardLinkToADirectory
) {
1296 StringRef Dir
= "path/to/dummy/dir";
1297 StringRef Link
= "/path/to/link";
1298 StringRef File
= "path/to/dummy/dir/target";
1299 StringRef Content
= "content of target";
1300 EXPECT_TRUE(FS
.addFile(File
, 0, MemoryBuffer::getMemBuffer(Content
)));
1301 EXPECT_FALSE(FS
.addHardLink(Link
, Dir
));
1304 TEST_F(InMemoryFileSystemTest
, AddHardLinkToASymlink
) {
1305 EXPECT_TRUE(FS
.addFile("/file", 0, MemoryBuffer::getMemBuffer("content")));
1306 EXPECT_TRUE(FS
.addSymbolicLink("/symlink", "/file", 0));
1307 EXPECT_TRUE(FS
.addHardLink("/hardlink", "/symlink"));
1308 EXPECT_EQ((*FS
.getBufferForFile("/hardlink"))->getBuffer(), "content");
1311 TEST_F(InMemoryFileSystemTest
, AddHardLinkFromADirectory
) {
1312 StringRef Dir
= "path/to/dummy/dir";
1313 StringRef Target
= "path/to/dummy/dir/target";
1314 StringRef Content
= "content of target";
1315 EXPECT_TRUE(FS
.addFile(Target
, 0, MemoryBuffer::getMemBuffer(Content
)));
1316 EXPECT_FALSE(FS
.addHardLink(Dir
, Target
));
1319 TEST_F(InMemoryFileSystemTest
, AddHardLinkUnderAFile
) {
1320 StringRef CommonContent
= "content string";
1321 FS
.addFile("/a/b", 0, MemoryBuffer::getMemBuffer(CommonContent
));
1322 FS
.addFile("/c/d", 0, MemoryBuffer::getMemBuffer(CommonContent
));
1323 EXPECT_FALSE(FS
.addHardLink("/c/d/e", "/a/b"));
1326 TEST_F(InMemoryFileSystemTest
, RecursiveIterationWithHardLink
) {
1328 FS
.addFile("/a/b", 0, MemoryBuffer::getMemBuffer("content string"));
1329 EXPECT_TRUE(FS
.addHardLink("/c/d", "/a/b"));
1330 auto I
= vfs::recursive_directory_iterator(FS
, "/", EC
);
1332 std::vector
<std::string
> Nodes
;
1333 for (auto E
= vfs::recursive_directory_iterator(); !EC
&& I
!= E
;
1335 Nodes
.push_back(getPosixPath(std::string(I
->path())));
1337 EXPECT_THAT(Nodes
, testing::UnorderedElementsAre("/a", "/a/b", "/c", "/c/d"));
1340 TEST_F(InMemoryFileSystemTest
, UniqueID
) {
1341 ASSERT_TRUE(FS
.addFile("/a/b", 0, MemoryBuffer::getMemBuffer("text")));
1342 ASSERT_TRUE(FS
.addFile("/c/d", 0, MemoryBuffer::getMemBuffer("text")));
1343 ASSERT_TRUE(FS
.addHardLink("/e/f", "/a/b"));
1345 EXPECT_EQ(FS
.status("/a/b")->getUniqueID(), FS
.status("/a/b")->getUniqueID());
1346 EXPECT_NE(FS
.status("/a/b")->getUniqueID(), FS
.status("/c/d")->getUniqueID());
1347 EXPECT_EQ(FS
.status("/a/b")->getUniqueID(), FS
.status("/e/f")->getUniqueID());
1348 EXPECT_EQ(FS
.status("/a")->getUniqueID(), FS
.status("/a")->getUniqueID());
1349 EXPECT_NE(FS
.status("/a")->getUniqueID(), FS
.status("/c")->getUniqueID());
1350 EXPECT_NE(FS
.status("/a")->getUniqueID(), FS
.status("/e")->getUniqueID());
1352 // Recreating the "same" FS yields the same UniqueIDs.
1353 // Note: FS2 should match FS with respect to path normalization.
1354 vfs::InMemoryFileSystem
FS2(/*UseNormalizedPath=*/false);
1355 ASSERT_TRUE(FS2
.addFile("/a/b", 0, MemoryBuffer::getMemBuffer("text")));
1356 EXPECT_EQ(FS
.status("/a/b")->getUniqueID(),
1357 FS2
.status("/a/b")->getUniqueID());
1358 EXPECT_EQ(FS
.status("/a")->getUniqueID(), FS2
.status("/a")->getUniqueID());
1361 TEST_F(InMemoryFileSystemTest
, AddSymlinkToAFile
) {
1363 FS
.addFile("/some/file", 0, MemoryBuffer::getMemBuffer("contents")));
1364 EXPECT_TRUE(FS
.addSymbolicLink("/other/file/link", "/some/file", 0));
1365 ErrorOr
<vfs::Status
> Stat
= FS
.status("/some/file");
1366 EXPECT_TRUE(Stat
->isRegularFile());
1369 TEST_F(InMemoryFileSystemTest
, AddSymlinkToADirectory
) {
1370 EXPECT_TRUE(FS
.addSymbolicLink("/link", "/target", 0));
1372 FS
.addFile("/target/foo.h", 0, MemoryBuffer::getMemBuffer("foo")));
1373 ErrorOr
<vfs::Status
> Stat
= FS
.status("/link/foo.h");
1375 EXPECT_EQ((*Stat
).getName(), "/link/foo.h");
1376 EXPECT_TRUE(Stat
->isRegularFile());
1379 TEST_F(InMemoryFileSystemTest
, AddSymlinkToASymlink
) {
1380 EXPECT_TRUE(FS
.addSymbolicLink("/first", "/second", 0));
1381 EXPECT_TRUE(FS
.addSymbolicLink("/second", "/third", 0));
1382 EXPECT_TRUE(FS
.addFile("/third", 0, MemoryBuffer::getMemBuffer("")));
1383 ErrorOr
<vfs::Status
> Stat
= FS
.status("/first");
1385 EXPECT_EQ((*Stat
).getName(), "/first");
1386 // Follow-through symlinks by default. This matches RealFileSystem's
1388 EXPECT_TRUE(Stat
->isRegularFile());
1389 Stat
= FS
.status("/second");
1391 EXPECT_EQ((*Stat
).getName(), "/second");
1392 EXPECT_TRUE(Stat
->isRegularFile());
1393 Stat
= FS
.status("/third");
1395 EXPECT_EQ((*Stat
).getName(), "/third");
1396 EXPECT_TRUE(Stat
->isRegularFile());
1399 TEST_F(InMemoryFileSystemTest
, AddRecursiveSymlink
) {
1400 EXPECT_TRUE(FS
.addSymbolicLink("/link-a", "/link-b", 0));
1401 EXPECT_TRUE(FS
.addSymbolicLink("/link-b", "/link-a", 0));
1402 ErrorOr
<vfs::Status
> Stat
= FS
.status("/link-a/foo");
1404 EXPECT_EQ(Stat
.getError(), errc::no_such_file_or_directory
);
1407 TEST_F(InMemoryFileSystemTest
, DirectoryIteratorWithSymlinkToAFile
) {
1410 EXPECT_TRUE(FS
.addFile("/file", 0, MemoryBuffer::getMemBuffer("")));
1411 EXPECT_TRUE(FS
.addSymbolicLink("/symlink", "/file", 0));
1413 vfs::directory_iterator I
= FS
.dir_begin("/", EC
), E
;
1416 std::vector
<std::string
> Nodes
;
1417 for (; !EC
&& I
!= E
; I
.increment(EC
))
1418 Nodes
.push_back(getPosixPath(std::string(I
->path())));
1420 EXPECT_THAT(Nodes
, testing::UnorderedElementsAre("/file", "/file"));
1423 TEST_F(InMemoryFileSystemTest
, RecursiveDirectoryIteratorWithSymlinkToADir
) {
1426 EXPECT_TRUE(FS
.addFile("/dir/file", 0, MemoryBuffer::getMemBuffer("")));
1427 EXPECT_TRUE(FS
.addSymbolicLink("/dir_symlink", "/dir", 0));
1429 vfs::recursive_directory_iterator
I(FS
, "/", EC
), E
;
1432 std::vector
<std::string
> Nodes
;
1433 for (; !EC
&& I
!= E
; I
.increment(EC
))
1434 Nodes
.push_back(getPosixPath(std::string(I
->path())));
1436 EXPECT_THAT(Nodes
, testing::UnorderedElementsAre("/dir", "/dir/file", "/dir",
1440 // NOTE: in the tests below, we use '//root/' as our root directory, since it is
1441 // a legal *absolute* path on Windows as well as *nix.
1442 class VFSFromYAMLTest
: public ::testing::Test
{
1446 void SetUp() override
{ NumDiagnostics
= 0; }
1448 static void CountingDiagHandler(const SMDiagnostic
&, void *Context
) {
1449 VFSFromYAMLTest
*Test
= static_cast<VFSFromYAMLTest
*>(Context
);
1450 ++Test
->NumDiagnostics
;
1453 std::unique_ptr
<vfs::FileSystem
>
1454 getFromYAMLRawString(StringRef Content
,
1455 IntrusiveRefCntPtr
<vfs::FileSystem
> ExternalFS
) {
1456 std::unique_ptr
<MemoryBuffer
> Buffer
= MemoryBuffer::getMemBuffer(Content
);
1457 return getVFSFromYAML(std::move(Buffer
), CountingDiagHandler
, "", this,
1461 std::unique_ptr
<vfs::FileSystem
> getFromYAMLString(
1463 IntrusiveRefCntPtr
<vfs::FileSystem
> ExternalFS
= new DummyFileSystem()) {
1464 std::string
VersionPlusContent("{\n 'version':0,\n");
1465 VersionPlusContent
+= Content
.slice(Content
.find('{') + 1, StringRef::npos
);
1466 return getFromYAMLRawString(VersionPlusContent
, ExternalFS
);
1469 // This is intended as a "XFAIL" for windows hosts.
1470 bool supportsSameDirMultipleYAMLEntries() {
1471 Triple
Host(Triple::normalize(sys::getProcessTriple()));
1472 return !Host
.isOSWindows();
1476 TEST_F(VFSFromYAMLTest
, BasicVFSFromYAML
) {
1477 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
;
1478 FS
= getFromYAMLString("");
1479 EXPECT_EQ(nullptr, FS
.get());
1480 FS
= getFromYAMLString("[]");
1481 EXPECT_EQ(nullptr, FS
.get());
1482 FS
= getFromYAMLString("'string'");
1483 EXPECT_EQ(nullptr, FS
.get());
1484 EXPECT_EQ(3, NumDiagnostics
);
1487 TEST_F(VFSFromYAMLTest
, MappedFiles
) {
1488 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
1489 Lower
->addDirectory("//root/foo/bar");
1490 Lower
->addRegularFile("//root/foo/bar/a");
1491 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= getFromYAMLString(
1494 " 'type': 'directory',\n"
1495 " 'name': '//root/',\n"
1496 " 'contents': [ {\n"
1497 " 'type': 'file',\n"
1498 " 'name': 'file1',\n"
1499 " 'external-contents': '//root/foo/bar/a'\n"
1502 " 'type': 'file',\n"
1503 " 'name': 'file2',\n"
1504 " 'external-contents': '//root/foo/b'\n"
1507 " 'type': 'directory-remap',\n"
1508 " 'name': 'mappeddir',\n"
1509 " 'external-contents': '//root/foo/bar'\n"
1512 " 'type': 'directory-remap',\n"
1513 " 'name': 'mappeddir2',\n"
1514 " 'use-external-name': false,\n"
1515 " 'external-contents': '//root/foo/bar'\n"
1522 ASSERT_NE(FS
.get(), nullptr);
1524 IntrusiveRefCntPtr
<vfs::OverlayFileSystem
> O(
1525 new vfs::OverlayFileSystem(Lower
));
1529 ErrorOr
<vfs::Status
> S
= O
->status("//root/file1");
1530 ASSERT_FALSE(S
.getError());
1531 EXPECT_EQ("//root/foo/bar/a", S
->getName());
1532 EXPECT_TRUE(S
->IsVFSMapped
);
1533 EXPECT_TRUE(S
->ExposesExternalVFSPath
);
1535 ErrorOr
<vfs::Status
> SLower
= O
->status("//root/foo/bar/a");
1536 EXPECT_EQ("//root/foo/bar/a", SLower
->getName());
1537 EXPECT_TRUE(S
->equivalent(*SLower
));
1538 EXPECT_FALSE(SLower
->IsVFSMapped
);
1539 EXPECT_FALSE(SLower
->ExposesExternalVFSPath
);
1541 // file after opening
1542 auto OpenedF
= O
->openFileForRead("//root/file1");
1543 ASSERT_FALSE(OpenedF
.getError());
1544 auto OpenedS
= (*OpenedF
)->status();
1545 ASSERT_FALSE(OpenedS
.getError());
1546 EXPECT_EQ("//root/foo/bar/a", OpenedS
->getName());
1547 EXPECT_TRUE(OpenedS
->IsVFSMapped
);
1548 EXPECT_TRUE(OpenedS
->ExposesExternalVFSPath
);
1551 S
= O
->status("//root/");
1552 ASSERT_FALSE(S
.getError());
1553 EXPECT_TRUE(S
->isDirectory());
1554 EXPECT_TRUE(S
->equivalent(*O
->status("//root/"))); // non-volatile UniqueID
1556 // remapped directory
1557 S
= O
->status("//root/mappeddir");
1558 ASSERT_FALSE(S
.getError());
1559 EXPECT_TRUE(S
->isDirectory());
1560 EXPECT_TRUE(S
->IsVFSMapped
);
1561 EXPECT_TRUE(S
->ExposesExternalVFSPath
);
1562 EXPECT_TRUE(S
->equivalent(*O
->status("//root/foo/bar")));
1564 SLower
= O
->status("//root/foo/bar");
1565 EXPECT_EQ("//root/foo/bar", SLower
->getName());
1566 EXPECT_TRUE(S
->equivalent(*SLower
));
1567 EXPECT_FALSE(SLower
->IsVFSMapped
);
1568 EXPECT_FALSE(SLower
->ExposesExternalVFSPath
);
1570 // file in remapped directory
1571 S
= O
->status("//root/mappeddir/a");
1572 ASSERT_FALSE(S
.getError());
1573 EXPECT_FALSE(S
->isDirectory());
1574 EXPECT_TRUE(S
->IsVFSMapped
);
1575 EXPECT_TRUE(S
->ExposesExternalVFSPath
);
1576 EXPECT_EQ("//root/foo/bar/a", S
->getName());
1578 // file in remapped directory, with use-external-name=false
1579 S
= O
->status("//root/mappeddir2/a");
1580 ASSERT_FALSE(S
.getError());
1581 EXPECT_FALSE(S
->isDirectory());
1582 EXPECT_TRUE(S
->IsVFSMapped
);
1583 EXPECT_FALSE(S
->ExposesExternalVFSPath
);
1584 EXPECT_EQ("//root/mappeddir2/a", S
->getName());
1586 // file contents in remapped directory
1587 OpenedF
= O
->openFileForRead("//root/mappeddir/a");
1588 ASSERT_FALSE(OpenedF
.getError());
1589 OpenedS
= (*OpenedF
)->status();
1590 ASSERT_FALSE(OpenedS
.getError());
1591 EXPECT_EQ("//root/foo/bar/a", OpenedS
->getName());
1592 EXPECT_TRUE(OpenedS
->IsVFSMapped
);
1593 EXPECT_TRUE(OpenedS
->ExposesExternalVFSPath
);
1595 // file contents in remapped directory, with use-external-name=false
1596 OpenedF
= O
->openFileForRead("//root/mappeddir2/a");
1597 ASSERT_FALSE(OpenedF
.getError());
1598 OpenedS
= (*OpenedF
)->status();
1599 ASSERT_FALSE(OpenedS
.getError());
1600 EXPECT_EQ("//root/mappeddir2/a", OpenedS
->getName());
1601 EXPECT_TRUE(OpenedS
->IsVFSMapped
);
1602 EXPECT_FALSE(OpenedS
->ExposesExternalVFSPath
);
1605 EXPECT_EQ(O
->status("//root/file2").getError(),
1606 llvm::errc::no_such_file_or_directory
);
1607 EXPECT_EQ(0, NumDiagnostics
);
1610 TEST_F(VFSFromYAMLTest
, MappedRoot
) {
1611 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
1612 Lower
->addDirectory("//root/foo/bar");
1613 Lower
->addRegularFile("//root/foo/bar/a");
1614 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
=
1615 getFromYAMLString("{ 'roots': [\n"
1617 " 'type': 'directory-remap',\n"
1618 " 'name': '//mappedroot/',\n"
1619 " 'external-contents': '//root/foo/bar'\n"
1624 ASSERT_NE(FS
.get(), nullptr);
1626 IntrusiveRefCntPtr
<vfs::OverlayFileSystem
> O(
1627 new vfs::OverlayFileSystem(Lower
));
1631 ErrorOr
<vfs::Status
> S
= O
->status("//mappedroot/a");
1632 ASSERT_FALSE(S
.getError());
1633 EXPECT_EQ("//root/foo/bar/a", S
->getName());
1634 EXPECT_TRUE(S
->IsVFSMapped
);
1635 EXPECT_TRUE(S
->ExposesExternalVFSPath
);
1637 ErrorOr
<vfs::Status
> SLower
= O
->status("//root/foo/bar/a");
1638 EXPECT_EQ("//root/foo/bar/a", SLower
->getName());
1639 EXPECT_TRUE(S
->equivalent(*SLower
));
1640 EXPECT_FALSE(SLower
->IsVFSMapped
);
1641 EXPECT_FALSE(SLower
->ExposesExternalVFSPath
);
1643 // file after opening
1644 auto OpenedF
= O
->openFileForRead("//mappedroot/a");
1645 ASSERT_FALSE(OpenedF
.getError());
1646 auto OpenedS
= (*OpenedF
)->status();
1647 ASSERT_FALSE(OpenedS
.getError());
1648 EXPECT_EQ("//root/foo/bar/a", OpenedS
->getName());
1649 EXPECT_TRUE(OpenedS
->IsVFSMapped
);
1650 EXPECT_TRUE(OpenedS
->ExposesExternalVFSPath
);
1652 EXPECT_EQ(0, NumDiagnostics
);
1655 TEST_F(VFSFromYAMLTest
, RemappedDirectoryOverlay
) {
1656 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
1657 Lower
->addDirectory("//root/foo");
1658 Lower
->addRegularFile("//root/foo/a");
1659 Lower
->addDirectory("//root/bar");
1660 Lower
->addRegularFile("//root/bar/b");
1661 Lower
->addRegularFile("//root/bar/c");
1662 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
=
1663 getFromYAMLString("{ 'roots': [\n"
1665 " 'type': 'directory',\n"
1666 " 'name': '//root/',\n"
1667 " 'contents': [ {\n"
1668 " 'type': 'directory-remap',\n"
1670 " 'external-contents': '//root/foo'\n"
1675 ASSERT_NE(FS
.get(), nullptr);
1677 IntrusiveRefCntPtr
<vfs::OverlayFileSystem
> O(
1678 new vfs::OverlayFileSystem(Lower
));
1681 ErrorOr
<vfs::Status
> S
= O
->status("//root/foo");
1682 ASSERT_FALSE(S
.getError());
1684 ErrorOr
<vfs::Status
> SS
= O
->status("//root/bar");
1685 ASSERT_FALSE(SS
.getError());
1686 EXPECT_TRUE(S
->equivalent(*SS
));
1689 checkContents(O
->dir_begin("//root/bar", EC
),
1690 {"//root/foo/a", "//root/bar/b", "//root/bar/c"});
1692 Lower
->addRegularFile("//root/foo/b");
1693 checkContents(O
->dir_begin("//root/bar", EC
),
1694 {"//root/foo/a", "//root/foo/b", "//root/bar/c"});
1696 EXPECT_EQ(0, NumDiagnostics
);
1699 TEST_F(VFSFromYAMLTest
, RemappedDirectoryOverlayNoExternalNames
) {
1700 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
1701 Lower
->addDirectory("//root/foo");
1702 Lower
->addRegularFile("//root/foo/a");
1703 Lower
->addDirectory("//root/bar");
1704 Lower
->addRegularFile("//root/bar/b");
1705 Lower
->addRegularFile("//root/bar/c");
1706 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
=
1707 getFromYAMLString("{ 'use-external-names': false,\n"
1710 " 'type': 'directory',\n"
1711 " 'name': '//root/',\n"
1712 " 'contents': [ {\n"
1713 " 'type': 'directory-remap',\n"
1715 " 'external-contents': '//root/foo'\n"
1720 ASSERT_NE(FS
.get(), nullptr);
1722 ErrorOr
<vfs::Status
> S
= FS
->status("//root/foo");
1723 ASSERT_FALSE(S
.getError());
1725 ErrorOr
<vfs::Status
> SS
= FS
->status("//root/bar");
1726 ASSERT_FALSE(SS
.getError());
1727 EXPECT_TRUE(S
->equivalent(*SS
));
1730 checkContents(FS
->dir_begin("//root/bar", EC
),
1731 {"//root/bar/a", "//root/bar/b", "//root/bar/c"});
1733 Lower
->addRegularFile("//root/foo/b");
1734 checkContents(FS
->dir_begin("//root/bar", EC
),
1735 {"//root/bar/a", "//root/bar/b", "//root/bar/c"});
1737 EXPECT_EQ(0, NumDiagnostics
);
1740 TEST_F(VFSFromYAMLTest
, RemappedDirectoryOverlayNoFallthrough
) {
1741 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
1742 Lower
->addDirectory("//root/foo");
1743 Lower
->addRegularFile("//root/foo/a");
1744 Lower
->addDirectory("//root/bar");
1745 Lower
->addRegularFile("//root/bar/b");
1746 Lower
->addRegularFile("//root/bar/c");
1747 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
=
1748 getFromYAMLString("{ 'fallthrough': false,\n"
1751 " 'type': 'directory',\n"
1752 " 'name': '//root/',\n"
1753 " 'contents': [ {\n"
1754 " 'type': 'directory-remap',\n"
1756 " 'external-contents': '//root/foo'\n"
1761 ASSERT_NE(FS
.get(), nullptr);
1763 ErrorOr
<vfs::Status
> S
= Lower
->status("//root/foo");
1764 ASSERT_FALSE(S
.getError());
1766 ErrorOr
<vfs::Status
> SS
= FS
->status("//root/bar");
1767 ASSERT_FALSE(SS
.getError());
1768 EXPECT_TRUE(S
->equivalent(*SS
));
1771 checkContents(FS
->dir_begin("//root/bar", EC
), {"//root/foo/a"});
1773 Lower
->addRegularFile("//root/foo/b");
1774 checkContents(FS
->dir_begin("//root/bar", EC
),
1775 {"//root/foo/a", "//root/foo/b"});
1777 EXPECT_EQ(0, NumDiagnostics
);
1780 TEST_F(VFSFromYAMLTest
, ReturnsRequestedPathVFSMiss
) {
1781 IntrusiveRefCntPtr
<vfs::InMemoryFileSystem
> BaseFS(
1782 new vfs::InMemoryFileSystem
);
1783 BaseFS
->addFile("//root/foo/a", 0,
1784 MemoryBuffer::getMemBuffer("contents of a"));
1785 ASSERT_FALSE(BaseFS
->setCurrentWorkingDirectory("//root/foo"));
1786 auto RemappedFS
= vfs::RedirectingFileSystem::create(
1787 {}, /*UseExternalNames=*/false, *BaseFS
);
1789 auto OpenedF
= RemappedFS
->openFileForRead("a");
1790 ASSERT_FALSE(OpenedF
.getError());
1791 llvm::ErrorOr
<std::string
> Name
= (*OpenedF
)->getName();
1792 ASSERT_FALSE(Name
.getError());
1793 EXPECT_EQ("a", Name
.get());
1795 auto OpenedS
= (*OpenedF
)->status();
1796 ASSERT_FALSE(OpenedS
.getError());
1797 EXPECT_EQ("a", OpenedS
->getName());
1798 EXPECT_FALSE(OpenedS
->IsVFSMapped
);
1799 EXPECT_FALSE(OpenedS
->ExposesExternalVFSPath
);
1801 auto DirectS
= RemappedFS
->status("a");
1802 ASSERT_FALSE(DirectS
.getError());
1803 EXPECT_EQ("a", DirectS
->getName());
1804 EXPECT_FALSE(DirectS
->IsVFSMapped
);
1805 EXPECT_FALSE(DirectS
->ExposesExternalVFSPath
);
1807 EXPECT_EQ(0, NumDiagnostics
);
1810 TEST_F(VFSFromYAMLTest
, ReturnsExternalPathVFSHit
) {
1811 IntrusiveRefCntPtr
<vfs::InMemoryFileSystem
> BaseFS(
1812 new vfs::InMemoryFileSystem
);
1813 BaseFS
->addFile("//root/foo/realname", 0,
1814 MemoryBuffer::getMemBuffer("contents of a"));
1816 getFromYAMLString("{ 'use-external-names': true,\n"
1819 " 'type': 'directory',\n"
1820 " 'name': '//root/foo',\n"
1821 " 'contents': [ {\n"
1822 " 'type': 'file',\n"
1823 " 'name': 'vfsname',\n"
1824 " 'external-contents': 'realname'\n"
1829 ASSERT_FALSE(FS
->setCurrentWorkingDirectory("//root/foo"));
1831 auto OpenedF
= FS
->openFileForRead("vfsname");
1832 ASSERT_FALSE(OpenedF
.getError());
1833 llvm::ErrorOr
<std::string
> Name
= (*OpenedF
)->getName();
1834 ASSERT_FALSE(Name
.getError());
1835 EXPECT_EQ("realname", Name
.get());
1837 auto OpenedS
= (*OpenedF
)->status();
1838 ASSERT_FALSE(OpenedS
.getError());
1839 EXPECT_EQ("realname", OpenedS
->getName());
1840 EXPECT_TRUE(OpenedS
->IsVFSMapped
);
1841 EXPECT_TRUE(OpenedS
->ExposesExternalVFSPath
);
1843 auto DirectS
= FS
->status("vfsname");
1844 ASSERT_FALSE(DirectS
.getError());
1845 EXPECT_EQ("realname", DirectS
->getName());
1846 EXPECT_TRUE(DirectS
->IsVFSMapped
);
1847 EXPECT_TRUE(DirectS
->ExposesExternalVFSPath
);
1849 EXPECT_EQ(0, NumDiagnostics
);
1852 TEST_F(VFSFromYAMLTest
, ReturnsInternalPathVFSHit
) {
1853 IntrusiveRefCntPtr
<vfs::InMemoryFileSystem
> BaseFS(
1854 new vfs::InMemoryFileSystem
);
1855 BaseFS
->addFile("//root/foo/realname", 0,
1856 MemoryBuffer::getMemBuffer("contents of a"));
1858 getFromYAMLString("{ 'use-external-names': false,\n"
1861 " 'type': 'directory',\n"
1862 " 'name': '//root/foo',\n"
1863 " 'contents': [ {\n"
1864 " 'type': 'file',\n"
1865 " 'name': 'vfsname',\n"
1866 " 'external-contents': 'realname'\n"
1871 ASSERT_FALSE(FS
->setCurrentWorkingDirectory("//root/foo"));
1873 auto OpenedF
= FS
->openFileForRead("vfsname");
1874 ASSERT_FALSE(OpenedF
.getError());
1875 llvm::ErrorOr
<std::string
> Name
= (*OpenedF
)->getName();
1876 ASSERT_FALSE(Name
.getError());
1877 EXPECT_EQ("vfsname", Name
.get());
1879 auto OpenedS
= (*OpenedF
)->status();
1880 ASSERT_FALSE(OpenedS
.getError());
1881 EXPECT_EQ("vfsname", OpenedS
->getName());
1882 EXPECT_TRUE(OpenedS
->IsVFSMapped
);
1883 EXPECT_FALSE(OpenedS
->ExposesExternalVFSPath
);
1885 auto DirectS
= FS
->status("vfsname");
1886 ASSERT_FALSE(DirectS
.getError());
1887 EXPECT_EQ("vfsname", DirectS
->getName());
1888 EXPECT_TRUE(DirectS
->IsVFSMapped
);
1889 EXPECT_FALSE(DirectS
->ExposesExternalVFSPath
);
1891 EXPECT_EQ(0, NumDiagnostics
);
1894 TEST_F(VFSFromYAMLTest
, CaseInsensitive
) {
1895 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
1896 Lower
->addRegularFile("//root/foo/bar/a");
1897 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= getFromYAMLString(
1898 "{ 'case-sensitive': 'false',\n"
1901 " 'type': 'directory',\n"
1902 " 'name': '//root/',\n"
1903 " 'contents': [ {\n"
1904 " 'type': 'file',\n"
1906 " 'external-contents': '//root/foo/bar/a'\n"
1911 ASSERT_NE(FS
.get(), nullptr);
1913 IntrusiveRefCntPtr
<vfs::OverlayFileSystem
> O(
1914 new vfs::OverlayFileSystem(Lower
));
1917 ErrorOr
<vfs::Status
> S
= O
->status("//root/XX");
1918 ASSERT_FALSE(S
.getError());
1920 ErrorOr
<vfs::Status
> SS
= O
->status("//root/xx");
1921 ASSERT_FALSE(SS
.getError());
1922 EXPECT_TRUE(S
->equivalent(*SS
));
1923 SS
= O
->status("//root/xX");
1924 EXPECT_TRUE(S
->equivalent(*SS
));
1925 SS
= O
->status("//root/Xx");
1926 EXPECT_TRUE(S
->equivalent(*SS
));
1927 EXPECT_EQ(0, NumDiagnostics
);
1930 TEST_F(VFSFromYAMLTest
, CaseSensitive
) {
1931 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
1932 Lower
->addRegularFile("//root/foo/bar/a");
1933 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= getFromYAMLString(
1934 "{ 'case-sensitive': 'true',\n"
1937 " 'type': 'directory',\n"
1938 " 'name': '//root/',\n"
1939 " 'contents': [ {\n"
1940 " 'type': 'file',\n"
1942 " 'external-contents': '//root/foo/bar/a'\n"
1947 ASSERT_NE(FS
.get(), nullptr);
1949 IntrusiveRefCntPtr
<vfs::OverlayFileSystem
> O(
1950 new vfs::OverlayFileSystem(Lower
));
1953 ErrorOr
<vfs::Status
> SS
= O
->status("//root/xx");
1954 EXPECT_EQ(SS
.getError(), llvm::errc::no_such_file_or_directory
);
1955 SS
= O
->status("//root/xX");
1956 EXPECT_EQ(SS
.getError(), llvm::errc::no_such_file_or_directory
);
1957 SS
= O
->status("//root/Xx");
1958 EXPECT_EQ(SS
.getError(), llvm::errc::no_such_file_or_directory
);
1959 EXPECT_EQ(0, NumDiagnostics
);
1962 TEST_F(VFSFromYAMLTest
, IllegalVFSFile
) {
1963 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
1965 // invalid YAML at top-level
1966 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= getFromYAMLString("{]", Lower
);
1967 EXPECT_EQ(nullptr, FS
.get());
1968 // invalid YAML in roots
1969 FS
= getFromYAMLString("{ 'roots':[}", Lower
);
1970 // invalid YAML in directory
1971 FS
= getFromYAMLString(
1972 "{ 'roots':[ { 'name': 'foo', 'type': 'directory', 'contents': [}",
1974 EXPECT_EQ(nullptr, FS
.get());
1976 // invalid configuration
1977 FS
= getFromYAMLString("{ 'knobular': 'true', 'roots':[] }", Lower
);
1978 EXPECT_EQ(nullptr, FS
.get());
1979 FS
= getFromYAMLString("{ 'case-sensitive': 'maybe', 'roots':[] }", Lower
);
1980 EXPECT_EQ(nullptr, FS
.get());
1983 FS
= getFromYAMLString("{ 'roots':'' }", Lower
);
1984 EXPECT_EQ(nullptr, FS
.get());
1985 FS
= getFromYAMLString("{ 'roots':{} }", Lower
);
1986 EXPECT_EQ(nullptr, FS
.get());
1989 FS
= getFromYAMLString(
1990 "{ 'roots':[ { 'type': 'other', 'name': 'me', 'contents': '' }", Lower
);
1991 EXPECT_EQ(nullptr, FS
.get());
1992 FS
= getFromYAMLString("{ 'roots':[ { 'type': 'file', 'name': [], "
1993 "'external-contents': 'other' }",
1995 EXPECT_EQ(nullptr, FS
.get());
1996 FS
= getFromYAMLString(
1997 "{ 'roots':[ { 'type': 'file', 'name': 'me', 'external-contents': [] }",
1999 EXPECT_EQ(nullptr, FS
.get());
2000 FS
= getFromYAMLString(
2001 "{ 'roots':[ { 'type': 'file', 'name': 'me', 'external-contents': {} }",
2003 EXPECT_EQ(nullptr, FS
.get());
2004 FS
= getFromYAMLString(
2005 "{ 'roots':[ { 'type': 'directory', 'name': 'me', 'contents': {} }",
2007 EXPECT_EQ(nullptr, FS
.get());
2008 FS
= getFromYAMLString(
2009 "{ 'roots':[ { 'type': 'directory', 'name': 'me', 'contents': '' }",
2011 EXPECT_EQ(nullptr, FS
.get());
2012 FS
= getFromYAMLString(
2013 "{ 'roots':[ { 'thingy': 'directory', 'name': 'me', 'contents': [] }",
2015 EXPECT_EQ(nullptr, FS
.get());
2017 // missing mandatory fields
2018 FS
= getFromYAMLString("{ 'roots':[ { 'type': 'file', 'name': 'me' }", Lower
);
2019 EXPECT_EQ(nullptr, FS
.get());
2020 FS
= getFromYAMLString(
2021 "{ 'roots':[ { 'type': 'file', 'external-contents': 'other' }", Lower
);
2022 EXPECT_EQ(nullptr, FS
.get());
2023 FS
= getFromYAMLString("{ 'roots':[ { 'name': 'me', 'contents': [] }", Lower
);
2024 EXPECT_EQ(nullptr, FS
.get());
2027 FS
= getFromYAMLString("{ 'roots':[], 'roots':[] }", Lower
);
2028 EXPECT_EQ(nullptr, FS
.get());
2029 FS
= getFromYAMLString(
2030 "{ 'case-sensitive':'true', 'case-sensitive':'true', 'roots':[] }",
2032 EXPECT_EQ(nullptr, FS
.get());
2034 getFromYAMLString("{ 'roots':[{'name':'me', 'name':'you', 'type':'file', "
2035 "'external-contents':'blah' } ] }",
2037 EXPECT_EQ(nullptr, FS
.get());
2040 FS
= getFromYAMLRawString("{ 'roots':[] }", Lower
);
2041 EXPECT_EQ(nullptr, FS
.get());
2043 // bad version number
2044 FS
= getFromYAMLRawString("{ 'version':'foo', 'roots':[] }", Lower
);
2045 EXPECT_EQ(nullptr, FS
.get());
2046 FS
= getFromYAMLRawString("{ 'version':-1, 'roots':[] }", Lower
);
2047 EXPECT_EQ(nullptr, FS
.get());
2048 FS
= getFromYAMLRawString("{ 'version':100000, 'roots':[] }", Lower
);
2049 EXPECT_EQ(nullptr, FS
.get());
2051 // both 'external-contents' and 'contents' specified
2052 Lower
->addDirectory("//root/external/dir");
2053 FS
= getFromYAMLString(
2055 "{ 'type': 'directory', 'name': '//root/A', 'contents': [],\n"
2056 " 'external-contents': '//root/external/dir'}]}",
2058 EXPECT_EQ(nullptr, FS
.get());
2060 // 'directory-remap' with 'contents'
2061 FS
= getFromYAMLString(
2063 "{ 'type': 'directory-remap', 'name': '//root/A', 'contents': [] }]}",
2065 EXPECT_EQ(nullptr, FS
.get());
2067 // invalid redirect kind
2068 FS
= getFromYAMLString("{ 'redirecting-with': 'none', 'roots': [{\n"
2069 " 'type': 'directory-remap',\n"
2070 " 'name': '//root/A',\n"
2071 " 'external-contents': '//root/B' }]}",
2073 EXPECT_EQ(nullptr, FS
.get());
2075 // redirect and fallthrough passed
2076 FS
= getFromYAMLString("{ 'redirecting-with': 'fallthrough',\n"
2077 " 'fallthrough': true,\n"
2079 " 'type': 'directory-remap',\n"
2080 " 'name': '//root/A',\n"
2081 " 'external-contents': '//root/B' }]}",
2083 EXPECT_EQ(nullptr, FS
.get());
2085 EXPECT_EQ(28, NumDiagnostics
);
2088 TEST_F(VFSFromYAMLTest
, UseExternalName
) {
2089 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
2090 Lower
->addRegularFile("//root/external/file");
2092 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
=
2093 getFromYAMLString("{ 'roots': [\n"
2094 " { 'type': 'file', 'name': '//root/A',\n"
2095 " 'external-contents': '//root/external/file'\n"
2097 " { 'type': 'file', 'name': '//root/B',\n"
2098 " 'use-external-name': true,\n"
2099 " 'external-contents': '//root/external/file'\n"
2101 " { 'type': 'file', 'name': '//root/C',\n"
2102 " 'use-external-name': false,\n"
2103 " 'external-contents': '//root/external/file'\n"
2107 ASSERT_NE(nullptr, FS
.get());
2110 EXPECT_EQ("//root/external/file", FS
->status("//root/A")->getName());
2112 EXPECT_EQ("//root/external/file", FS
->status("//root/B")->getName());
2113 EXPECT_EQ("//root/C", FS
->status("//root/C")->getName());
2115 // global configuration
2116 FS
= getFromYAMLString("{ 'use-external-names': false,\n"
2118 " { 'type': 'file', 'name': '//root/A',\n"
2119 " 'external-contents': '//root/external/file'\n"
2121 " { 'type': 'file', 'name': '//root/B',\n"
2122 " 'use-external-name': true,\n"
2123 " 'external-contents': '//root/external/file'\n"
2125 " { 'type': 'file', 'name': '//root/C',\n"
2126 " 'use-external-name': false,\n"
2127 " 'external-contents': '//root/external/file'\n"
2131 ASSERT_NE(nullptr, FS
.get());
2134 EXPECT_EQ("//root/A", FS
->status("//root/A")->getName());
2136 EXPECT_EQ("//root/external/file", FS
->status("//root/B")->getName());
2137 EXPECT_EQ("//root/C", FS
->status("//root/C")->getName());
2140 TEST_F(VFSFromYAMLTest
, MultiComponentPath
) {
2141 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
2142 Lower
->addRegularFile("//root/other");
2145 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
=
2146 getFromYAMLString("{ 'roots': [\n"
2147 " { 'type': 'file', 'name': '//root/path/to/file',\n"
2148 " 'external-contents': '//root/other' }]\n"
2151 ASSERT_NE(nullptr, FS
.get());
2152 EXPECT_FALSE(FS
->status("//root/path/to/file").getError());
2153 EXPECT_FALSE(FS
->status("//root/path/to").getError());
2154 EXPECT_FALSE(FS
->status("//root/path").getError());
2155 EXPECT_FALSE(FS
->status("//root/").getError());
2158 FS
= getFromYAMLString(
2160 " { 'type': 'directory', 'name': '//root/path/to',\n"
2161 " 'contents': [ { 'type': 'file', 'name': 'file',\n"
2162 " 'external-contents': '//root/other' }]}]\n"
2165 ASSERT_NE(nullptr, FS
.get());
2166 EXPECT_FALSE(FS
->status("//root/path/to/file").getError());
2167 EXPECT_FALSE(FS
->status("//root/path/to").getError());
2168 EXPECT_FALSE(FS
->status("//root/path").getError());
2169 EXPECT_FALSE(FS
->status("//root/").getError());
2172 FS
= getFromYAMLString(
2174 " { 'type': 'directory', 'name': '//root/',\n"
2175 " 'contents': [ { 'type': 'file', 'name': 'path/to/file',\n"
2176 " 'external-contents': '//root/other' }]}]\n"
2179 ASSERT_NE(nullptr, FS
.get());
2180 EXPECT_FALSE(FS
->status("//root/path/to/file").getError());
2181 EXPECT_FALSE(FS
->status("//root/path/to").getError());
2182 EXPECT_FALSE(FS
->status("//root/path").getError());
2183 EXPECT_FALSE(FS
->status("//root/").getError());
2186 TEST_F(VFSFromYAMLTest
, TrailingSlashes
) {
2187 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
2188 Lower
->addRegularFile("//root/other");
2191 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= getFromYAMLString(
2193 " { 'type': 'directory', 'name': '//root/path/to////',\n"
2194 " 'contents': [ { 'type': 'file', 'name': 'file',\n"
2195 " 'external-contents': '//root/other' }]}]\n"
2198 ASSERT_NE(nullptr, FS
.get());
2199 EXPECT_FALSE(FS
->status("//root/path/to/file").getError());
2200 EXPECT_FALSE(FS
->status("//root/path/to").getError());
2201 EXPECT_FALSE(FS
->status("//root/path").getError());
2202 EXPECT_FALSE(FS
->status("//root/").getError());
2205 TEST_F(VFSFromYAMLTest
, DirectoryIteration
) {
2206 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
2207 Lower
->addDirectory("//root/");
2208 Lower
->addDirectory("//root/foo");
2209 Lower
->addDirectory("//root/foo/bar");
2210 Lower
->addRegularFile("//root/foo/bar/a");
2211 Lower
->addRegularFile("//root/foo/bar/b");
2212 Lower
->addRegularFile("//root/file3");
2213 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= getFromYAMLString(
2214 "{ 'use-external-names': false,\n"
2217 " 'type': 'directory',\n"
2218 " 'name': '//root/',\n"
2219 " 'contents': [ {\n"
2220 " 'type': 'file',\n"
2221 " 'name': 'file1',\n"
2222 " 'external-contents': '//root/foo/bar/a'\n"
2225 " 'type': 'file',\n"
2226 " 'name': 'file2',\n"
2227 " 'external-contents': '//root/foo/bar/b'\n"
2234 ASSERT_NE(FS
.get(), nullptr);
2236 IntrusiveRefCntPtr
<vfs::OverlayFileSystem
> O(
2237 new vfs::OverlayFileSystem(Lower
));
2241 checkContents(O
->dir_begin("//root/", EC
),
2242 {"//root/file1", "//root/file2", "//root/file3", "//root/foo"});
2244 checkContents(O
->dir_begin("//root/foo/bar", EC
),
2245 {"//root/foo/bar/a", "//root/foo/bar/b"});
2248 TEST_F(VFSFromYAMLTest
, DirectoryIterationSameDirMultipleEntries
) {
2249 // https://llvm.org/bugs/show_bug.cgi?id=27725
2250 if (!supportsSameDirMultipleYAMLEntries())
2253 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
2254 Lower
->addDirectory("//root/zab");
2255 Lower
->addDirectory("//root/baz");
2256 Lower
->addRegularFile("//root/zab/a");
2257 Lower
->addRegularFile("//root/zab/b");
2258 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= getFromYAMLString(
2259 "{ 'use-external-names': false,\n"
2262 " 'type': 'directory',\n"
2263 " 'name': '//root/baz/',\n"
2264 " 'contents': [ {\n"
2265 " 'type': 'file',\n"
2267 " 'external-contents': '//root/zab/a'\n"
2272 " 'type': 'directory',\n"
2273 " 'name': '//root/baz/',\n"
2274 " 'contents': [ {\n"
2275 " 'type': 'file',\n"
2277 " 'external-contents': '//root/zab/b'\n"
2284 ASSERT_NE(FS
.get(), nullptr);
2286 IntrusiveRefCntPtr
<vfs::OverlayFileSystem
> O(
2287 new vfs::OverlayFileSystem(Lower
));
2292 checkContents(O
->dir_begin("//root/baz/", EC
),
2293 {"//root/baz/x", "//root/baz/y"});
2296 TEST_F(VFSFromYAMLTest
, RecursiveDirectoryIterationLevel
) {
2298 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
2299 Lower
->addDirectory("//root/a");
2300 Lower
->addDirectory("//root/a/b");
2301 Lower
->addDirectory("//root/a/b/c");
2302 Lower
->addRegularFile("//root/a/b/c/file");
2303 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= getFromYAMLString(
2304 "{ 'use-external-names': false,\n"
2307 " 'type': 'directory',\n"
2308 " 'name': '//root/a/b/c/',\n"
2309 " 'contents': [ {\n"
2310 " 'type': 'file',\n"
2311 " 'name': 'file',\n"
2312 " 'external-contents': '//root/a/b/c/file'\n"
2319 ASSERT_NE(FS
.get(), nullptr);
2321 IntrusiveRefCntPtr
<vfs::OverlayFileSystem
> O(
2322 new vfs::OverlayFileSystem(Lower
));
2327 // Test recursive_directory_iterator level()
2328 vfs::recursive_directory_iterator I
= vfs::recursive_directory_iterator(
2332 for (int l
= 0; I
!= E
; I
.increment(EC
), ++l
) {
2334 EXPECT_EQ(I
.level(), l
);
2339 TEST_F(VFSFromYAMLTest
, RelativePaths
) {
2340 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
2342 SmallString
<128> CWD
;
2343 EC
= llvm::sys::fs::current_path(CWD
);
2346 // Filename at root level without a parent directory.
2347 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= getFromYAMLString(
2349 " { 'type': 'file', 'name': 'file-not-in-directory.h',\n"
2350 " 'external-contents': '//root/external/file'\n"
2354 ASSERT_TRUE(FS
.get() != nullptr);
2355 SmallString
<128> ExpectedPathNotInDir("file-not-in-directory.h");
2356 llvm::sys::fs::make_absolute(ExpectedPathNotInDir
);
2357 checkContents(FS
->dir_begin(CWD
, EC
), {ExpectedPathNotInDir
});
2359 // Relative file path.
2360 FS
= getFromYAMLString("{ 'roots': [\n"
2361 " { 'type': 'file', 'name': 'relative/path.h',\n"
2362 " 'external-contents': '//root/external/file'\n"
2366 ASSERT_TRUE(FS
.get() != nullptr);
2367 SmallString
<128> Parent("relative");
2368 llvm::sys::fs::make_absolute(Parent
);
2369 auto I
= FS
->dir_begin(Parent
, EC
);
2371 // Convert to POSIX path for comparison of windows paths
2372 ASSERT_EQ("relative/path.h",
2373 getPosixPath(std::string(I
->path().substr(CWD
.size() + 1))));
2375 // Relative directory path.
2376 FS
= getFromYAMLString(
2378 " { 'type': 'directory', 'name': 'relative/directory/path.h',\n"
2383 ASSERT_TRUE(FS
.get() != nullptr);
2384 SmallString
<128> Root("relative/directory");
2385 llvm::sys::fs::make_absolute(Root
);
2386 I
= FS
->dir_begin(Root
, EC
);
2388 ASSERT_EQ("path.h", std::string(I
->path().substr(Root
.size() + 1)));
2390 EXPECT_EQ(0, NumDiagnostics
);
2393 TEST_F(VFSFromYAMLTest
, NonFallthroughDirectoryIteration
) {
2394 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
2395 Lower
->addDirectory("//root/");
2396 Lower
->addRegularFile("//root/a");
2397 Lower
->addRegularFile("//root/b");
2398 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= getFromYAMLString(
2399 "{ 'use-external-names': false,\n"
2400 " 'fallthrough': false,\n"
2403 " 'type': 'directory',\n"
2404 " 'name': '//root/',\n"
2405 " 'contents': [ {\n"
2406 " 'type': 'file',\n"
2408 " 'external-contents': '//root/a'\n"
2415 ASSERT_NE(FS
.get(), nullptr);
2418 checkContents(FS
->dir_begin("//root/", EC
),
2422 TEST_F(VFSFromYAMLTest
, DirectoryIterationWithDuplicates
) {
2423 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
2424 Lower
->addDirectory("//root/");
2425 Lower
->addRegularFile("//root/a");
2426 Lower
->addRegularFile("//root/b");
2427 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= getFromYAMLString(
2428 "{ 'use-external-names': false,\n"
2431 " 'type': 'directory',\n"
2432 " 'name': '//root/',\n"
2433 " 'contents': [ {\n"
2434 " 'type': 'file',\n"
2436 " 'external-contents': '//root/a'\n"
2443 ASSERT_NE(FS
.get(), nullptr);
2446 checkContents(FS
->dir_begin("//root/", EC
),
2447 {"//root/a", "//root/b"});
2450 TEST_F(VFSFromYAMLTest
, DirectoryIterationErrorInVFSLayer
) {
2451 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
2452 Lower
->addDirectory("//root/");
2453 Lower
->addDirectory("//root/foo");
2454 Lower
->addRegularFile("//root/foo/a");
2455 Lower
->addRegularFile("//root/foo/b");
2456 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= getFromYAMLString(
2457 "{ 'use-external-names': false,\n"
2460 " 'type': 'directory',\n"
2461 " 'name': '//root/',\n"
2462 " 'contents': [ {\n"
2463 " 'type': 'file',\n"
2464 " 'name': 'bar/a',\n"
2465 " 'external-contents': '//root/foo/a'\n"
2472 ASSERT_NE(FS
.get(), nullptr);
2475 checkContents(FS
->dir_begin("//root/foo", EC
),
2476 {"//root/foo/a", "//root/foo/b"});
2479 TEST_F(VFSFromYAMLTest
, GetRealPath
) {
2480 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
2481 Lower
->addDirectory("//dir/");
2482 Lower
->addRegularFile("/foo");
2483 Lower
->addSymlink("/link");
2484 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= getFromYAMLString(
2485 "{ 'use-external-names': false,\n"
2488 " 'type': 'directory',\n"
2489 " 'name': '//root/',\n"
2490 " 'contents': [ {\n"
2491 " 'type': 'file',\n"
2493 " 'external-contents': '/link'\n"
2498 " 'type': 'directory',\n"
2499 " 'name': '//dir/',\n"
2505 ASSERT_NE(FS
.get(), nullptr);
2507 // Regular file present in underlying file system.
2508 SmallString
<16> RealPath
;
2509 EXPECT_FALSE(FS
->getRealPath("/foo", RealPath
));
2510 EXPECT_EQ(RealPath
.str(), "/foo");
2512 // File present in YAML pointing to symlink in underlying file system.
2513 EXPECT_FALSE(FS
->getRealPath("//root/bar", RealPath
));
2514 EXPECT_EQ(RealPath
.str(), "/symlink");
2516 // Directories should fall back to the underlying file system is possible.
2517 EXPECT_FALSE(FS
->getRealPath("//dir/", RealPath
));
2518 EXPECT_EQ(RealPath
.str(), "//dir/");
2520 // Try a non-existing file.
2521 EXPECT_EQ(FS
->getRealPath("/non_existing", RealPath
),
2522 errc::no_such_file_or_directory
);
2525 TEST_F(VFSFromYAMLTest
, WorkingDirectory
) {
2526 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
2527 Lower
->addDirectory("//root/");
2528 Lower
->addDirectory("//root/foo");
2529 Lower
->addRegularFile("//root/foo/a");
2530 Lower
->addRegularFile("//root/foo/b");
2531 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= getFromYAMLString(
2532 "{ 'use-external-names': false,\n"
2535 " 'type': 'directory',\n"
2536 " 'name': '//root/bar',\n"
2537 " 'contents': [ {\n"
2538 " 'type': 'file',\n"
2540 " 'external-contents': '//root/foo/a'\n"
2547 ASSERT_NE(FS
.get(), nullptr);
2548 std::error_code EC
= FS
->setCurrentWorkingDirectory("//root/bar");
2551 llvm::ErrorOr
<std::string
> WorkingDir
= FS
->getCurrentWorkingDirectory();
2552 ASSERT_TRUE(WorkingDir
);
2553 EXPECT_EQ(*WorkingDir
, "//root/bar");
2555 llvm::ErrorOr
<vfs::Status
> Status
= FS
->status("./a");
2556 ASSERT_FALSE(Status
.getError());
2557 EXPECT_TRUE(Status
->isStatusKnown());
2558 EXPECT_FALSE(Status
->isDirectory());
2559 EXPECT_TRUE(Status
->isRegularFile());
2560 EXPECT_FALSE(Status
->isSymlink());
2561 EXPECT_FALSE(Status
->isOther());
2562 EXPECT_TRUE(Status
->exists());
2564 EC
= FS
->setCurrentWorkingDirectory("bogus");
2566 WorkingDir
= FS
->getCurrentWorkingDirectory();
2567 ASSERT_TRUE(WorkingDir
);
2568 EXPECT_EQ(*WorkingDir
, "//root/bar");
2570 EC
= FS
->setCurrentWorkingDirectory("//root/");
2572 WorkingDir
= FS
->getCurrentWorkingDirectory();
2573 ASSERT_TRUE(WorkingDir
);
2574 EXPECT_EQ(*WorkingDir
, "//root/");
2576 EC
= FS
->setCurrentWorkingDirectory("bar");
2578 WorkingDir
= FS
->getCurrentWorkingDirectory();
2579 ASSERT_TRUE(WorkingDir
);
2580 EXPECT_EQ(*WorkingDir
, "//root/bar");
2583 TEST_F(VFSFromYAMLTest
, WorkingDirectoryFallthrough
) {
2584 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
2585 Lower
->addDirectory("//root/");
2586 Lower
->addDirectory("//root/foo");
2587 Lower
->addRegularFile("//root/foo/a");
2588 Lower
->addRegularFile("//root/foo/b");
2589 Lower
->addRegularFile("//root/c");
2590 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= getFromYAMLString(
2591 "{ 'use-external-names': false,\n"
2594 " 'type': 'directory',\n"
2595 " 'name': '//root/bar',\n"
2596 " 'contents': [ {\n"
2597 " 'type': 'file',\n"
2599 " 'external-contents': '//root/foo/a'\n"
2604 " 'type': 'directory',\n"
2605 " 'name': '//root/bar/baz',\n"
2606 " 'contents': [ {\n"
2607 " 'type': 'file',\n"
2609 " 'external-contents': '//root/foo/a'\n"
2616 ASSERT_NE(FS
.get(), nullptr);
2617 std::error_code EC
= FS
->setCurrentWorkingDirectory("//root/");
2619 ASSERT_NE(FS
.get(), nullptr);
2621 llvm::ErrorOr
<vfs::Status
> Status
= FS
->status("bar/a");
2622 ASSERT_FALSE(Status
.getError());
2623 EXPECT_TRUE(Status
->exists());
2625 Status
= FS
->status("foo/a");
2626 ASSERT_FALSE(Status
.getError());
2627 EXPECT_TRUE(Status
->exists());
2629 EC
= FS
->setCurrentWorkingDirectory("//root/bar");
2632 Status
= FS
->status("./a");
2633 ASSERT_FALSE(Status
.getError());
2634 EXPECT_TRUE(Status
->exists());
2636 Status
= FS
->status("./b");
2637 ASSERT_TRUE(Status
.getError());
2639 Status
= FS
->status("./c");
2640 ASSERT_TRUE(Status
.getError());
2642 EC
= FS
->setCurrentWorkingDirectory("//root/");
2645 Status
= FS
->status("c");
2646 ASSERT_FALSE(Status
.getError());
2647 EXPECT_TRUE(Status
->exists());
2649 Status
= FS
->status("./bar/baz/a");
2650 ASSERT_FALSE(Status
.getError());
2651 EXPECT_TRUE(Status
->exists());
2653 EC
= FS
->setCurrentWorkingDirectory("//root/bar");
2656 Status
= FS
->status("./baz/a");
2657 ASSERT_FALSE(Status
.getError());
2658 EXPECT_TRUE(Status
->exists());
2660 Status
= FS
->status("../bar/baz/a");
2661 ASSERT_FALSE(Status
.getError());
2662 EXPECT_TRUE(Status
->exists());
2665 TEST_F(VFSFromYAMLTest
, WorkingDirectoryFallthroughInvalid
) {
2666 IntrusiveRefCntPtr
<ErrorDummyFileSystem
> Lower(new ErrorDummyFileSystem());
2667 Lower
->addDirectory("//root/");
2668 Lower
->addDirectory("//root/foo");
2669 Lower
->addRegularFile("//root/foo/a");
2670 Lower
->addRegularFile("//root/foo/b");
2671 Lower
->addRegularFile("//root/c");
2672 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= getFromYAMLString(
2673 "{ 'use-external-names': false,\n"
2676 " 'type': 'directory',\n"
2677 " 'name': '//root/bar',\n"
2678 " 'contents': [ {\n"
2679 " 'type': 'file',\n"
2681 " 'external-contents': '//root/foo/a'\n"
2688 ASSERT_NE(FS
.get(), nullptr);
2689 std::error_code EC
= FS
->setCurrentWorkingDirectory("//root/");
2691 ASSERT_NE(FS
.get(), nullptr);
2693 llvm::ErrorOr
<vfs::Status
> Status
= FS
->status("bar/a");
2694 ASSERT_FALSE(Status
.getError());
2695 EXPECT_TRUE(Status
->exists());
2697 Status
= FS
->status("foo/a");
2698 ASSERT_FALSE(Status
.getError());
2699 EXPECT_TRUE(Status
->exists());
2702 TEST_F(VFSFromYAMLTest
, VirtualWorkingDirectory
) {
2703 IntrusiveRefCntPtr
<ErrorDummyFileSystem
> Lower(new ErrorDummyFileSystem());
2704 Lower
->addDirectory("//root/");
2705 Lower
->addDirectory("//root/foo");
2706 Lower
->addRegularFile("//root/foo/a");
2707 Lower
->addRegularFile("//root/foo/b");
2708 Lower
->addRegularFile("//root/c");
2709 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= getFromYAMLString(
2710 "{ 'use-external-names': false,\n"
2713 " 'type': 'directory',\n"
2714 " 'name': '//root/bar',\n"
2715 " 'contents': [ {\n"
2716 " 'type': 'file',\n"
2718 " 'external-contents': '//root/foo/a'\n"
2725 ASSERT_NE(FS
.get(), nullptr);
2726 std::error_code EC
= FS
->setCurrentWorkingDirectory("//root/bar");
2728 ASSERT_NE(FS
.get(), nullptr);
2730 llvm::ErrorOr
<vfs::Status
> Status
= FS
->status("a");
2731 ASSERT_FALSE(Status
.getError());
2732 EXPECT_TRUE(Status
->exists());
2735 TEST_F(VFSFromYAMLTest
, YAMLVFSWriterTest
) {
2736 TempDir
TestDirectory("virtual-file-system-test", /*Unique*/ true);
2737 TempDir
_a(TestDirectory
.path("a"));
2738 TempFile
_ab(TestDirectory
.path("a, b"));
2739 TempDir
_c(TestDirectory
.path("c"));
2740 TempFile
_cd(TestDirectory
.path("c/d"));
2741 TempDir
_e(TestDirectory
.path("e"));
2742 TempDir
_ef(TestDirectory
.path("e/f"));
2743 TempFile
_g(TestDirectory
.path("g"));
2744 TempDir
_h(TestDirectory
.path("h"));
2746 vfs::YAMLVFSWriter VFSWriter
;
2747 VFSWriter
.addDirectoryMapping(_a
.path(), "//root/a");
2748 VFSWriter
.addFileMapping(_ab
.path(), "//root/a/b");
2749 VFSWriter
.addFileMapping(_cd
.path(), "//root/c/d");
2750 VFSWriter
.addDirectoryMapping(_e
.path(), "//root/e");
2751 VFSWriter
.addDirectoryMapping(_ef
.path(), "//root/e/f");
2752 VFSWriter
.addFileMapping(_g
.path(), "//root/g");
2753 VFSWriter
.addDirectoryMapping(_h
.path(), "//root/h");
2756 raw_string_ostream
OS(Buffer
);
2757 VFSWriter
.write(OS
);
2760 IntrusiveRefCntPtr
<ErrorDummyFileSystem
> Lower(new ErrorDummyFileSystem());
2761 Lower
->addDirectory("//root/");
2762 Lower
->addDirectory("//root/a");
2763 Lower
->addRegularFile("//root/a/b");
2764 Lower
->addDirectory("//root/b");
2765 Lower
->addDirectory("//root/c");
2766 Lower
->addRegularFile("//root/c/d");
2767 Lower
->addDirectory("//root/e");
2768 Lower
->addDirectory("//root/e/f");
2769 Lower
->addRegularFile("//root/g");
2770 Lower
->addDirectory("//root/h");
2772 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= getFromYAMLRawString(Buffer
, Lower
);
2773 ASSERT_NE(FS
.get(), nullptr);
2775 EXPECT_TRUE(FS
->exists(_a
.path()));
2776 EXPECT_TRUE(FS
->exists(_ab
.path()));
2777 EXPECT_TRUE(FS
->exists(_c
.path()));
2778 EXPECT_TRUE(FS
->exists(_cd
.path()));
2779 EXPECT_TRUE(FS
->exists(_e
.path()));
2780 EXPECT_TRUE(FS
->exists(_ef
.path()));
2781 EXPECT_TRUE(FS
->exists(_g
.path()));
2782 EXPECT_TRUE(FS
->exists(_h
.path()));
2785 TEST_F(VFSFromYAMLTest
, YAMLVFSWriterTest2
) {
2786 TempDir
TestDirectory("virtual-file-system-test", /*Unique*/ true);
2787 TempDir
_a(TestDirectory
.path("a"));
2788 TempFile
_ab(TestDirectory
.path("a/b"));
2789 TempDir
_ac(TestDirectory
.path("a/c"));
2790 TempFile
_acd(TestDirectory
.path("a/c/d"));
2791 TempFile
_ace(TestDirectory
.path("a/c/e"));
2792 TempFile
_acf(TestDirectory
.path("a/c/f"));
2793 TempDir
_ag(TestDirectory
.path("a/g"));
2794 TempFile
_agh(TestDirectory
.path("a/g/h"));
2796 vfs::YAMLVFSWriter VFSWriter
;
2797 VFSWriter
.addDirectoryMapping(_a
.path(), "//root/a");
2798 VFSWriter
.addFileMapping(_ab
.path(), "//root/a/b");
2799 VFSWriter
.addDirectoryMapping(_ac
.path(), "//root/a/c");
2800 VFSWriter
.addFileMapping(_acd
.path(), "//root/a/c/d");
2801 VFSWriter
.addFileMapping(_ace
.path(), "//root/a/c/e");
2802 VFSWriter
.addFileMapping(_acf
.path(), "//root/a/c/f");
2803 VFSWriter
.addDirectoryMapping(_ag
.path(), "//root/a/g");
2804 VFSWriter
.addFileMapping(_agh
.path(), "//root/a/g/h");
2807 raw_string_ostream
OS(Buffer
);
2808 VFSWriter
.write(OS
);
2811 IntrusiveRefCntPtr
<ErrorDummyFileSystem
> Lower(new ErrorDummyFileSystem());
2812 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= getFromYAMLRawString(Buffer
, Lower
);
2813 EXPECT_NE(FS
.get(), nullptr);
2816 TEST_F(VFSFromYAMLTest
, YAMLVFSWriterTest3
) {
2817 TempDir
TestDirectory("virtual-file-system-test", /*Unique*/ true);
2818 TempDir
_a(TestDirectory
.path("a"));
2819 TempFile
_ab(TestDirectory
.path("a/b"));
2820 TempDir
_ac(TestDirectory
.path("a/c"));
2821 TempDir
_acd(TestDirectory
.path("a/c/d"));
2822 TempDir
_acde(TestDirectory
.path("a/c/d/e"));
2823 TempFile
_acdef(TestDirectory
.path("a/c/d/e/f"));
2824 TempFile
_acdeg(TestDirectory
.path("a/c/d/e/g"));
2825 TempDir
_ah(TestDirectory
.path("a/h"));
2826 TempFile
_ahi(TestDirectory
.path("a/h/i"));
2828 vfs::YAMLVFSWriter VFSWriter
;
2829 VFSWriter
.addDirectoryMapping(_a
.path(), "//root/a");
2830 VFSWriter
.addFileMapping(_ab
.path(), "//root/a/b");
2831 VFSWriter
.addDirectoryMapping(_ac
.path(), "//root/a/c");
2832 VFSWriter
.addDirectoryMapping(_acd
.path(), "//root/a/c/d");
2833 VFSWriter
.addDirectoryMapping(_acde
.path(), "//root/a/c/d/e");
2834 VFSWriter
.addFileMapping(_acdef
.path(), "//root/a/c/d/e/f");
2835 VFSWriter
.addFileMapping(_acdeg
.path(), "//root/a/c/d/e/g");
2836 VFSWriter
.addDirectoryMapping(_ahi
.path(), "//root/a/h");
2837 VFSWriter
.addFileMapping(_ahi
.path(), "//root/a/h/i");
2840 raw_string_ostream
OS(Buffer
);
2841 VFSWriter
.write(OS
);
2844 IntrusiveRefCntPtr
<ErrorDummyFileSystem
> Lower(new ErrorDummyFileSystem());
2845 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= getFromYAMLRawString(Buffer
, Lower
);
2846 EXPECT_NE(FS
.get(), nullptr);
2849 TEST_F(VFSFromYAMLTest
, YAMLVFSWriterTestHandleDirs
) {
2850 TempDir
TestDirectory("virtual-file-system-test", /*Unique*/ true);
2851 TempDir
_a(TestDirectory
.path("a"));
2852 TempDir
_b(TestDirectory
.path("b"));
2853 TempDir
_c(TestDirectory
.path("c"));
2855 vfs::YAMLVFSWriter VFSWriter
;
2856 VFSWriter
.addDirectoryMapping(_a
.path(), "//root/a");
2857 VFSWriter
.addDirectoryMapping(_b
.path(), "//root/b");
2858 VFSWriter
.addDirectoryMapping(_c
.path(), "//root/c");
2861 raw_string_ostream
OS(Buffer
);
2862 VFSWriter
.write(OS
);
2865 // We didn't add a single file - only directories.
2866 EXPECT_EQ(Buffer
.find("'type': 'file'"), std::string::npos
);
2868 IntrusiveRefCntPtr
<ErrorDummyFileSystem
> Lower(new ErrorDummyFileSystem());
2869 Lower
->addDirectory("//root/a");
2870 Lower
->addDirectory("//root/b");
2871 Lower
->addDirectory("//root/c");
2873 Lower
->addRegularFile("//root/a/a");
2874 Lower
->addRegularFile("//root/b/b");
2875 Lower
->addRegularFile("//root/c/c");
2877 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= getFromYAMLRawString(Buffer
, Lower
);
2878 ASSERT_NE(FS
.get(), nullptr);
2880 EXPECT_FALSE(FS
->exists(_a
.path("a")));
2881 EXPECT_FALSE(FS
->exists(_b
.path("b")));
2882 EXPECT_FALSE(FS
->exists(_c
.path("c")));
2885 TEST_F(VFSFromYAMLTest
, RedirectingWith
) {
2886 IntrusiveRefCntPtr
<DummyFileSystem
> Both(new DummyFileSystem());
2887 Both
->addDirectory("//root/a");
2888 Both
->addRegularFile("//root/a/f");
2889 Both
->addDirectory("//root/b");
2890 Both
->addRegularFile("//root/b/f");
2892 IntrusiveRefCntPtr
<DummyFileSystem
> AOnly(new DummyFileSystem());
2893 AOnly
->addDirectory("//root/a");
2894 AOnly
->addRegularFile("//root/a/f");
2896 IntrusiveRefCntPtr
<DummyFileSystem
> BOnly(new DummyFileSystem());
2897 BOnly
->addDirectory("//root/b");
2898 BOnly
->addRegularFile("//root/b/f");
2900 auto BaseStr
= std::string(" 'roots': [\n"
2902 " 'type': 'directory-remap',\n"
2903 " 'name': '//root/a',\n"
2904 " 'external-contents': '//root/b'\n"
2908 auto FallthroughStr
= "{ 'redirecting-with': 'fallthrough',\n" + BaseStr
;
2909 auto FallbackStr
= "{ 'redirecting-with': 'fallback',\n" + BaseStr
;
2910 auto RedirectOnlyStr
= "{ 'redirecting-with': 'redirect-only',\n" + BaseStr
;
2912 auto ExpectPath
= [&](vfs::FileSystem
&FS
, StringRef Expected
,
2913 StringRef Message
) {
2914 auto AF
= FS
.openFileForRead("//root/a/f");
2915 ASSERT_FALSE(AF
.getError()) << Message
;
2916 auto AFName
= (*AF
)->getName();
2917 ASSERT_FALSE(AFName
.getError()) << Message
;
2918 EXPECT_EQ(Expected
.str(), AFName
.get()) << Message
;
2920 auto AS
= FS
.status("//root/a/f");
2921 ASSERT_FALSE(AS
.getError()) << Message
;
2922 EXPECT_EQ(Expected
.str(), AS
->getName()) << Message
;
2925 auto ExpectFailure
= [&](vfs::FileSystem
&FS
, StringRef Message
) {
2926 EXPECT_TRUE(FS
.openFileForRead("//root/a/f").getError()) << Message
;
2927 EXPECT_TRUE(FS
.status("//root/a/f").getError()) << Message
;
2931 // `f` in both `a` and `b`
2933 // `fallthrough` tries `external-name` first, so should be `b`
2934 IntrusiveRefCntPtr
<vfs::FileSystem
> Fallthrough
=
2935 getFromYAMLString(FallthroughStr
, Both
);
2936 ASSERT_TRUE(Fallthrough
.get() != nullptr);
2937 ExpectPath(*Fallthrough
, "//root/b/f", "fallthrough, both exist");
2939 // `fallback` tries the original name first, so should be `a`
2940 IntrusiveRefCntPtr
<vfs::FileSystem
> Fallback
=
2941 getFromYAMLString(FallbackStr
, Both
);
2942 ASSERT_TRUE(Fallback
.get() != nullptr);
2943 ExpectPath(*Fallback
, "//root/a/f", "fallback, both exist");
2945 // `redirect-only` is the same as `fallthrough` but doesn't try the
2946 // original on failure, so no change here (ie. `b`)
2947 IntrusiveRefCntPtr
<vfs::FileSystem
> Redirect
=
2948 getFromYAMLString(RedirectOnlyStr
, Both
);
2949 ASSERT_TRUE(Redirect
.get() != nullptr);
2950 ExpectPath(*Redirect
, "//root/b/f", "redirect-only, both exist");
2956 // Fallthrough to the original path, `a`
2957 IntrusiveRefCntPtr
<vfs::FileSystem
> Fallthrough
=
2958 getFromYAMLString(FallthroughStr
, AOnly
);
2959 ASSERT_TRUE(Fallthrough
.get() != nullptr);
2960 ExpectPath(*Fallthrough
, "//root/a/f", "fallthrough, a only");
2962 // Original first, so still `a`
2963 IntrusiveRefCntPtr
<vfs::FileSystem
> Fallback
=
2964 getFromYAMLString(FallbackStr
, AOnly
);
2965 ASSERT_TRUE(Fallback
.get() != nullptr);
2966 ExpectPath(*Fallback
, "//root/a/f", "fallback, a only");
2968 // Fails since no fallthrough
2969 IntrusiveRefCntPtr
<vfs::FileSystem
> Redirect
=
2970 getFromYAMLString(RedirectOnlyStr
, AOnly
);
2971 ASSERT_TRUE(Redirect
.get() != nullptr);
2972 ExpectFailure(*Redirect
, "redirect-only, a only");
2978 // Tries `b` first (no fallthrough)
2979 IntrusiveRefCntPtr
<vfs::FileSystem
> Fallthrough
=
2980 getFromYAMLString(FallthroughStr
, BOnly
);
2981 ASSERT_TRUE(Fallthrough
.get() != nullptr);
2982 ExpectPath(*Fallthrough
, "//root/b/f", "fallthrough, b only");
2984 // Tries original first but then fallsback to `b`
2985 IntrusiveRefCntPtr
<vfs::FileSystem
> Fallback
=
2986 getFromYAMLString(FallbackStr
, BOnly
);
2987 ASSERT_TRUE(Fallback
.get() != nullptr);
2988 ExpectPath(*Fallback
, "//root/b/f", "fallback, b only");
2990 // Redirect exists, so uses it (`b`)
2991 IntrusiveRefCntPtr
<vfs::FileSystem
> Redirect
=
2992 getFromYAMLString(RedirectOnlyStr
, BOnly
);
2993 ASSERT_TRUE(Redirect
.get() != nullptr);
2994 ExpectPath(*Redirect
, "//root/b/f", "redirect-only, b only");
2997 EXPECT_EQ(0, NumDiagnostics
);
3000 TEST(VFSFromRemappedFilesTest
, Basic
) {
3001 IntrusiveRefCntPtr
<vfs::InMemoryFileSystem
> BaseFS
=
3002 new vfs::InMemoryFileSystem
;
3003 BaseFS
->addFile("//root/b", 0, MemoryBuffer::getMemBuffer("contents of b"));
3004 BaseFS
->addFile("//root/c", 0, MemoryBuffer::getMemBuffer("contents of c"));
3006 std::vector
<std::pair
<std::string
, std::string
>> RemappedFiles
= {
3007 {"//root/a/a", "//root/b"},
3008 {"//root/a/b/c", "//root/c"},
3010 auto RemappedFS
= vfs::RedirectingFileSystem::create(
3011 RemappedFiles
, /*UseExternalNames=*/false, *BaseFS
);
3013 auto StatA
= RemappedFS
->status("//root/a/a");
3014 auto StatB
= RemappedFS
->status("//root/a/b/c");
3017 EXPECT_EQ("//root/a/a", StatA
->getName());
3018 EXPECT_EQ("//root/a/b/c", StatB
->getName());
3020 auto BufferA
= RemappedFS
->getBufferForFile("//root/a/a");
3021 auto BufferB
= RemappedFS
->getBufferForFile("//root/a/b/c");
3022 ASSERT_TRUE(BufferA
);
3023 ASSERT_TRUE(BufferB
);
3024 EXPECT_EQ("contents of b", (*BufferA
)->getBuffer());
3025 EXPECT_EQ("contents of c", (*BufferB
)->getBuffer());
3028 TEST(VFSFromRemappedFilesTest
, UseExternalNames
) {
3029 IntrusiveRefCntPtr
<vfs::InMemoryFileSystem
> BaseFS
=
3030 new vfs::InMemoryFileSystem
;
3031 BaseFS
->addFile("//root/b", 0, MemoryBuffer::getMemBuffer("contents of b"));
3032 BaseFS
->addFile("//root/c", 0, MemoryBuffer::getMemBuffer("contents of c"));
3034 std::vector
<std::pair
<std::string
, std::string
>> RemappedFiles
= {
3035 {"//root/a/a", "//root/b"},
3036 {"//root/a/b/c", "//root/c"},
3038 auto RemappedFS
= vfs::RedirectingFileSystem::create(
3039 RemappedFiles
, /*UseExternalNames=*/true, *BaseFS
);
3041 auto StatA
= RemappedFS
->status("//root/a/a");
3042 auto StatB
= RemappedFS
->status("//root/a/b/c");
3045 EXPECT_EQ("//root/b", StatA
->getName());
3046 EXPECT_EQ("//root/c", StatB
->getName());
3048 auto BufferA
= RemappedFS
->getBufferForFile("//root/a/a");
3049 auto BufferB
= RemappedFS
->getBufferForFile("//root/a/b/c");
3050 ASSERT_TRUE(BufferA
);
3051 ASSERT_TRUE(BufferB
);
3052 EXPECT_EQ("contents of b", (*BufferA
)->getBuffer());
3053 EXPECT_EQ("contents of c", (*BufferB
)->getBuffer());
3056 TEST(VFSFromRemappedFilesTest
, LastMappingWins
) {
3057 IntrusiveRefCntPtr
<vfs::InMemoryFileSystem
> BaseFS
=
3058 new vfs::InMemoryFileSystem
;
3059 BaseFS
->addFile("//root/b", 0, MemoryBuffer::getMemBuffer("contents of b"));
3060 BaseFS
->addFile("//root/c", 0, MemoryBuffer::getMemBuffer("contents of c"));
3062 std::vector
<std::pair
<std::string
, std::string
>> RemappedFiles
= {
3063 {"//root/a", "//root/b"},
3064 {"//root/a", "//root/c"},
3066 auto RemappedFSKeepName
= vfs::RedirectingFileSystem::create(
3067 RemappedFiles
, /*UseExternalNames=*/false, *BaseFS
);
3068 auto RemappedFSExternalName
= vfs::RedirectingFileSystem::create(
3069 RemappedFiles
, /*UseExternalNames=*/true, *BaseFS
);
3071 auto StatKeepA
= RemappedFSKeepName
->status("//root/a");
3072 auto StatExternalA
= RemappedFSExternalName
->status("//root/a");
3073 ASSERT_TRUE(StatKeepA
);
3074 ASSERT_TRUE(StatExternalA
);
3075 EXPECT_EQ("//root/a", StatKeepA
->getName());
3076 EXPECT_EQ("//root/c", StatExternalA
->getName());
3078 auto BufferKeepA
= RemappedFSKeepName
->getBufferForFile("//root/a");
3079 auto BufferExternalA
= RemappedFSExternalName
->getBufferForFile("//root/a");
3080 ASSERT_TRUE(BufferKeepA
);
3081 ASSERT_TRUE(BufferExternalA
);
3082 EXPECT_EQ("contents of c", (*BufferKeepA
)->getBuffer());
3083 EXPECT_EQ("contents of c", (*BufferExternalA
)->getBuffer());
3086 TEST(RedirectingFileSystemTest
, PrintOutput
) {
3088 MemoryBuffer::getMemBuffer("{\n"
3092 " 'type': 'directory-remap',\n"
3093 " 'name': '/dremap',\n"
3094 " 'external-contents': '/a',\n"
3097 " 'type': 'directory',\n"
3098 " 'name': '/vdir',\n"
3101 " 'type': 'directory-remap',\n"
3102 " 'name': 'dremap',\n"
3103 " 'external-contents': '/b'\n"
3104 " 'use-external-name': 'true'\n"
3107 " 'type': 'file',\n"
3108 " 'name': 'vfile',\n"
3109 " 'external-contents': '/c'\n"
3110 " 'use-external-name': 'false'\n"
3115 auto Dummy
= makeIntrusiveRefCnt
<DummyFileSystem
>();
3116 auto Redirecting
= vfs::RedirectingFileSystem::create(
3117 std::move(Buffer
), nullptr, "", nullptr, Dummy
);
3119 SmallString
<0> Output
;
3120 raw_svector_ostream OuputStream
{Output
};
3122 Redirecting
->print(OuputStream
, vfs::FileSystem::PrintType::Summary
);
3123 ASSERT_EQ("RedirectingFileSystem (UseExternalNames: true)\n", Output
);
3126 Redirecting
->print(OuputStream
, vfs::FileSystem::PrintType::Contents
);
3127 ASSERT_EQ("RedirectingFileSystem (UseExternalNames: true)\n"
3129 " 'dremap' -> '/a'\n"
3131 " 'dremap' -> '/b' (UseExternalName: true)\n"
3132 " 'vfile' -> '/c' (UseExternalName: false)\n"
3134 " DummyFileSystem (Summary)\n",
3138 Redirecting
->print(OuputStream
, vfs::FileSystem::PrintType::Contents
, 1);
3139 ASSERT_EQ(" RedirectingFileSystem (UseExternalNames: true)\n"
3141 " 'dremap' -> '/a'\n"
3143 " 'dremap' -> '/b' (UseExternalName: true)\n"
3144 " 'vfile' -> '/c' (UseExternalName: false)\n"
3146 " DummyFileSystem (Summary)\n",
3150 Redirecting
->print(OuputStream
,
3151 vfs::FileSystem::PrintType::RecursiveContents
);
3152 ASSERT_EQ("RedirectingFileSystem (UseExternalNames: true)\n"
3154 " 'dremap' -> '/a'\n"
3156 " 'dremap' -> '/b' (UseExternalName: true)\n"
3157 " 'vfile' -> '/c' (UseExternalName: false)\n"
3159 " DummyFileSystem (RecursiveContents)\n",