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/ScopeExit.h"
11 #include "llvm/Config/llvm-config.h"
12 #include "llvm/Support/Errc.h"
13 #include "llvm/Support/MemoryBuffer.h"
14 #include "llvm/Support/Path.h"
15 #include "llvm/Support/SourceMgr.h"
16 #include "llvm/TargetParser/Host.h"
17 #include "llvm/TargetParser/Triple.h"
18 #include "llvm/Testing/Support/SupportHelpers.h"
19 #include "gmock/gmock.h"
20 #include "gtest/gtest.h"
25 using llvm::sys::fs::UniqueID
;
26 using llvm::unittest::TempDir
;
27 using llvm::unittest::TempFile
;
28 using llvm::unittest::TempLink
;
29 using testing::ElementsAre
;
31 using testing::UnorderedElementsAre
;
34 struct DummyFile
: public vfs::File
{
36 explicit DummyFile(vfs::Status S
) : S(S
) {}
37 llvm::ErrorOr
<vfs::Status
> status() override
{ return S
; }
38 llvm::ErrorOr
<std::unique_ptr
<llvm::MemoryBuffer
>>
39 getBuffer(const Twine
&Name
, int64_t FileSize
, bool RequiresNullTerminator
,
40 bool IsVolatile
) override
{
41 llvm_unreachable("unimplemented");
43 std::error_code
close() override
{ return std::error_code(); }
46 class DummyFileSystem
: public vfs::FileSystem
{
47 int FSID
; // used to produce UniqueIDs
48 int FileID
; // used to produce UniqueIDs
49 std::string WorkingDirectory
;
50 std::map
<std::string
, vfs::Status
> FilesAndDirs
;
51 typedef std::map
<std::string
, vfs::Status
>::const_iterator const_iterator
;
53 static int getNextFSID() {
59 DummyFileSystem() : FSID(getNextFSID()), FileID(0) {}
61 ErrorOr
<vfs::Status
> status(const Twine
&Path
) override
{
62 auto I
= findEntry(Path
);
63 if (I
== FilesAndDirs
.end())
64 return make_error_code(llvm::errc::no_such_file_or_directory
);
67 ErrorOr
<std::unique_ptr
<vfs::File
>>
68 openFileForRead(const Twine
&Path
) override
{
69 auto S
= status(Path
);
71 return std::unique_ptr
<vfs::File
>(new DummyFile
{*S
});
74 llvm::ErrorOr
<std::string
> getCurrentWorkingDirectory() const override
{
75 return WorkingDirectory
;
77 std::error_code
setCurrentWorkingDirectory(const Twine
&Path
) override
{
78 WorkingDirectory
= Path
.str();
79 return std::error_code();
81 // Map any symlink to "/symlink".
82 std::error_code
getRealPath(const Twine
&Path
,
83 SmallVectorImpl
<char> &Output
) const override
{
84 auto I
= findEntry(Path
);
85 if (I
== FilesAndDirs
.end())
86 return make_error_code(llvm::errc::no_such_file_or_directory
);
87 if (I
->second
.isSymlink()) {
89 Twine("/symlink").toVector(Output
);
90 return std::error_code();
93 Path
.toVector(Output
);
94 return std::error_code();
97 struct DirIterImpl
: public llvm::vfs::detail::DirIterImpl
{
98 std::map
<std::string
, vfs::Status
> &FilesAndDirs
;
99 std::map
<std::string
, vfs::Status
>::iterator I
;
101 bool isInPath(StringRef S
) {
102 if (Path
.size() < S
.size() && S
.find(Path
) == 0) {
103 auto LastSep
= S
.find_last_of('/');
104 if (LastSep
== Path
.size() || LastSep
== Path
.size() - 1)
109 DirIterImpl(std::map
<std::string
, vfs::Status
> &FilesAndDirs
,
111 : FilesAndDirs(FilesAndDirs
), I(FilesAndDirs
.begin()),
113 for (; I
!= FilesAndDirs
.end(); ++I
) {
114 if (isInPath(I
->first
)) {
115 CurrentEntry
= vfs::directory_entry(std::string(I
->second
.getName()),
116 I
->second
.getType());
121 std::error_code
increment() override
{
123 for (; I
!= FilesAndDirs
.end(); ++I
) {
124 if (isInPath(I
->first
)) {
125 CurrentEntry
= vfs::directory_entry(std::string(I
->second
.getName()),
126 I
->second
.getType());
130 if (I
== FilesAndDirs
.end())
131 CurrentEntry
= vfs::directory_entry();
132 return std::error_code();
136 vfs::directory_iterator
dir_begin(const Twine
&Dir
,
137 std::error_code
&EC
) override
{
138 return vfs::directory_iterator(
139 std::make_shared
<DirIterImpl
>(FilesAndDirs
, Dir
));
142 void addEntry(StringRef Path
, const vfs::Status
&Status
) {
143 FilesAndDirs
[std::string(Path
)] = Status
;
146 const_iterator
findEntry(const Twine
&Path
) const {
149 std::error_code EC
= makeAbsolute(P
);
152 return FilesAndDirs
.find(std::string(P
.str()));
155 void addRegularFile(StringRef Path
, sys::fs::perms Perms
= sys::fs::all_all
) {
156 vfs::Status
S(Path
, UniqueID(FSID
, FileID
++),
157 std::chrono::system_clock::now(), 0, 0, 1024,
158 sys::fs::file_type::regular_file
, Perms
);
162 void addDirectory(StringRef Path
, sys::fs::perms Perms
= sys::fs::all_all
) {
163 vfs::Status
S(Path
, UniqueID(FSID
, FileID
++),
164 std::chrono::system_clock::now(), 0, 0, 0,
165 sys::fs::file_type::directory_file
, Perms
);
169 void addSymlink(StringRef Path
) {
170 vfs::Status
S(Path
, UniqueID(FSID
, FileID
++),
171 std::chrono::system_clock::now(), 0, 0, 0,
172 sys::fs::file_type::symlink_file
, sys::fs::all_all
);
177 void printImpl(raw_ostream
&OS
, PrintType Type
,
178 unsigned IndentLevel
) const override
{
179 printIndent(OS
, IndentLevel
);
180 OS
<< "DummyFileSystem (";
182 case vfs::FileSystem::PrintType::Summary
:
185 case vfs::FileSystem::PrintType::Contents
:
188 case vfs::FileSystem::PrintType::RecursiveContents
:
189 OS
<< "RecursiveContents";
196 class ErrorDummyFileSystem
: public DummyFileSystem
{
197 std::error_code
setCurrentWorkingDirectory(const Twine
&Path
) override
{
198 return llvm::errc::no_such_file_or_directory
;
202 /// Replace back-slashes by front-slashes.
203 std::string
getPosixPath(const Twine
&S
) {
204 SmallString
<128> Result
;
205 llvm::sys::path::native(S
, Result
, llvm::sys::path::Style::posix
);
206 return std::string(Result
.str());
208 } // end anonymous namespace
210 TEST(VirtualFileSystemTest
, StatusQueries
) {
211 IntrusiveRefCntPtr
<DummyFileSystem
> D(new DummyFileSystem());
212 ErrorOr
<vfs::Status
> Status((std::error_code()));
214 D
->addRegularFile("/foo");
215 Status
= D
->status("/foo");
216 ASSERT_FALSE(Status
.getError());
217 EXPECT_TRUE(Status
->isStatusKnown());
218 EXPECT_FALSE(Status
->isDirectory());
219 EXPECT_TRUE(Status
->isRegularFile());
220 EXPECT_FALSE(Status
->isSymlink());
221 EXPECT_FALSE(Status
->isOther());
222 EXPECT_TRUE(Status
->exists());
224 D
->addDirectory("/bar");
225 Status
= D
->status("/bar");
226 ASSERT_FALSE(Status
.getError());
227 EXPECT_TRUE(Status
->isStatusKnown());
228 EXPECT_TRUE(Status
->isDirectory());
229 EXPECT_FALSE(Status
->isRegularFile());
230 EXPECT_FALSE(Status
->isSymlink());
231 EXPECT_FALSE(Status
->isOther());
232 EXPECT_TRUE(Status
->exists());
234 D
->addSymlink("/baz");
235 Status
= D
->status("/baz");
236 ASSERT_FALSE(Status
.getError());
237 EXPECT_TRUE(Status
->isStatusKnown());
238 EXPECT_FALSE(Status
->isDirectory());
239 EXPECT_FALSE(Status
->isRegularFile());
240 EXPECT_TRUE(Status
->isSymlink());
241 EXPECT_FALSE(Status
->isOther());
242 EXPECT_TRUE(Status
->exists());
244 EXPECT_TRUE(Status
->equivalent(*Status
));
245 ErrorOr
<vfs::Status
> Status2
= D
->status("/foo");
246 ASSERT_FALSE(Status2
.getError());
247 EXPECT_FALSE(Status
->equivalent(*Status2
));
250 TEST(VirtualFileSystemTest
, BaseOnlyOverlay
) {
251 IntrusiveRefCntPtr
<DummyFileSystem
> D(new DummyFileSystem());
252 ErrorOr
<vfs::Status
> Status((std::error_code()));
253 EXPECT_FALSE(Status
= D
->status("/foo"));
255 IntrusiveRefCntPtr
<vfs::OverlayFileSystem
> O(new vfs::OverlayFileSystem(D
));
256 EXPECT_FALSE(Status
= O
->status("/foo"));
258 D
->addRegularFile("/foo");
259 Status
= D
->status("/foo");
260 EXPECT_FALSE(Status
.getError());
262 ErrorOr
<vfs::Status
> Status2((std::error_code()));
263 Status2
= O
->status("/foo");
264 EXPECT_FALSE(Status2
.getError());
265 EXPECT_TRUE(Status
->equivalent(*Status2
));
268 TEST(VirtualFileSystemTest
, GetRealPathInOverlay
) {
269 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
270 Lower
->addRegularFile("/foo");
271 Lower
->addSymlink("/lower_link");
272 IntrusiveRefCntPtr
<DummyFileSystem
> Upper(new DummyFileSystem());
274 IntrusiveRefCntPtr
<vfs::OverlayFileSystem
> O(
275 new vfs::OverlayFileSystem(Lower
));
276 O
->pushOverlay(Upper
);
279 SmallString
<16> RealPath
;
280 EXPECT_FALSE(O
->getRealPath("/foo", RealPath
));
281 EXPECT_EQ(RealPath
.str(), "/foo");
283 // Expect no error getting real path for symlink in lower overlay.
284 EXPECT_FALSE(O
->getRealPath("/lower_link", RealPath
));
285 EXPECT_EQ(RealPath
.str(), "/symlink");
287 // Try a non-existing link.
288 EXPECT_EQ(O
->getRealPath("/upper_link", RealPath
),
289 errc::no_such_file_or_directory
);
291 // Add a new symlink in upper.
292 Upper
->addSymlink("/upper_link");
293 EXPECT_FALSE(O
->getRealPath("/upper_link", RealPath
));
294 EXPECT_EQ(RealPath
.str(), "/symlink");
297 TEST(VirtualFileSystemTest
, OverlayFiles
) {
298 IntrusiveRefCntPtr
<DummyFileSystem
> Base(new DummyFileSystem());
299 IntrusiveRefCntPtr
<DummyFileSystem
> Middle(new DummyFileSystem());
300 IntrusiveRefCntPtr
<DummyFileSystem
> Top(new DummyFileSystem());
301 IntrusiveRefCntPtr
<vfs::OverlayFileSystem
> O(
302 new vfs::OverlayFileSystem(Base
));
303 O
->pushOverlay(Middle
);
306 ErrorOr
<vfs::Status
> Status1((std::error_code())),
307 Status2((std::error_code())), Status3((std::error_code())),
308 StatusB((std::error_code())), StatusM((std::error_code())),
309 StatusT((std::error_code()));
311 Base
->addRegularFile("/foo");
312 StatusB
= Base
->status("/foo");
313 ASSERT_FALSE(StatusB
.getError());
314 Status1
= O
->status("/foo");
315 ASSERT_FALSE(Status1
.getError());
316 Middle
->addRegularFile("/foo");
317 StatusM
= Middle
->status("/foo");
318 ASSERT_FALSE(StatusM
.getError());
319 Status2
= O
->status("/foo");
320 ASSERT_FALSE(Status2
.getError());
321 Top
->addRegularFile("/foo");
322 StatusT
= Top
->status("/foo");
323 ASSERT_FALSE(StatusT
.getError());
324 Status3
= O
->status("/foo");
325 ASSERT_FALSE(Status3
.getError());
327 EXPECT_TRUE(Status1
->equivalent(*StatusB
));
328 EXPECT_TRUE(Status2
->equivalent(*StatusM
));
329 EXPECT_TRUE(Status3
->equivalent(*StatusT
));
331 EXPECT_FALSE(Status1
->equivalent(*Status2
));
332 EXPECT_FALSE(Status2
->equivalent(*Status3
));
333 EXPECT_FALSE(Status1
->equivalent(*Status3
));
336 TEST(VirtualFileSystemTest
, OverlayDirsNonMerged
) {
337 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
338 IntrusiveRefCntPtr
<DummyFileSystem
> Upper(new DummyFileSystem());
339 IntrusiveRefCntPtr
<vfs::OverlayFileSystem
> O(
340 new vfs::OverlayFileSystem(Lower
));
341 O
->pushOverlay(Upper
);
343 Lower
->addDirectory("/lower-only");
344 Upper
->addDirectory("/upper-only");
346 // non-merged paths should be the same
347 ErrorOr
<vfs::Status
> Status1
= Lower
->status("/lower-only");
348 ASSERT_FALSE(Status1
.getError());
349 ErrorOr
<vfs::Status
> Status2
= O
->status("/lower-only");
350 ASSERT_FALSE(Status2
.getError());
351 EXPECT_TRUE(Status1
->equivalent(*Status2
));
353 Status1
= Upper
->status("/upper-only");
354 ASSERT_FALSE(Status1
.getError());
355 Status2
= O
->status("/upper-only");
356 ASSERT_FALSE(Status2
.getError());
357 EXPECT_TRUE(Status1
->equivalent(*Status2
));
360 TEST(VirtualFileSystemTest
, MergedDirPermissions
) {
361 // merged directories get the permissions of the upper dir
362 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
363 IntrusiveRefCntPtr
<DummyFileSystem
> Upper(new DummyFileSystem());
364 IntrusiveRefCntPtr
<vfs::OverlayFileSystem
> O(
365 new vfs::OverlayFileSystem(Lower
));
366 O
->pushOverlay(Upper
);
368 ErrorOr
<vfs::Status
> Status((std::error_code()));
369 Lower
->addDirectory("/both", sys::fs::owner_read
);
370 Upper
->addDirectory("/both", sys::fs::owner_all
| sys::fs::group_read
);
371 Status
= O
->status("/both");
372 ASSERT_FALSE(Status
.getError());
373 EXPECT_EQ(0740, Status
->getPermissions());
375 // permissions (as usual) are not recursively applied
376 Lower
->addRegularFile("/both/foo", sys::fs::owner_read
);
377 Upper
->addRegularFile("/both/bar", sys::fs::owner_write
);
378 Status
= O
->status("/both/foo");
379 ASSERT_FALSE(Status
.getError());
380 EXPECT_EQ(0400, Status
->getPermissions());
381 Status
= O
->status("/both/bar");
382 ASSERT_FALSE(Status
.getError());
383 EXPECT_EQ(0200, Status
->getPermissions());
386 TEST(VirtualFileSystemTest
, OverlayIterator
) {
387 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
388 Lower
->addRegularFile("/foo");
389 IntrusiveRefCntPtr
<DummyFileSystem
> Upper(new DummyFileSystem());
391 IntrusiveRefCntPtr
<vfs::OverlayFileSystem
> O(
392 new vfs::OverlayFileSystem(Lower
));
393 O
->pushOverlay(Upper
);
395 ErrorOr
<vfs::Status
> Status((std::error_code()));
397 auto it
= O
->overlays_begin();
398 auto end
= O
->overlays_end();
402 Status
= (*it
)->status("/foo");
403 ASSERT_TRUE(Status
.getError());
408 Status
= (*it
)->status("/foo");
409 ASSERT_FALSE(Status
.getError());
410 EXPECT_TRUE(Status
->exists());
417 auto it
= O
->overlays_rbegin();
418 auto end
= O
->overlays_rend();
422 Status
= (*it
)->status("/foo");
423 ASSERT_FALSE(Status
.getError());
424 EXPECT_TRUE(Status
->exists());
429 Status
= (*it
)->status("/foo");
430 ASSERT_TRUE(Status
.getError());
437 TEST(VirtualFileSystemTest
, BasicRealFSIteration
) {
438 TempDir
TestDirectory("virtual-file-system-test", /*Unique*/ true);
439 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= vfs::getRealFileSystem();
442 vfs::directory_iterator I
= FS
->dir_begin(Twine(TestDirectory
.path()), EC
);
444 EXPECT_EQ(vfs::directory_iterator(), I
); // empty directory is empty
446 TempDir
_a(TestDirectory
.path("a"));
447 TempDir
_ab(TestDirectory
.path("a/b"));
448 TempDir
_c(TestDirectory
.path("c"));
449 TempDir
_cd(TestDirectory
.path("c/d"));
451 I
= FS
->dir_begin(Twine(TestDirectory
.path()), EC
);
453 ASSERT_NE(vfs::directory_iterator(), I
);
454 // Check either a or c, since we can't rely on the iteration order.
455 EXPECT_TRUE(I
->path().endswith("a") || I
->path().endswith("c"));
458 ASSERT_NE(vfs::directory_iterator(), I
);
459 EXPECT_TRUE(I
->path().endswith("a") || I
->path().endswith("c"));
461 EXPECT_EQ(vfs::directory_iterator(), I
);
465 TEST(VirtualFileSystemTest
, MultipleWorkingDirs
) {
466 // Our root contains a/aa, b/bb, c, where c is a link to a/.
467 // Run tests both in root/b/ and root/c/ (to test "normal" and symlink dirs).
468 // Interleave operations to show the working directories are independent.
469 TempDir
Root("r", /*Unique*/ true);
470 TempDir
ADir(Root
.path("a"));
471 TempDir
BDir(Root
.path("b"));
472 TempLink
C(ADir
.path(), Root
.path("c"));
473 TempFile
AA(ADir
.path("aa"), "", "aaaa");
474 TempFile
BB(BDir
.path("bb"), "", "bbbb");
475 std::unique_ptr
<vfs::FileSystem
> BFS
= vfs::createPhysicalFileSystem(),
476 CFS
= vfs::createPhysicalFileSystem();
478 ASSERT_FALSE(BFS
->setCurrentWorkingDirectory(BDir
.path()));
479 ASSERT_FALSE(CFS
->setCurrentWorkingDirectory(C
.path()));
480 EXPECT_EQ(BDir
.path(), *BFS
->getCurrentWorkingDirectory());
481 EXPECT_EQ(C
.path(), *CFS
->getCurrentWorkingDirectory());
483 // openFileForRead(), indirectly.
484 auto BBuf
= BFS
->getBufferForFile("bb");
486 EXPECT_EQ("bbbb", (*BBuf
)->getBuffer());
488 auto ABuf
= CFS
->getBufferForFile("aa");
490 EXPECT_EQ("aaaa", (*ABuf
)->getBuffer());
493 auto BStat
= BFS
->status("bb");
495 EXPECT_EQ("bb", BStat
->getName());
497 auto AStat
= CFS
->status("aa");
499 EXPECT_EQ("aa", AStat
->getName()); // unresolved name
502 SmallString
<128> BPath
;
503 ASSERT_FALSE(BFS
->getRealPath("bb", BPath
));
504 EXPECT_EQ(BB
.path(), BPath
);
506 SmallString
<128> APath
;
507 ASSERT_FALSE(CFS
->getRealPath("aa", APath
));
508 EXPECT_EQ(AA
.path(), APath
); // Reports resolved name.
512 auto BIt
= BFS
->dir_begin(".", EC
);
514 ASSERT_NE(BIt
, vfs::directory_iterator());
515 EXPECT_EQ((BDir
.path() + "/./bb").str(), BIt
->path());
518 ASSERT_EQ(BIt
, vfs::directory_iterator());
520 auto CIt
= CFS
->dir_begin(".", EC
);
522 ASSERT_NE(CIt
, vfs::directory_iterator());
523 EXPECT_EQ((ADir
.path() + "/./aa").str(),
524 CIt
->path()); // Partly resolved name!
525 CIt
.increment(EC
); // Because likely to read through this path.
527 ASSERT_EQ(CIt
, vfs::directory_iterator());
530 TEST(VirtualFileSystemTest
, PhysicalFileSystemWorkingDirFailure
) {
531 TempDir
D2("d2", /*Unique*/ true);
532 SmallString
<128> WD
, PrevWD
;
533 ASSERT_EQ(sys::fs::current_path(PrevWD
), std::error_code());
534 ASSERT_EQ(sys::fs::createUniqueDirectory("d1", WD
), std::error_code());
535 ASSERT_EQ(sys::fs::set_current_path(WD
), std::error_code());
537 llvm::make_scope_exit([&] { sys::fs::set_current_path(PrevWD
); });
539 // Delete the working directory to create an error.
540 if (sys::fs::remove_directories(WD
, /*IgnoreErrors=*/false))
541 // Some platforms (e.g. Solaris) disallow removal of the working directory.
542 GTEST_SKIP() << "test requires deletion of working directory";
544 // Verify that we still get two separate working directories.
545 auto FS1
= vfs::createPhysicalFileSystem();
546 auto FS2
= vfs::createPhysicalFileSystem();
547 ASSERT_EQ(FS1
->getCurrentWorkingDirectory().getError(),
548 errc::no_such_file_or_directory
);
549 ASSERT_EQ(FS1
->setCurrentWorkingDirectory(D2
.path()), std::error_code());
550 ASSERT_EQ(FS1
->getCurrentWorkingDirectory().get(), D2
.path());
551 EXPECT_EQ(FS2
->getCurrentWorkingDirectory().getError(),
552 errc::no_such_file_or_directory
);
553 SmallString
<128> WD2
;
554 EXPECT_EQ(sys::fs::current_path(WD2
), errc::no_such_file_or_directory
);
557 TEST(VirtualFileSystemTest
, BrokenSymlinkRealFSIteration
) {
558 TempDir
TestDirectory("virtual-file-system-test", /*Unique*/ true);
559 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= vfs::getRealFileSystem();
561 TempLink
_a("no_such_file", TestDirectory
.path("a"));
562 TempDir
_b(TestDirectory
.path("b"));
563 TempLink
_c("no_such_file", TestDirectory
.path("c"));
565 // Should get no iteration error, but a stat error for the broken symlinks.
566 std::map
<std::string
, std::error_code
> StatResults
;
568 for (vfs::directory_iterator
569 I
= FS
->dir_begin(Twine(TestDirectory
.path()), EC
),
571 I
!= E
; I
.increment(EC
)) {
573 StatResults
[std::string(sys::path::filename(I
->path()))] =
574 FS
->status(I
->path()).getError();
579 Pair("a", std::make_error_code(std::errc::no_such_file_or_directory
)),
580 Pair("b", std::error_code()),
582 std::make_error_code(std::errc::no_such_file_or_directory
))));
586 TEST(VirtualFileSystemTest
, BasicRealFSRecursiveIteration
) {
587 TempDir
TestDirectory("virtual-file-system-test", /*Unique*/ true);
588 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= vfs::getRealFileSystem();
592 vfs::recursive_directory_iterator(*FS
, Twine(TestDirectory
.path()), EC
);
594 EXPECT_EQ(vfs::recursive_directory_iterator(), I
); // empty directory is empty
596 TempDir
_a(TestDirectory
.path("a"));
597 TempDir
_ab(TestDirectory
.path("a/b"));
598 TempDir
_c(TestDirectory
.path("c"));
599 TempDir
_cd(TestDirectory
.path("c/d"));
601 I
= vfs::recursive_directory_iterator(*FS
, Twine(TestDirectory
.path()), EC
);
603 ASSERT_NE(vfs::recursive_directory_iterator(), I
);
605 std::vector
<std::string
> Contents
;
606 for (auto E
= vfs::recursive_directory_iterator(); !EC
&& I
!= E
;
608 Contents
.push_back(std::string(I
->path()));
611 // Check contents, which may be in any order
612 EXPECT_EQ(4U, Contents
.size());
613 int Counts
[4] = {0, 0, 0, 0};
614 for (const std::string
&Name
: Contents
) {
615 ASSERT_FALSE(Name
.empty());
616 int Index
= Name
[Name
.size() - 1] - 'a';
621 EXPECT_EQ(1, Counts
[0]); // a
622 EXPECT_EQ(1, Counts
[1]); // b
623 EXPECT_EQ(1, Counts
[2]); // c
624 EXPECT_EQ(1, Counts
[3]); // d
627 TEST(VirtualFileSystemTest
, BasicRealFSRecursiveIterationNoPush
) {
628 TempDir
TestDirectory("virtual-file-system-test", /*Unique*/ true);
630 TempDir
_a(TestDirectory
.path("a"));
631 TempDir
_ab(TestDirectory
.path("a/b"));
632 TempDir
_c(TestDirectory
.path("c"));
633 TempDir
_cd(TestDirectory
.path("c/d"));
634 TempDir
_e(TestDirectory
.path("e"));
635 TempDir
_ef(TestDirectory
.path("e/f"));
636 TempDir
_g(TestDirectory
.path("g"));
638 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= vfs::getRealFileSystem();
640 // Test that calling no_push on entries without subdirectories has no effect.
644 vfs::recursive_directory_iterator(*FS
, Twine(TestDirectory
.path()), EC
);
647 std::vector
<std::string
> Contents
;
648 for (auto E
= vfs::recursive_directory_iterator(); !EC
&& I
!= E
;
650 Contents
.push_back(std::string(I
->path()));
651 char last
= I
->path().back();
663 EXPECT_EQ(7U, Contents
.size());
666 // Test that calling no_push skips subdirectories.
670 vfs::recursive_directory_iterator(*FS
, Twine(TestDirectory
.path()), EC
);
673 std::vector
<std::string
> Contents
;
674 for (auto E
= vfs::recursive_directory_iterator(); !EC
&& I
!= E
;
676 Contents
.push_back(std::string(I
->path()));
677 char last
= I
->path().back();
689 // Check contents, which may be in any order
690 EXPECT_EQ(4U, Contents
.size());
691 int Counts
[7] = {0, 0, 0, 0, 0, 0, 0};
692 for (const std::string
&Name
: Contents
) {
693 ASSERT_FALSE(Name
.empty());
694 int Index
= Name
[Name
.size() - 1] - 'a';
699 EXPECT_EQ(1, Counts
[0]); // a
700 EXPECT_EQ(0, Counts
[1]); // b
701 EXPECT_EQ(1, Counts
[2]); // c
702 EXPECT_EQ(0, Counts
[3]); // d
703 EXPECT_EQ(1, Counts
[4]); // e
704 EXPECT_EQ(0, Counts
[5]); // f
705 EXPECT_EQ(1, Counts
[6]); // g
710 TEST(VirtualFileSystemTest
, BrokenSymlinkRealFSRecursiveIteration
) {
711 TempDir
TestDirectory("virtual-file-system-test", /*Unique*/ true);
712 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= vfs::getRealFileSystem();
714 TempLink
_a("no_such_file", TestDirectory
.path("a"));
715 TempDir
_b(TestDirectory
.path("b"));
716 TempLink
_ba("no_such_file", TestDirectory
.path("b/a"));
717 TempDir
_bb(TestDirectory
.path("b/b"));
718 TempLink
_bc("no_such_file", TestDirectory
.path("b/c"));
719 TempLink
_c("no_such_file", TestDirectory
.path("c"));
720 TempDir
_d(TestDirectory
.path("d"));
721 TempDir
_dd(TestDirectory
.path("d/d"));
722 TempDir
_ddd(TestDirectory
.path("d/d/d"));
723 TempLink
_e("no_such_file", TestDirectory
.path("e"));
725 std::vector
<std::string
> VisitedBrokenSymlinks
;
726 std::vector
<std::string
> VisitedNonBrokenSymlinks
;
728 for (vfs::recursive_directory_iterator
729 I(*FS
, Twine(TestDirectory
.path()), EC
),
731 I
!= E
; I
.increment(EC
)) {
733 (FS
->status(I
->path()) ? VisitedNonBrokenSymlinks
: VisitedBrokenSymlinks
)
734 .push_back(std::string(I
->path()));
737 // Check visited file names.
738 EXPECT_THAT(VisitedBrokenSymlinks
,
739 UnorderedElementsAre(_a
.path().str(), _ba
.path().str(),
740 _bc
.path().str(), _c
.path().str(),
742 EXPECT_THAT(VisitedNonBrokenSymlinks
,
743 UnorderedElementsAre(_b
.path().str(), _bb
.path().str(),
744 _d
.path().str(), _dd
.path().str(),
749 template <typename DirIter
>
750 static void checkContents(DirIter I
, ArrayRef
<StringRef
> ExpectedOut
) {
752 SmallVector
<StringRef
, 4> Expected(ExpectedOut
.begin(), ExpectedOut
.end());
753 SmallVector
<std::string
, 4> InputToCheck
;
755 // Do not rely on iteration order to check for contents, sort both
756 // content vectors before comparison.
757 for (DirIter E
; !EC
&& I
!= E
; I
.increment(EC
))
758 InputToCheck
.push_back(std::string(I
->path()));
760 llvm::sort(InputToCheck
);
761 llvm::sort(Expected
);
762 EXPECT_EQ(InputToCheck
.size(), Expected
.size());
764 unsigned LastElt
= std::min(InputToCheck
.size(), Expected
.size());
765 for (unsigned Idx
= 0; Idx
!= LastElt
; ++Idx
)
766 EXPECT_EQ(StringRef(InputToCheck
[Idx
]), Expected
[Idx
]);
769 TEST(VirtualFileSystemTest
, OverlayIteration
) {
770 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
771 IntrusiveRefCntPtr
<DummyFileSystem
> Upper(new DummyFileSystem());
772 IntrusiveRefCntPtr
<vfs::OverlayFileSystem
> O(
773 new vfs::OverlayFileSystem(Lower
));
774 O
->pushOverlay(Upper
);
777 checkContents(O
->dir_begin("/", EC
), ArrayRef
<StringRef
>());
779 Lower
->addRegularFile("/file1");
780 checkContents(O
->dir_begin("/", EC
), ArrayRef
<StringRef
>("/file1"));
782 Upper
->addRegularFile("/file2");
783 checkContents(O
->dir_begin("/", EC
), {"/file2", "/file1"});
785 Lower
->addDirectory("/dir1");
786 Lower
->addRegularFile("/dir1/foo");
787 Upper
->addDirectory("/dir2");
788 Upper
->addRegularFile("/dir2/foo");
789 checkContents(O
->dir_begin("/dir2", EC
), ArrayRef
<StringRef
>("/dir2/foo"));
790 checkContents(O
->dir_begin("/", EC
), {"/dir2", "/file2", "/dir1", "/file1"});
793 TEST(VirtualFileSystemTest
, OverlayRecursiveIteration
) {
794 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
795 IntrusiveRefCntPtr
<DummyFileSystem
> Middle(new DummyFileSystem());
796 IntrusiveRefCntPtr
<DummyFileSystem
> Upper(new DummyFileSystem());
797 IntrusiveRefCntPtr
<vfs::OverlayFileSystem
> O(
798 new vfs::OverlayFileSystem(Lower
));
799 O
->pushOverlay(Middle
);
800 O
->pushOverlay(Upper
);
803 checkContents(vfs::recursive_directory_iterator(*O
, "/", EC
),
804 ArrayRef
<StringRef
>());
806 Lower
->addRegularFile("/file1");
807 checkContents(vfs::recursive_directory_iterator(*O
, "/", EC
),
808 ArrayRef
<StringRef
>("/file1"));
810 Upper
->addDirectory("/dir");
811 Upper
->addRegularFile("/dir/file2");
812 checkContents(vfs::recursive_directory_iterator(*O
, "/", EC
),
813 {"/dir", "/dir/file2", "/file1"});
815 Lower
->addDirectory("/dir1");
816 Lower
->addRegularFile("/dir1/foo");
817 Lower
->addDirectory("/dir1/a");
818 Lower
->addRegularFile("/dir1/a/b");
819 Middle
->addDirectory("/a");
820 Middle
->addDirectory("/a/b");
821 Middle
->addDirectory("/a/b/c");
822 Middle
->addRegularFile("/a/b/c/d");
823 Middle
->addRegularFile("/hiddenByUp");
824 Upper
->addDirectory("/dir2");
825 Upper
->addRegularFile("/dir2/foo");
826 Upper
->addRegularFile("/hiddenByUp");
827 checkContents(vfs::recursive_directory_iterator(*O
, "/dir2", EC
),
828 ArrayRef
<StringRef
>("/dir2/foo"));
829 checkContents(vfs::recursive_directory_iterator(*O
, "/", EC
),
830 {"/dir", "/dir/file2", "/dir2", "/dir2/foo", "/hiddenByUp",
831 "/a", "/a/b", "/a/b/c", "/a/b/c/d", "/dir1", "/dir1/a",
832 "/dir1/a/b", "/dir1/foo", "/file1"});
835 TEST(VirtualFileSystemTest
, ThreeLevelIteration
) {
836 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
837 IntrusiveRefCntPtr
<DummyFileSystem
> Middle(new DummyFileSystem());
838 IntrusiveRefCntPtr
<DummyFileSystem
> Upper(new DummyFileSystem());
839 IntrusiveRefCntPtr
<vfs::OverlayFileSystem
> O(
840 new vfs::OverlayFileSystem(Lower
));
841 O
->pushOverlay(Middle
);
842 O
->pushOverlay(Upper
);
845 checkContents(O
->dir_begin("/", EC
), ArrayRef
<StringRef
>());
847 Middle
->addRegularFile("/file2");
848 checkContents(O
->dir_begin("/", EC
), ArrayRef
<StringRef
>("/file2"));
850 Lower
->addRegularFile("/file1");
851 Upper
->addRegularFile("/file3");
852 checkContents(O
->dir_begin("/", EC
), {"/file3", "/file2", "/file1"});
855 TEST(VirtualFileSystemTest
, HiddenInIteration
) {
856 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
857 IntrusiveRefCntPtr
<DummyFileSystem
> Middle(new DummyFileSystem());
858 IntrusiveRefCntPtr
<DummyFileSystem
> Upper(new DummyFileSystem());
859 IntrusiveRefCntPtr
<vfs::OverlayFileSystem
> O(
860 new vfs::OverlayFileSystem(Lower
));
861 O
->pushOverlay(Middle
);
862 O
->pushOverlay(Upper
);
865 Lower
->addRegularFile("/onlyInLow");
866 Lower
->addDirectory("/hiddenByMid");
867 Lower
->addDirectory("/hiddenByUp");
868 Middle
->addRegularFile("/onlyInMid");
869 Middle
->addRegularFile("/hiddenByMid");
870 Middle
->addDirectory("/hiddenByUp");
871 Upper
->addRegularFile("/onlyInUp");
872 Upper
->addRegularFile("/hiddenByUp");
874 O
->dir_begin("/", EC
),
875 {"/hiddenByUp", "/onlyInUp", "/hiddenByMid", "/onlyInMid", "/onlyInLow"});
877 // Make sure we get the top-most entry
880 vfs::directory_iterator I
= O
->dir_begin("/", EC
), E
;
881 for (; !EC
&& I
!= E
; I
.increment(EC
))
882 if (I
->path() == "/hiddenByUp")
885 EXPECT_EQ(sys::fs::file_type::regular_file
, I
->type());
889 vfs::directory_iterator I
= O
->dir_begin("/", EC
), E
;
890 for (; !EC
&& I
!= E
; I
.increment(EC
))
891 if (I
->path() == "/hiddenByMid")
894 EXPECT_EQ(sys::fs::file_type::regular_file
, I
->type());
898 TEST(OverlayFileSystemTest
, PrintOutput
) {
899 auto Dummy
= makeIntrusiveRefCnt
<DummyFileSystem
>();
900 auto Overlay1
= makeIntrusiveRefCnt
<vfs::OverlayFileSystem
>(Dummy
);
901 Overlay1
->pushOverlay(Dummy
);
902 auto Overlay2
= makeIntrusiveRefCnt
<vfs::OverlayFileSystem
>(Overlay1
);
903 Overlay2
->pushOverlay(Dummy
);
905 SmallString
<0> Output
;
906 raw_svector_ostream OuputStream
{Output
};
908 Overlay2
->print(OuputStream
, vfs::FileSystem::PrintType::Summary
);
909 ASSERT_EQ("OverlayFileSystem\n", Output
);
912 Overlay2
->print(OuputStream
, vfs::FileSystem::PrintType::Contents
);
913 ASSERT_EQ("OverlayFileSystem\n"
914 " DummyFileSystem (Summary)\n"
915 " OverlayFileSystem\n",
919 Overlay2
->print(OuputStream
, vfs::FileSystem::PrintType::RecursiveContents
);
920 ASSERT_EQ("OverlayFileSystem\n"
921 " DummyFileSystem (RecursiveContents)\n"
922 " OverlayFileSystem\n"
923 " DummyFileSystem (RecursiveContents)\n"
924 " DummyFileSystem (RecursiveContents)\n",
928 TEST(ProxyFileSystemTest
, Basic
) {
929 IntrusiveRefCntPtr
<vfs::InMemoryFileSystem
> Base(
930 new vfs::InMemoryFileSystem());
931 vfs::ProxyFileSystem
PFS(Base
);
933 Base
->addFile("/a", 0, MemoryBuffer::getMemBuffer("test"));
935 auto Stat
= PFS
.status("/a");
936 ASSERT_FALSE(Stat
.getError());
938 auto File
= PFS
.openFileForRead("/a");
939 ASSERT_FALSE(File
.getError());
940 EXPECT_EQ("test", (*(*File
)->getBuffer("ignored"))->getBuffer());
943 vfs::directory_iterator I
= PFS
.dir_begin("/", EC
);
945 ASSERT_EQ("/a", I
->path());
948 ASSERT_EQ(vfs::directory_iterator(), I
);
950 ASSERT_FALSE(PFS
.setCurrentWorkingDirectory("/"));
952 auto PWD
= PFS
.getCurrentWorkingDirectory();
953 ASSERT_FALSE(PWD
.getError());
954 ASSERT_EQ("/", getPosixPath(*PWD
));
956 SmallString
<16> Path
;
957 ASSERT_FALSE(PFS
.getRealPath("a", Path
));
958 ASSERT_EQ("/a", getPosixPath(Path
));
961 ASSERT_FALSE(PFS
.isLocal("/a", Local
));
965 class InMemoryFileSystemTest
: public ::testing::Test
{
967 llvm::vfs::InMemoryFileSystem FS
;
968 llvm::vfs::InMemoryFileSystem NormalizedFS
;
970 InMemoryFileSystemTest()
971 : FS(/*UseNormalizedPaths=*/false),
972 NormalizedFS(/*UseNormalizedPaths=*/true) {}
975 MATCHER_P2(IsHardLinkTo
, FS
, Target
, "") {
976 StringRef From
= arg
;
977 StringRef To
= Target
;
978 auto OpenedFrom
= FS
->openFileForRead(From
);
979 auto OpenedTo
= FS
->openFileForRead(To
);
980 return !OpenedFrom
.getError() && !OpenedTo
.getError() &&
981 (*OpenedFrom
)->status()->getUniqueID() ==
982 (*OpenedTo
)->status()->getUniqueID();
985 TEST_F(InMemoryFileSystemTest
, IsEmpty
) {
986 auto Stat
= FS
.status("/a");
987 ASSERT_EQ(Stat
.getError(), errc::no_such_file_or_directory
) << FS
.toString();
988 Stat
= FS
.status("/");
989 ASSERT_EQ(Stat
.getError(), errc::no_such_file_or_directory
) << FS
.toString();
992 TEST_F(InMemoryFileSystemTest
, WindowsPath
) {
993 FS
.addFile("c:/windows/system128/foo.cpp", 0, MemoryBuffer::getMemBuffer(""));
994 auto Stat
= FS
.status("c:");
996 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << FS
.toString();
998 Stat
= FS
.status("c:/windows/system128/foo.cpp");
999 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << FS
.toString();
1000 FS
.addFile("d:/windows/foo.cpp", 0, MemoryBuffer::getMemBuffer(""));
1001 Stat
= FS
.status("d:/windows/foo.cpp");
1002 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << FS
.toString();
1005 TEST_F(InMemoryFileSystemTest
, OverlayFile
) {
1006 FS
.addFile("/a", 0, MemoryBuffer::getMemBuffer("a"));
1007 NormalizedFS
.addFile("/a", 0, MemoryBuffer::getMemBuffer("a"));
1008 auto Stat
= FS
.status("/");
1009 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << FS
.toString();
1010 Stat
= FS
.status("/.");
1012 Stat
= NormalizedFS
.status("/.");
1013 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << FS
.toString();
1014 Stat
= FS
.status("/a");
1015 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n" << FS
.toString();
1016 ASSERT_EQ("/a", Stat
->getName());
1019 TEST_F(InMemoryFileSystemTest
, OverlayFileNoOwn
) {
1020 auto Buf
= MemoryBuffer::getMemBuffer("a");
1021 FS
.addFileNoOwn("/a", 0, *Buf
);
1022 auto Stat
= FS
.status("/a");
1023 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n" << FS
.toString();
1024 ASSERT_EQ("/a", Stat
->getName());
1027 TEST_F(InMemoryFileSystemTest
, OpenFileForRead
) {
1028 FS
.addFile("/a", 0, MemoryBuffer::getMemBuffer("a"));
1029 FS
.addFile("././c", 0, MemoryBuffer::getMemBuffer("c"));
1030 FS
.addFile("./d/../d", 0, MemoryBuffer::getMemBuffer("d"));
1031 NormalizedFS
.addFile("/a", 0, MemoryBuffer::getMemBuffer("a"));
1032 NormalizedFS
.addFile("././c", 0, MemoryBuffer::getMemBuffer("c"));
1033 NormalizedFS
.addFile("./d/../d", 0, MemoryBuffer::getMemBuffer("d"));
1034 auto File
= FS
.openFileForRead("/a");
1035 ASSERT_EQ("a", (*(*File
)->getBuffer("ignored"))->getBuffer());
1036 File
= FS
.openFileForRead("/a"); // Open again.
1037 ASSERT_EQ("a", (*(*File
)->getBuffer("ignored"))->getBuffer());
1038 File
= NormalizedFS
.openFileForRead("/././a"); // Open again.
1039 ASSERT_EQ("a", (*(*File
)->getBuffer("ignored"))->getBuffer());
1040 File
= FS
.openFileForRead("/");
1041 ASSERT_EQ(File
.getError(), errc::invalid_argument
) << FS
.toString();
1042 File
= FS
.openFileForRead("/b");
1043 ASSERT_EQ(File
.getError(), errc::no_such_file_or_directory
) << FS
.toString();
1044 File
= FS
.openFileForRead("./c");
1046 File
= FS
.openFileForRead("e/../d");
1048 File
= NormalizedFS
.openFileForRead("./c");
1049 ASSERT_EQ("c", (*(*File
)->getBuffer("ignored"))->getBuffer());
1050 File
= NormalizedFS
.openFileForRead("e/../d");
1051 ASSERT_EQ("d", (*(*File
)->getBuffer("ignored"))->getBuffer());
1054 TEST_F(InMemoryFileSystemTest
, DuplicatedFile
) {
1055 ASSERT_TRUE(FS
.addFile("/a", 0, MemoryBuffer::getMemBuffer("a")));
1056 ASSERT_FALSE(FS
.addFile("/a/b", 0, MemoryBuffer::getMemBuffer("a")));
1057 ASSERT_TRUE(FS
.addFile("/a", 0, MemoryBuffer::getMemBuffer("a")));
1058 ASSERT_FALSE(FS
.addFile("/a", 0, MemoryBuffer::getMemBuffer("b")));
1061 TEST_F(InMemoryFileSystemTest
, DirectoryIteration
) {
1062 FS
.addFile("/a", 0, MemoryBuffer::getMemBuffer(""));
1063 FS
.addFile("/b/c", 0, MemoryBuffer::getMemBuffer(""));
1066 vfs::directory_iterator I
= FS
.dir_begin("/", EC
);
1068 ASSERT_EQ("/a", I
->path());
1071 ASSERT_EQ("/b", I
->path());
1074 ASSERT_EQ(vfs::directory_iterator(), I
);
1076 I
= FS
.dir_begin("/b", EC
);
1078 // When on Windows, we end up with "/b\\c" as the name. Convert to Posix
1079 // path for the sake of the comparison.
1080 ASSERT_EQ("/b/c", getPosixPath(std::string(I
->path())));
1083 ASSERT_EQ(vfs::directory_iterator(), I
);
1086 TEST_F(InMemoryFileSystemTest
, WorkingDirectory
) {
1087 FS
.setCurrentWorkingDirectory("/b");
1088 FS
.addFile("c", 0, MemoryBuffer::getMemBuffer(""));
1090 auto Stat
= FS
.status("/b/c");
1091 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n" << FS
.toString();
1092 ASSERT_EQ("/b/c", Stat
->getName());
1093 ASSERT_EQ("/b", *FS
.getCurrentWorkingDirectory());
1095 Stat
= FS
.status("c");
1096 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n" << FS
.toString();
1098 NormalizedFS
.setCurrentWorkingDirectory("/b/c");
1099 NormalizedFS
.setCurrentWorkingDirectory(".");
1101 getPosixPath(NormalizedFS
.getCurrentWorkingDirectory().get()));
1102 NormalizedFS
.setCurrentWorkingDirectory("..");
1104 getPosixPath(NormalizedFS
.getCurrentWorkingDirectory().get()));
1107 TEST_F(InMemoryFileSystemTest
, IsLocal
) {
1108 FS
.setCurrentWorkingDirectory("/b");
1109 FS
.addFile("c", 0, MemoryBuffer::getMemBuffer(""));
1112 bool IsLocal
= true;
1113 EC
= FS
.isLocal("c", IsLocal
);
1115 ASSERT_FALSE(IsLocal
);
1118 #if !defined(_WIN32)
1119 TEST_F(InMemoryFileSystemTest
, GetRealPath
) {
1120 SmallString
<16> Path
;
1121 EXPECT_EQ(FS
.getRealPath("b", Path
), errc::operation_not_permitted
);
1123 auto GetRealPath
= [this](StringRef P
) {
1124 SmallString
<16> Output
;
1125 auto EC
= FS
.getRealPath(P
, Output
);
1127 return std::string(Output
);
1130 FS
.setCurrentWorkingDirectory("a");
1131 EXPECT_EQ(GetRealPath("b"), "a/b");
1132 EXPECT_EQ(GetRealPath("../b"), "b");
1133 EXPECT_EQ(GetRealPath("b/./c"), "a/b/c");
1135 FS
.setCurrentWorkingDirectory("/a");
1136 EXPECT_EQ(GetRealPath("b"), "/a/b");
1137 EXPECT_EQ(GetRealPath("../b"), "/b");
1138 EXPECT_EQ(GetRealPath("b/./c"), "/a/b/c");
1142 TEST_F(InMemoryFileSystemTest
, AddFileWithUser
) {
1143 FS
.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"), 0xFEEDFACE);
1144 auto Stat
= FS
.status("/a");
1145 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n" << FS
.toString();
1146 ASSERT_TRUE(Stat
->isDirectory());
1147 ASSERT_EQ(0xFEEDFACE, Stat
->getUser());
1148 Stat
= FS
.status("/a/b");
1149 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n" << FS
.toString();
1150 ASSERT_TRUE(Stat
->isDirectory());
1151 ASSERT_EQ(0xFEEDFACE, Stat
->getUser());
1152 Stat
= FS
.status("/a/b/c");
1153 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n" << FS
.toString();
1154 ASSERT_TRUE(Stat
->isRegularFile());
1155 ASSERT_EQ(sys::fs::perms::all_all
, Stat
->getPermissions());
1156 ASSERT_EQ(0xFEEDFACE, Stat
->getUser());
1159 TEST_F(InMemoryFileSystemTest
, AddFileWithGroup
) {
1160 FS
.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"), std::nullopt
,
1162 auto Stat
= FS
.status("/a");
1163 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n" << FS
.toString();
1164 ASSERT_TRUE(Stat
->isDirectory());
1165 ASSERT_EQ(0xDABBAD00, Stat
->getGroup());
1166 Stat
= FS
.status("/a/b");
1167 ASSERT_TRUE(Stat
->isDirectory());
1168 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n" << FS
.toString();
1169 ASSERT_EQ(0xDABBAD00, Stat
->getGroup());
1170 Stat
= FS
.status("/a/b/c");
1171 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n" << FS
.toString();
1172 ASSERT_TRUE(Stat
->isRegularFile());
1173 ASSERT_EQ(sys::fs::perms::all_all
, Stat
->getPermissions());
1174 ASSERT_EQ(0xDABBAD00, Stat
->getGroup());
1177 TEST_F(InMemoryFileSystemTest
, AddFileWithFileType
) {
1178 FS
.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"), std::nullopt
,
1179 std::nullopt
, sys::fs::file_type::socket_file
);
1180 auto Stat
= FS
.status("/a");
1181 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n" << FS
.toString();
1182 ASSERT_TRUE(Stat
->isDirectory());
1183 Stat
= FS
.status("/a/b");
1184 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n" << FS
.toString();
1185 ASSERT_TRUE(Stat
->isDirectory());
1186 Stat
= FS
.status("/a/b/c");
1187 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n" << FS
.toString();
1188 ASSERT_EQ(sys::fs::file_type::socket_file
, Stat
->getType());
1189 ASSERT_EQ(sys::fs::perms::all_all
, Stat
->getPermissions());
1192 TEST_F(InMemoryFileSystemTest
, AddFileWithPerms
) {
1193 FS
.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"), std::nullopt
,
1194 std::nullopt
, std::nullopt
,
1195 sys::fs::perms::owner_read
| sys::fs::perms::owner_write
);
1196 auto Stat
= FS
.status("/a");
1197 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n" << FS
.toString();
1198 ASSERT_TRUE(Stat
->isDirectory());
1199 ASSERT_EQ(sys::fs::perms::owner_read
| sys::fs::perms::owner_write
|
1200 sys::fs::perms::owner_exe
,
1201 Stat
->getPermissions());
1202 Stat
= FS
.status("/a/b");
1203 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n" << FS
.toString();
1204 ASSERT_TRUE(Stat
->isDirectory());
1205 ASSERT_EQ(sys::fs::perms::owner_read
| sys::fs::perms::owner_write
|
1206 sys::fs::perms::owner_exe
,
1207 Stat
->getPermissions());
1208 Stat
= FS
.status("/a/b/c");
1209 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n" << FS
.toString();
1210 ASSERT_TRUE(Stat
->isRegularFile());
1211 ASSERT_EQ(sys::fs::perms::owner_read
| sys::fs::perms::owner_write
,
1212 Stat
->getPermissions());
1215 TEST_F(InMemoryFileSystemTest
, AddDirectoryThenAddChild
) {
1216 FS
.addFile("/a", 0, MemoryBuffer::getMemBuffer(""), /*User=*/std::nullopt
,
1217 /*Group=*/std::nullopt
, sys::fs::file_type::directory_file
);
1218 FS
.addFile("/a/b", 0, MemoryBuffer::getMemBuffer("abc"),
1219 /*User=*/std::nullopt
,
1220 /*Group=*/std::nullopt
, sys::fs::file_type::regular_file
);
1221 auto Stat
= FS
.status("/a");
1222 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n" << FS
.toString();
1223 ASSERT_TRUE(Stat
->isDirectory());
1224 Stat
= FS
.status("/a/b");
1225 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n" << FS
.toString();
1226 ASSERT_TRUE(Stat
->isRegularFile());
1229 // Test that the name returned by status() is in the same form as the path that
1230 // was requested (to match the behavior of RealFileSystem).
1231 TEST_F(InMemoryFileSystemTest
, StatusName
) {
1232 NormalizedFS
.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"),
1233 /*User=*/std::nullopt
,
1234 /*Group=*/std::nullopt
,
1235 sys::fs::file_type::regular_file
);
1236 NormalizedFS
.setCurrentWorkingDirectory("/a/b");
1238 // Access using InMemoryFileSystem::status.
1239 auto Stat
= NormalizedFS
.status("../b/c");
1240 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n"
1241 << NormalizedFS
.toString();
1242 ASSERT_TRUE(Stat
->isRegularFile());
1243 ASSERT_EQ("../b/c", Stat
->getName());
1245 // Access using InMemoryFileAdaptor::status.
1246 auto File
= NormalizedFS
.openFileForRead("../b/c");
1247 ASSERT_FALSE(File
.getError()) << File
.getError() << "\n"
1248 << NormalizedFS
.toString();
1249 Stat
= (*File
)->status();
1250 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n"
1251 << NormalizedFS
.toString();
1252 ASSERT_TRUE(Stat
->isRegularFile());
1253 ASSERT_EQ("../b/c", Stat
->getName());
1255 // Access using a directory iterator.
1257 llvm::vfs::directory_iterator It
= NormalizedFS
.dir_begin("../b", EC
);
1258 // When on Windows, we end up with "../b\\c" as the name. Convert to Posix
1259 // path for the sake of the comparison.
1260 ASSERT_EQ("../b/c", getPosixPath(std::string(It
->path())));
1263 TEST_F(InMemoryFileSystemTest
, AddHardLinkToFile
) {
1264 StringRef FromLink
= "/path/to/FROM/link";
1265 StringRef Target
= "/path/to/TO/file";
1266 FS
.addFile(Target
, 0, MemoryBuffer::getMemBuffer("content of target"));
1267 EXPECT_TRUE(FS
.addHardLink(FromLink
, Target
));
1268 EXPECT_THAT(FromLink
, IsHardLinkTo(&FS
, Target
));
1269 EXPECT_EQ(FS
.status(FromLink
)->getSize(), FS
.status(Target
)->getSize());
1270 EXPECT_EQ(FS
.getBufferForFile(FromLink
)->get()->getBuffer(),
1271 FS
.getBufferForFile(Target
)->get()->getBuffer());
1274 TEST_F(InMemoryFileSystemTest
, AddHardLinkInChainPattern
) {
1275 StringRef Link0
= "/path/to/0/link";
1276 StringRef Link1
= "/path/to/1/link";
1277 StringRef Link2
= "/path/to/2/link";
1278 StringRef Target
= "/path/to/target";
1279 FS
.addFile(Target
, 0, MemoryBuffer::getMemBuffer("content of target file"));
1280 EXPECT_TRUE(FS
.addHardLink(Link2
, Target
));
1281 EXPECT_TRUE(FS
.addHardLink(Link1
, Link2
));
1282 EXPECT_TRUE(FS
.addHardLink(Link0
, Link1
));
1283 EXPECT_THAT(Link0
, IsHardLinkTo(&FS
, Target
));
1284 EXPECT_THAT(Link1
, IsHardLinkTo(&FS
, Target
));
1285 EXPECT_THAT(Link2
, IsHardLinkTo(&FS
, Target
));
1288 TEST_F(InMemoryFileSystemTest
, AddHardLinkToAFileThatWasNotAddedBefore
) {
1289 EXPECT_FALSE(FS
.addHardLink("/path/to/link", "/path/to/target"));
1292 TEST_F(InMemoryFileSystemTest
, AddHardLinkFromAFileThatWasAddedBefore
) {
1293 StringRef Link
= "/path/to/link";
1294 StringRef Target
= "/path/to/target";
1295 FS
.addFile(Target
, 0, MemoryBuffer::getMemBuffer("content of target"));
1296 FS
.addFile(Link
, 0, MemoryBuffer::getMemBuffer("content of link"));
1297 EXPECT_FALSE(FS
.addHardLink(Link
, Target
));
1300 TEST_F(InMemoryFileSystemTest
, AddSameHardLinkMoreThanOnce
) {
1301 StringRef Link
= "/path/to/link";
1302 StringRef Target
= "/path/to/target";
1303 FS
.addFile(Target
, 0, MemoryBuffer::getMemBuffer("content of target"));
1304 EXPECT_TRUE(FS
.addHardLink(Link
, Target
));
1305 EXPECT_FALSE(FS
.addHardLink(Link
, Target
));
1308 TEST_F(InMemoryFileSystemTest
, AddFileInPlaceOfAHardLinkWithSameContent
) {
1309 StringRef Link
= "/path/to/link";
1310 StringRef Target
= "/path/to/target";
1311 StringRef Content
= "content of target";
1312 EXPECT_TRUE(FS
.addFile(Target
, 0, MemoryBuffer::getMemBuffer(Content
)));
1313 EXPECT_TRUE(FS
.addHardLink(Link
, Target
));
1314 EXPECT_TRUE(FS
.addFile(Link
, 0, MemoryBuffer::getMemBuffer(Content
)));
1317 TEST_F(InMemoryFileSystemTest
, AddFileInPlaceOfAHardLinkWithDifferentContent
) {
1318 StringRef Link
= "/path/to/link";
1319 StringRef Target
= "/path/to/target";
1320 StringRef Content
= "content of target";
1321 StringRef LinkContent
= "different content of link";
1322 EXPECT_TRUE(FS
.addFile(Target
, 0, MemoryBuffer::getMemBuffer(Content
)));
1323 EXPECT_TRUE(FS
.addHardLink(Link
, Target
));
1324 EXPECT_FALSE(FS
.addFile(Link
, 0, MemoryBuffer::getMemBuffer(LinkContent
)));
1327 TEST_F(InMemoryFileSystemTest
, AddHardLinkToADirectory
) {
1328 StringRef Dir
= "path/to/dummy/dir";
1329 StringRef Link
= "/path/to/link";
1330 StringRef File
= "path/to/dummy/dir/target";
1331 StringRef Content
= "content of target";
1332 EXPECT_TRUE(FS
.addFile(File
, 0, MemoryBuffer::getMemBuffer(Content
)));
1333 EXPECT_FALSE(FS
.addHardLink(Link
, Dir
));
1336 TEST_F(InMemoryFileSystemTest
, AddHardLinkToASymlink
) {
1337 EXPECT_TRUE(FS
.addFile("/file", 0, MemoryBuffer::getMemBuffer("content")));
1338 EXPECT_TRUE(FS
.addSymbolicLink("/symlink", "/file", 0));
1339 EXPECT_TRUE(FS
.addHardLink("/hardlink", "/symlink"));
1340 EXPECT_EQ((*FS
.getBufferForFile("/hardlink"))->getBuffer(), "content");
1343 TEST_F(InMemoryFileSystemTest
, AddHardLinkFromADirectory
) {
1344 StringRef Dir
= "path/to/dummy/dir";
1345 StringRef Target
= "path/to/dummy/dir/target";
1346 StringRef Content
= "content of target";
1347 EXPECT_TRUE(FS
.addFile(Target
, 0, MemoryBuffer::getMemBuffer(Content
)));
1348 EXPECT_FALSE(FS
.addHardLink(Dir
, Target
));
1351 TEST_F(InMemoryFileSystemTest
, AddHardLinkUnderAFile
) {
1352 StringRef CommonContent
= "content string";
1353 FS
.addFile("/a/b", 0, MemoryBuffer::getMemBuffer(CommonContent
));
1354 FS
.addFile("/c/d", 0, MemoryBuffer::getMemBuffer(CommonContent
));
1355 EXPECT_FALSE(FS
.addHardLink("/c/d/e", "/a/b"));
1358 TEST_F(InMemoryFileSystemTest
, RecursiveIterationWithHardLink
) {
1360 FS
.addFile("/a/b", 0, MemoryBuffer::getMemBuffer("content string"));
1361 EXPECT_TRUE(FS
.addHardLink("/c/d", "/a/b"));
1362 auto I
= vfs::recursive_directory_iterator(FS
, "/", EC
);
1364 std::vector
<std::string
> Nodes
;
1365 for (auto E
= vfs::recursive_directory_iterator(); !EC
&& I
!= E
;
1367 Nodes
.push_back(getPosixPath(std::string(I
->path())));
1369 EXPECT_THAT(Nodes
, testing::UnorderedElementsAre("/a", "/a/b", "/c", "/c/d"));
1372 TEST_F(InMemoryFileSystemTest
, UniqueID
) {
1373 ASSERT_TRUE(FS
.addFile("/a/b", 0, MemoryBuffer::getMemBuffer("text")));
1374 ASSERT_TRUE(FS
.addFile("/c/d", 0, MemoryBuffer::getMemBuffer("text")));
1375 ASSERT_TRUE(FS
.addHardLink("/e/f", "/a/b"));
1377 EXPECT_EQ(FS
.status("/a/b")->getUniqueID(), FS
.status("/a/b")->getUniqueID());
1378 EXPECT_NE(FS
.status("/a/b")->getUniqueID(), FS
.status("/c/d")->getUniqueID());
1379 EXPECT_EQ(FS
.status("/a/b")->getUniqueID(), FS
.status("/e/f")->getUniqueID());
1380 EXPECT_EQ(FS
.status("/a")->getUniqueID(), FS
.status("/a")->getUniqueID());
1381 EXPECT_NE(FS
.status("/a")->getUniqueID(), FS
.status("/c")->getUniqueID());
1382 EXPECT_NE(FS
.status("/a")->getUniqueID(), FS
.status("/e")->getUniqueID());
1384 // Recreating the "same" FS yields the same UniqueIDs.
1385 // Note: FS2 should match FS with respect to path normalization.
1386 vfs::InMemoryFileSystem
FS2(/*UseNormalizedPath=*/false);
1387 ASSERT_TRUE(FS2
.addFile("/a/b", 0, MemoryBuffer::getMemBuffer("text")));
1388 EXPECT_EQ(FS
.status("/a/b")->getUniqueID(),
1389 FS2
.status("/a/b")->getUniqueID());
1390 EXPECT_EQ(FS
.status("/a")->getUniqueID(), FS2
.status("/a")->getUniqueID());
1393 TEST_F(InMemoryFileSystemTest
, AddSymlinkToAFile
) {
1395 FS
.addFile("/some/file", 0, MemoryBuffer::getMemBuffer("contents")));
1396 EXPECT_TRUE(FS
.addSymbolicLink("/other/file/link", "/some/file", 0));
1397 ErrorOr
<vfs::Status
> Stat
= FS
.status("/some/file");
1398 EXPECT_TRUE(Stat
->isRegularFile());
1401 TEST_F(InMemoryFileSystemTest
, AddSymlinkToADirectory
) {
1402 EXPECT_TRUE(FS
.addSymbolicLink("/link", "/target", 0));
1404 FS
.addFile("/target/foo.h", 0, MemoryBuffer::getMemBuffer("foo")));
1405 ErrorOr
<vfs::Status
> Stat
= FS
.status("/link/foo.h");
1407 EXPECT_EQ((*Stat
).getName(), "/link/foo.h");
1408 EXPECT_TRUE(Stat
->isRegularFile());
1411 TEST_F(InMemoryFileSystemTest
, AddSymlinkToASymlink
) {
1412 EXPECT_TRUE(FS
.addSymbolicLink("/first", "/second", 0));
1413 EXPECT_TRUE(FS
.addSymbolicLink("/second", "/third", 0));
1414 EXPECT_TRUE(FS
.addFile("/third", 0, MemoryBuffer::getMemBuffer("")));
1415 ErrorOr
<vfs::Status
> Stat
= FS
.status("/first");
1417 EXPECT_EQ((*Stat
).getName(), "/first");
1418 // Follow-through symlinks by default. This matches RealFileSystem's
1420 EXPECT_TRUE(Stat
->isRegularFile());
1421 Stat
= FS
.status("/second");
1423 EXPECT_EQ((*Stat
).getName(), "/second");
1424 EXPECT_TRUE(Stat
->isRegularFile());
1425 Stat
= FS
.status("/third");
1427 EXPECT_EQ((*Stat
).getName(), "/third");
1428 EXPECT_TRUE(Stat
->isRegularFile());
1431 TEST_F(InMemoryFileSystemTest
, AddRecursiveSymlink
) {
1432 EXPECT_TRUE(FS
.addSymbolicLink("/link-a", "/link-b", 0));
1433 EXPECT_TRUE(FS
.addSymbolicLink("/link-b", "/link-a", 0));
1434 ErrorOr
<vfs::Status
> Stat
= FS
.status("/link-a/foo");
1436 EXPECT_EQ(Stat
.getError(), errc::no_such_file_or_directory
);
1439 TEST_F(InMemoryFileSystemTest
, DirectoryIteratorWithSymlinkToAFile
) {
1442 EXPECT_TRUE(FS
.addFile("/file", 0, MemoryBuffer::getMemBuffer("")));
1443 EXPECT_TRUE(FS
.addSymbolicLink("/symlink", "/file", 0));
1445 vfs::directory_iterator I
= FS
.dir_begin("/", EC
), E
;
1448 std::vector
<std::string
> Nodes
;
1449 for (; !EC
&& I
!= E
; I
.increment(EC
))
1450 Nodes
.push_back(getPosixPath(std::string(I
->path())));
1452 EXPECT_THAT(Nodes
, testing::UnorderedElementsAre("/file", "/file"));
1455 TEST_F(InMemoryFileSystemTest
, RecursiveDirectoryIteratorWithSymlinkToADir
) {
1458 EXPECT_TRUE(FS
.addFile("/dir/file", 0, MemoryBuffer::getMemBuffer("")));
1459 EXPECT_TRUE(FS
.addSymbolicLink("/dir_symlink", "/dir", 0));
1461 vfs::recursive_directory_iterator
I(FS
, "/", EC
), E
;
1464 std::vector
<std::string
> Nodes
;
1465 for (; !EC
&& I
!= E
; I
.increment(EC
))
1466 Nodes
.push_back(getPosixPath(std::string(I
->path())));
1468 EXPECT_THAT(Nodes
, testing::UnorderedElementsAre("/dir", "/dir/file", "/dir",
1472 // NOTE: in the tests below, we use '//root/' as our root directory, since it is
1473 // a legal *absolute* path on Windows as well as *nix.
1474 class VFSFromYAMLTest
: public ::testing::Test
{
1478 void SetUp() override
{ NumDiagnostics
= 0; }
1480 static void CountingDiagHandler(const SMDiagnostic
&, void *Context
) {
1481 VFSFromYAMLTest
*Test
= static_cast<VFSFromYAMLTest
*>(Context
);
1482 ++Test
->NumDiagnostics
;
1485 std::unique_ptr
<vfs::FileSystem
>
1486 getFromYAMLRawString(StringRef Content
,
1487 IntrusiveRefCntPtr
<vfs::FileSystem
> ExternalFS
,
1488 StringRef YAMLFilePath
= "") {
1489 std::unique_ptr
<MemoryBuffer
> Buffer
= MemoryBuffer::getMemBuffer(Content
);
1490 return getVFSFromYAML(std::move(Buffer
), CountingDiagHandler
, YAMLFilePath
,
1494 std::unique_ptr
<vfs::FileSystem
> getFromYAMLString(
1496 IntrusiveRefCntPtr
<vfs::FileSystem
> ExternalFS
= new DummyFileSystem(),
1497 StringRef YAMLFilePath
= "") {
1498 std::string
VersionPlusContent("{\n 'version':0,\n");
1499 VersionPlusContent
+= Content
.slice(Content
.find('{') + 1, StringRef::npos
);
1500 return getFromYAMLRawString(VersionPlusContent
, ExternalFS
, YAMLFilePath
);
1503 // This is intended as a "XFAIL" for windows hosts.
1504 bool supportsSameDirMultipleYAMLEntries() {
1505 Triple
Host(Triple::normalize(sys::getProcessTriple()));
1506 return !Host
.isOSWindows();
1510 TEST_F(VFSFromYAMLTest
, BasicVFSFromYAML
) {
1511 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
;
1512 FS
= getFromYAMLString("");
1513 EXPECT_EQ(nullptr, FS
.get());
1514 FS
= getFromYAMLString("[]");
1515 EXPECT_EQ(nullptr, FS
.get());
1516 FS
= getFromYAMLString("'string'");
1517 EXPECT_EQ(nullptr, FS
.get());
1518 EXPECT_EQ(3, NumDiagnostics
);
1521 TEST_F(VFSFromYAMLTest
, MappedFiles
) {
1522 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
1523 Lower
->addDirectory("//root/foo/bar");
1524 Lower
->addRegularFile("//root/foo/bar/a");
1525 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= getFromYAMLString(
1528 " 'type': 'directory',\n"
1529 " 'name': '//root/',\n"
1530 " 'contents': [ {\n"
1531 " 'type': 'file',\n"
1532 " 'name': 'file1',\n"
1533 " 'external-contents': '//root/foo/bar/a'\n"
1536 " 'type': 'file',\n"
1537 " 'name': 'file2',\n"
1538 " 'external-contents': '//root/foo/b'\n"
1541 " 'type': 'directory-remap',\n"
1542 " 'name': 'mappeddir',\n"
1543 " 'external-contents': '//root/foo/bar'\n"
1546 " 'type': 'directory-remap',\n"
1547 " 'name': 'mappeddir2',\n"
1548 " 'use-external-name': false,\n"
1549 " 'external-contents': '//root/foo/bar'\n"
1556 ASSERT_NE(FS
.get(), nullptr);
1558 IntrusiveRefCntPtr
<vfs::OverlayFileSystem
> O(
1559 new vfs::OverlayFileSystem(Lower
));
1563 ErrorOr
<vfs::Status
> S
= O
->status("//root/file1");
1564 ASSERT_FALSE(S
.getError());
1565 EXPECT_EQ("//root/foo/bar/a", S
->getName());
1566 EXPECT_TRUE(S
->IsVFSMapped
);
1567 EXPECT_TRUE(S
->ExposesExternalVFSPath
);
1569 ErrorOr
<vfs::Status
> SLower
= O
->status("//root/foo/bar/a");
1570 EXPECT_EQ("//root/foo/bar/a", SLower
->getName());
1571 EXPECT_TRUE(S
->equivalent(*SLower
));
1572 EXPECT_FALSE(SLower
->IsVFSMapped
);
1573 EXPECT_FALSE(SLower
->ExposesExternalVFSPath
);
1575 // file after opening
1576 auto OpenedF
= O
->openFileForRead("//root/file1");
1577 ASSERT_FALSE(OpenedF
.getError());
1578 auto OpenedS
= (*OpenedF
)->status();
1579 ASSERT_FALSE(OpenedS
.getError());
1580 EXPECT_EQ("//root/foo/bar/a", OpenedS
->getName());
1581 EXPECT_TRUE(OpenedS
->IsVFSMapped
);
1582 EXPECT_TRUE(OpenedS
->ExposesExternalVFSPath
);
1585 S
= O
->status("//root/");
1586 ASSERT_FALSE(S
.getError());
1587 EXPECT_TRUE(S
->isDirectory());
1588 EXPECT_TRUE(S
->equivalent(*O
->status("//root/"))); // non-volatile UniqueID
1590 // remapped directory
1591 S
= O
->status("//root/mappeddir");
1592 ASSERT_FALSE(S
.getError());
1593 EXPECT_TRUE(S
->isDirectory());
1594 EXPECT_TRUE(S
->IsVFSMapped
);
1595 EXPECT_TRUE(S
->ExposesExternalVFSPath
);
1596 EXPECT_TRUE(S
->equivalent(*O
->status("//root/foo/bar")));
1598 SLower
= O
->status("//root/foo/bar");
1599 EXPECT_EQ("//root/foo/bar", SLower
->getName());
1600 EXPECT_TRUE(S
->equivalent(*SLower
));
1601 EXPECT_FALSE(SLower
->IsVFSMapped
);
1602 EXPECT_FALSE(SLower
->ExposesExternalVFSPath
);
1604 // file in remapped directory
1605 S
= O
->status("//root/mappeddir/a");
1606 ASSERT_FALSE(S
.getError());
1607 EXPECT_FALSE(S
->isDirectory());
1608 EXPECT_TRUE(S
->IsVFSMapped
);
1609 EXPECT_TRUE(S
->ExposesExternalVFSPath
);
1610 EXPECT_EQ("//root/foo/bar/a", S
->getName());
1612 // file in remapped directory, with use-external-name=false
1613 S
= O
->status("//root/mappeddir2/a");
1614 ASSERT_FALSE(S
.getError());
1615 EXPECT_FALSE(S
->isDirectory());
1616 EXPECT_TRUE(S
->IsVFSMapped
);
1617 EXPECT_FALSE(S
->ExposesExternalVFSPath
);
1618 EXPECT_EQ("//root/mappeddir2/a", S
->getName());
1620 // file contents in remapped directory
1621 OpenedF
= O
->openFileForRead("//root/mappeddir/a");
1622 ASSERT_FALSE(OpenedF
.getError());
1623 OpenedS
= (*OpenedF
)->status();
1624 ASSERT_FALSE(OpenedS
.getError());
1625 EXPECT_EQ("//root/foo/bar/a", OpenedS
->getName());
1626 EXPECT_TRUE(OpenedS
->IsVFSMapped
);
1627 EXPECT_TRUE(OpenedS
->ExposesExternalVFSPath
);
1629 // file contents in remapped directory, with use-external-name=false
1630 OpenedF
= O
->openFileForRead("//root/mappeddir2/a");
1631 ASSERT_FALSE(OpenedF
.getError());
1632 OpenedS
= (*OpenedF
)->status();
1633 ASSERT_FALSE(OpenedS
.getError());
1634 EXPECT_EQ("//root/mappeddir2/a", OpenedS
->getName());
1635 EXPECT_TRUE(OpenedS
->IsVFSMapped
);
1636 EXPECT_FALSE(OpenedS
->ExposesExternalVFSPath
);
1639 EXPECT_EQ(O
->status("//root/file2").getError(),
1640 llvm::errc::no_such_file_or_directory
);
1641 EXPECT_EQ(0, NumDiagnostics
);
1644 TEST_F(VFSFromYAMLTest
, MappedRoot
) {
1645 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
1646 Lower
->addDirectory("//root/foo/bar");
1647 Lower
->addRegularFile("//root/foo/bar/a");
1648 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
=
1649 getFromYAMLString("{ 'roots': [\n"
1651 " 'type': 'directory-remap',\n"
1652 " 'name': '//mappedroot/',\n"
1653 " 'external-contents': '//root/foo/bar'\n"
1658 ASSERT_NE(FS
.get(), nullptr);
1660 IntrusiveRefCntPtr
<vfs::OverlayFileSystem
> O(
1661 new vfs::OverlayFileSystem(Lower
));
1665 ErrorOr
<vfs::Status
> S
= O
->status("//mappedroot/a");
1666 ASSERT_FALSE(S
.getError());
1667 EXPECT_EQ("//root/foo/bar/a", S
->getName());
1668 EXPECT_TRUE(S
->IsVFSMapped
);
1669 EXPECT_TRUE(S
->ExposesExternalVFSPath
);
1671 ErrorOr
<vfs::Status
> SLower
= O
->status("//root/foo/bar/a");
1672 EXPECT_EQ("//root/foo/bar/a", SLower
->getName());
1673 EXPECT_TRUE(S
->equivalent(*SLower
));
1674 EXPECT_FALSE(SLower
->IsVFSMapped
);
1675 EXPECT_FALSE(SLower
->ExposesExternalVFSPath
);
1677 // file after opening
1678 auto OpenedF
= O
->openFileForRead("//mappedroot/a");
1679 ASSERT_FALSE(OpenedF
.getError());
1680 auto OpenedS
= (*OpenedF
)->status();
1681 ASSERT_FALSE(OpenedS
.getError());
1682 EXPECT_EQ("//root/foo/bar/a", OpenedS
->getName());
1683 EXPECT_TRUE(OpenedS
->IsVFSMapped
);
1684 EXPECT_TRUE(OpenedS
->ExposesExternalVFSPath
);
1686 EXPECT_EQ(0, NumDiagnostics
);
1689 TEST_F(VFSFromYAMLTest
, RemappedDirectoryOverlay
) {
1690 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
1691 Lower
->addDirectory("//root/foo");
1692 Lower
->addRegularFile("//root/foo/a");
1693 Lower
->addDirectory("//root/bar");
1694 Lower
->addRegularFile("//root/bar/b");
1695 Lower
->addRegularFile("//root/bar/c");
1696 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
=
1697 getFromYAMLString("{ 'roots': [\n"
1699 " 'type': 'directory',\n"
1700 " 'name': '//root/',\n"
1701 " 'contents': [ {\n"
1702 " 'type': 'directory-remap',\n"
1704 " 'external-contents': '//root/foo'\n"
1709 ASSERT_NE(FS
.get(), nullptr);
1711 IntrusiveRefCntPtr
<vfs::OverlayFileSystem
> O(
1712 new vfs::OverlayFileSystem(Lower
));
1715 ErrorOr
<vfs::Status
> S
= O
->status("//root/foo");
1716 ASSERT_FALSE(S
.getError());
1718 ErrorOr
<vfs::Status
> SS
= O
->status("//root/bar");
1719 ASSERT_FALSE(SS
.getError());
1720 EXPECT_TRUE(S
->equivalent(*SS
));
1723 checkContents(O
->dir_begin("//root/bar", EC
),
1724 {"//root/foo/a", "//root/bar/b", "//root/bar/c"});
1726 Lower
->addRegularFile("//root/foo/b");
1727 checkContents(O
->dir_begin("//root/bar", EC
),
1728 {"//root/foo/a", "//root/foo/b", "//root/bar/c"});
1730 EXPECT_EQ(0, NumDiagnostics
);
1733 TEST_F(VFSFromYAMLTest
, RemappedDirectoryOverlayNoExternalNames
) {
1734 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
1735 Lower
->addDirectory("//root/foo");
1736 Lower
->addRegularFile("//root/foo/a");
1737 Lower
->addDirectory("//root/bar");
1738 Lower
->addRegularFile("//root/bar/b");
1739 Lower
->addRegularFile("//root/bar/c");
1740 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
=
1741 getFromYAMLString("{ 'use-external-names': false,\n"
1744 " 'type': 'directory',\n"
1745 " 'name': '//root/',\n"
1746 " 'contents': [ {\n"
1747 " 'type': 'directory-remap',\n"
1749 " 'external-contents': '//root/foo'\n"
1754 ASSERT_NE(FS
.get(), nullptr);
1756 ErrorOr
<vfs::Status
> S
= FS
->status("//root/foo");
1757 ASSERT_FALSE(S
.getError());
1759 ErrorOr
<vfs::Status
> SS
= FS
->status("//root/bar");
1760 ASSERT_FALSE(SS
.getError());
1761 EXPECT_TRUE(S
->equivalent(*SS
));
1764 checkContents(FS
->dir_begin("//root/bar", EC
),
1765 {"//root/bar/a", "//root/bar/b", "//root/bar/c"});
1767 Lower
->addRegularFile("//root/foo/b");
1768 checkContents(FS
->dir_begin("//root/bar", EC
),
1769 {"//root/bar/a", "//root/bar/b", "//root/bar/c"});
1771 EXPECT_EQ(0, NumDiagnostics
);
1774 TEST_F(VFSFromYAMLTest
, RemappedDirectoryOverlayNoFallthrough
) {
1775 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
1776 Lower
->addDirectory("//root/foo");
1777 Lower
->addRegularFile("//root/foo/a");
1778 Lower
->addDirectory("//root/bar");
1779 Lower
->addRegularFile("//root/bar/b");
1780 Lower
->addRegularFile("//root/bar/c");
1781 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
=
1782 getFromYAMLString("{ 'fallthrough': false,\n"
1785 " 'type': 'directory',\n"
1786 " 'name': '//root/',\n"
1787 " 'contents': [ {\n"
1788 " 'type': 'directory-remap',\n"
1790 " 'external-contents': '//root/foo'\n"
1795 ASSERT_NE(FS
.get(), nullptr);
1797 ErrorOr
<vfs::Status
> S
= Lower
->status("//root/foo");
1798 ASSERT_FALSE(S
.getError());
1800 ErrorOr
<vfs::Status
> SS
= FS
->status("//root/bar");
1801 ASSERT_FALSE(SS
.getError());
1802 EXPECT_TRUE(S
->equivalent(*SS
));
1805 checkContents(FS
->dir_begin("//root/bar", EC
), {"//root/foo/a"});
1807 Lower
->addRegularFile("//root/foo/b");
1808 checkContents(FS
->dir_begin("//root/bar", EC
),
1809 {"//root/foo/a", "//root/foo/b"});
1811 EXPECT_EQ(0, NumDiagnostics
);
1814 TEST_F(VFSFromYAMLTest
, ReturnsRequestedPathVFSMiss
) {
1815 IntrusiveRefCntPtr
<vfs::InMemoryFileSystem
> BaseFS(
1816 new vfs::InMemoryFileSystem
);
1817 BaseFS
->addFile("//root/foo/a", 0,
1818 MemoryBuffer::getMemBuffer("contents of a"));
1819 ASSERT_FALSE(BaseFS
->setCurrentWorkingDirectory("//root/foo"));
1820 auto RemappedFS
= vfs::RedirectingFileSystem::create(
1821 {}, /*UseExternalNames=*/false, *BaseFS
);
1823 auto OpenedF
= RemappedFS
->openFileForRead("a");
1824 ASSERT_FALSE(OpenedF
.getError());
1825 llvm::ErrorOr
<std::string
> Name
= (*OpenedF
)->getName();
1826 ASSERT_FALSE(Name
.getError());
1827 EXPECT_EQ("a", Name
.get());
1829 auto OpenedS
= (*OpenedF
)->status();
1830 ASSERT_FALSE(OpenedS
.getError());
1831 EXPECT_EQ("a", OpenedS
->getName());
1832 EXPECT_FALSE(OpenedS
->IsVFSMapped
);
1833 EXPECT_FALSE(OpenedS
->ExposesExternalVFSPath
);
1835 auto DirectS
= RemappedFS
->status("a");
1836 ASSERT_FALSE(DirectS
.getError());
1837 EXPECT_EQ("a", DirectS
->getName());
1838 EXPECT_FALSE(DirectS
->IsVFSMapped
);
1839 EXPECT_FALSE(DirectS
->ExposesExternalVFSPath
);
1841 EXPECT_EQ(0, NumDiagnostics
);
1844 TEST_F(VFSFromYAMLTest
, ReturnsExternalPathVFSHit
) {
1845 IntrusiveRefCntPtr
<vfs::InMemoryFileSystem
> BaseFS(
1846 new vfs::InMemoryFileSystem
);
1847 BaseFS
->addFile("//root/foo/realname", 0,
1848 MemoryBuffer::getMemBuffer("contents of a"));
1850 getFromYAMLString("{ 'use-external-names': true,\n"
1853 " 'type': 'directory',\n"
1854 " 'name': '//root/foo',\n"
1855 " 'contents': [ {\n"
1856 " 'type': 'file',\n"
1857 " 'name': 'vfsname',\n"
1858 " 'external-contents': 'realname'\n"
1863 ASSERT_FALSE(FS
->setCurrentWorkingDirectory("//root/foo"));
1865 auto OpenedF
= FS
->openFileForRead("vfsname");
1866 ASSERT_FALSE(OpenedF
.getError());
1867 llvm::ErrorOr
<std::string
> Name
= (*OpenedF
)->getName();
1868 ASSERT_FALSE(Name
.getError());
1869 EXPECT_EQ("realname", Name
.get());
1871 auto OpenedS
= (*OpenedF
)->status();
1872 ASSERT_FALSE(OpenedS
.getError());
1873 EXPECT_EQ("realname", OpenedS
->getName());
1874 EXPECT_TRUE(OpenedS
->IsVFSMapped
);
1875 EXPECT_TRUE(OpenedS
->ExposesExternalVFSPath
);
1877 auto DirectS
= FS
->status("vfsname");
1878 ASSERT_FALSE(DirectS
.getError());
1879 EXPECT_EQ("realname", DirectS
->getName());
1880 EXPECT_TRUE(DirectS
->IsVFSMapped
);
1881 EXPECT_TRUE(DirectS
->ExposesExternalVFSPath
);
1883 EXPECT_EQ(0, NumDiagnostics
);
1886 TEST_F(VFSFromYAMLTest
, RootRelativeTest
) {
1887 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
1888 Lower
->addDirectory("//root/foo/bar");
1889 Lower
->addRegularFile("//root/foo/bar/a");
1890 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
=
1891 getFromYAMLString("{\n"
1892 " 'case-sensitive': false,\n"
1893 " 'root-relative': 'overlay-dir',\n"
1895 " { 'name': 'b', 'type': 'file',\n"
1896 " 'external-contents': '//root/foo/bar/a'\n"
1900 Lower
, "//root/foo/bar/overlay");
1902 ASSERT_NE(FS
.get(), nullptr);
1903 ErrorOr
<vfs::Status
> S
= FS
->status("//root/foo/bar/b");
1904 ASSERT_FALSE(S
.getError());
1905 EXPECT_EQ("//root/foo/bar/a", S
->getName());
1907 // On Windows, with overlay-relative set to true, the relative
1908 // path in external-contents field will be prepend by OverlayDir
1909 // with native path separator, regardless of the actual path separator
1910 // used in YAMLFilePath field.
1912 FS
= getFromYAMLString("{\n"
1913 " 'case-sensitive': false,\n"
1914 " 'overlay-relative': true,\n"
1915 " 'root-relative': 'overlay-dir',\n"
1917 " { 'name': 'b', 'type': 'file',\n"
1918 " 'external-contents': 'a'\n"
1922 Lower
, "//root/foo/bar/overlay");
1923 ASSERT_NE(FS
.get(), nullptr);
1924 S
= FS
->status("//root/foo/bar/b");
1925 ASSERT_FALSE(S
.getError());
1926 EXPECT_EQ("//root/foo/bar/a", S
->getName());
1928 IntrusiveRefCntPtr
<DummyFileSystem
> LowerWindows(new DummyFileSystem());
1929 LowerWindows
->addDirectory("\\\\root\\foo\\bar");
1930 LowerWindows
->addRegularFile("\\\\root\\foo\\bar\\a");
1931 FS
= getFromYAMLString("{\n"
1932 " 'case-sensitive': false,\n"
1933 " 'overlay-relative': true,\n"
1934 " 'root-relative': 'overlay-dir',\n"
1936 " { 'name': 'b', 'type': 'file',\n"
1937 " 'external-contents': 'a'\n"
1941 LowerWindows
, "\\\\root\\foo\\bar\\overlay");
1942 ASSERT_NE(FS
.get(), nullptr);
1943 S
= FS
->status("\\\\root\\foo\\bar\\b");
1944 ASSERT_FALSE(S
.getError());
1945 EXPECT_EQ("\\\\root\\foo\\bar\\a", S
->getName());
1949 TEST_F(VFSFromYAMLTest
, ReturnsInternalPathVFSHit
) {
1950 IntrusiveRefCntPtr
<vfs::InMemoryFileSystem
> BaseFS(
1951 new vfs::InMemoryFileSystem
);
1952 BaseFS
->addFile("//root/foo/realname", 0,
1953 MemoryBuffer::getMemBuffer("contents of a"));
1955 getFromYAMLString("{ 'use-external-names': false,\n"
1958 " 'type': 'directory',\n"
1959 " 'name': '//root/foo',\n"
1960 " 'contents': [ {\n"
1961 " 'type': 'file',\n"
1962 " 'name': 'vfsname',\n"
1963 " 'external-contents': 'realname'\n"
1968 ASSERT_FALSE(FS
->setCurrentWorkingDirectory("//root/foo"));
1970 auto OpenedF
= FS
->openFileForRead("vfsname");
1971 ASSERT_FALSE(OpenedF
.getError());
1972 llvm::ErrorOr
<std::string
> Name
= (*OpenedF
)->getName();
1973 ASSERT_FALSE(Name
.getError());
1974 EXPECT_EQ("vfsname", Name
.get());
1976 auto OpenedS
= (*OpenedF
)->status();
1977 ASSERT_FALSE(OpenedS
.getError());
1978 EXPECT_EQ("vfsname", OpenedS
->getName());
1979 EXPECT_TRUE(OpenedS
->IsVFSMapped
);
1980 EXPECT_FALSE(OpenedS
->ExposesExternalVFSPath
);
1982 auto DirectS
= FS
->status("vfsname");
1983 ASSERT_FALSE(DirectS
.getError());
1984 EXPECT_EQ("vfsname", DirectS
->getName());
1985 EXPECT_TRUE(DirectS
->IsVFSMapped
);
1986 EXPECT_FALSE(DirectS
->ExposesExternalVFSPath
);
1988 EXPECT_EQ(0, NumDiagnostics
);
1991 TEST_F(VFSFromYAMLTest
, CaseInsensitive
) {
1992 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
1993 Lower
->addRegularFile("//root/foo/bar/a");
1994 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= getFromYAMLString(
1995 "{ 'case-sensitive': 'false',\n"
1998 " 'type': 'directory',\n"
1999 " 'name': '//root/',\n"
2000 " 'contents': [ {\n"
2001 " 'type': 'file',\n"
2003 " 'external-contents': '//root/foo/bar/a'\n"
2008 ASSERT_NE(FS
.get(), nullptr);
2010 IntrusiveRefCntPtr
<vfs::OverlayFileSystem
> O(
2011 new vfs::OverlayFileSystem(Lower
));
2014 ErrorOr
<vfs::Status
> S
= O
->status("//root/XX");
2015 ASSERT_FALSE(S
.getError());
2017 ErrorOr
<vfs::Status
> SS
= O
->status("//root/xx");
2018 ASSERT_FALSE(SS
.getError());
2019 EXPECT_TRUE(S
->equivalent(*SS
));
2020 SS
= O
->status("//root/xX");
2021 EXPECT_TRUE(S
->equivalent(*SS
));
2022 SS
= O
->status("//root/Xx");
2023 EXPECT_TRUE(S
->equivalent(*SS
));
2024 EXPECT_EQ(0, NumDiagnostics
);
2027 TEST_F(VFSFromYAMLTest
, CaseSensitive
) {
2028 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
2029 Lower
->addRegularFile("//root/foo/bar/a");
2030 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= getFromYAMLString(
2031 "{ 'case-sensitive': 'true',\n"
2034 " 'type': 'directory',\n"
2035 " 'name': '//root/',\n"
2036 " 'contents': [ {\n"
2037 " 'type': 'file',\n"
2039 " 'external-contents': '//root/foo/bar/a'\n"
2044 ASSERT_NE(FS
.get(), nullptr);
2046 IntrusiveRefCntPtr
<vfs::OverlayFileSystem
> O(
2047 new vfs::OverlayFileSystem(Lower
));
2050 ErrorOr
<vfs::Status
> SS
= O
->status("//root/xx");
2051 EXPECT_EQ(SS
.getError(), llvm::errc::no_such_file_or_directory
);
2052 SS
= O
->status("//root/xX");
2053 EXPECT_EQ(SS
.getError(), llvm::errc::no_such_file_or_directory
);
2054 SS
= O
->status("//root/Xx");
2055 EXPECT_EQ(SS
.getError(), llvm::errc::no_such_file_or_directory
);
2056 EXPECT_EQ(0, NumDiagnostics
);
2059 TEST_F(VFSFromYAMLTest
, IllegalVFSFile
) {
2060 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
2062 // invalid YAML at top-level
2063 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= getFromYAMLString("{]", Lower
);
2064 EXPECT_EQ(nullptr, FS
.get());
2065 // invalid YAML in roots
2066 FS
= getFromYAMLString("{ 'roots':[}", Lower
);
2067 // invalid YAML in directory
2068 FS
= getFromYAMLString(
2069 "{ 'roots':[ { 'name': 'foo', 'type': 'directory', 'contents': [}",
2071 EXPECT_EQ(nullptr, FS
.get());
2073 // invalid configuration
2074 FS
= getFromYAMLString("{ 'knobular': 'true', 'roots':[] }", Lower
);
2075 EXPECT_EQ(nullptr, FS
.get());
2076 FS
= getFromYAMLString("{ 'case-sensitive': 'maybe', 'roots':[] }", Lower
);
2077 EXPECT_EQ(nullptr, FS
.get());
2080 FS
= getFromYAMLString("{ 'roots':'' }", Lower
);
2081 EXPECT_EQ(nullptr, FS
.get());
2082 FS
= getFromYAMLString("{ 'roots':{} }", Lower
);
2083 EXPECT_EQ(nullptr, FS
.get());
2086 FS
= getFromYAMLString(
2087 "{ 'roots':[ { 'type': 'other', 'name': 'me', 'contents': '' }", Lower
);
2088 EXPECT_EQ(nullptr, FS
.get());
2089 FS
= getFromYAMLString("{ 'roots':[ { 'type': 'file', 'name': [], "
2090 "'external-contents': 'other' }",
2092 EXPECT_EQ(nullptr, FS
.get());
2093 FS
= getFromYAMLString(
2094 "{ 'roots':[ { 'type': 'file', 'name': 'me', 'external-contents': [] }",
2096 EXPECT_EQ(nullptr, FS
.get());
2097 FS
= getFromYAMLString(
2098 "{ 'roots':[ { 'type': 'file', 'name': 'me', 'external-contents': {} }",
2100 EXPECT_EQ(nullptr, FS
.get());
2101 FS
= getFromYAMLString(
2102 "{ 'roots':[ { 'type': 'directory', 'name': 'me', 'contents': {} }",
2104 EXPECT_EQ(nullptr, FS
.get());
2105 FS
= getFromYAMLString(
2106 "{ 'roots':[ { 'type': 'directory', 'name': 'me', 'contents': '' }",
2108 EXPECT_EQ(nullptr, FS
.get());
2109 FS
= getFromYAMLString(
2110 "{ 'roots':[ { 'thingy': 'directory', 'name': 'me', 'contents': [] }",
2112 EXPECT_EQ(nullptr, FS
.get());
2114 // missing mandatory fields
2115 FS
= getFromYAMLString("{ 'roots':[ { 'type': 'file', 'name': 'me' }", Lower
);
2116 EXPECT_EQ(nullptr, FS
.get());
2117 FS
= getFromYAMLString(
2118 "{ 'roots':[ { 'type': 'file', 'external-contents': 'other' }", Lower
);
2119 EXPECT_EQ(nullptr, FS
.get());
2120 FS
= getFromYAMLString("{ 'roots':[ { 'name': 'me', 'contents': [] }", Lower
);
2121 EXPECT_EQ(nullptr, FS
.get());
2124 FS
= getFromYAMLString("{ 'roots':[], 'roots':[] }", Lower
);
2125 EXPECT_EQ(nullptr, FS
.get());
2126 FS
= getFromYAMLString(
2127 "{ 'case-sensitive':'true', 'case-sensitive':'true', 'roots':[] }",
2129 EXPECT_EQ(nullptr, FS
.get());
2131 getFromYAMLString("{ 'roots':[{'name':'me', 'name':'you', 'type':'file', "
2132 "'external-contents':'blah' } ] }",
2134 EXPECT_EQ(nullptr, FS
.get());
2137 FS
= getFromYAMLRawString("{ 'roots':[] }", Lower
);
2138 EXPECT_EQ(nullptr, FS
.get());
2140 // bad version number
2141 FS
= getFromYAMLRawString("{ 'version':'foo', 'roots':[] }", Lower
);
2142 EXPECT_EQ(nullptr, FS
.get());
2143 FS
= getFromYAMLRawString("{ 'version':-1, 'roots':[] }", Lower
);
2144 EXPECT_EQ(nullptr, FS
.get());
2145 FS
= getFromYAMLRawString("{ 'version':100000, 'roots':[] }", Lower
);
2146 EXPECT_EQ(nullptr, FS
.get());
2148 // both 'external-contents' and 'contents' specified
2149 Lower
->addDirectory("//root/external/dir");
2150 FS
= getFromYAMLString(
2152 "{ 'type': 'directory', 'name': '//root/A', 'contents': [],\n"
2153 " 'external-contents': '//root/external/dir'}]}",
2155 EXPECT_EQ(nullptr, FS
.get());
2157 // 'directory-remap' with 'contents'
2158 FS
= getFromYAMLString(
2160 "{ 'type': 'directory-remap', 'name': '//root/A', 'contents': [] }]}",
2162 EXPECT_EQ(nullptr, FS
.get());
2164 // invalid redirect kind
2165 FS
= getFromYAMLString("{ 'redirecting-with': 'none', 'roots': [{\n"
2166 " 'type': 'directory-remap',\n"
2167 " 'name': '//root/A',\n"
2168 " 'external-contents': '//root/B' }]}",
2170 EXPECT_EQ(nullptr, FS
.get());
2172 // redirect and fallthrough passed
2173 FS
= getFromYAMLString("{ 'redirecting-with': 'fallthrough',\n"
2174 " 'fallthrough': true,\n"
2176 " 'type': 'directory-remap',\n"
2177 " 'name': '//root/A',\n"
2178 " 'external-contents': '//root/B' }]}",
2180 EXPECT_EQ(nullptr, FS
.get());
2182 EXPECT_EQ(28, NumDiagnostics
);
2185 TEST_F(VFSFromYAMLTest
, UseExternalName
) {
2186 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
2187 Lower
->addRegularFile("//root/external/file");
2189 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
=
2190 getFromYAMLString("{ 'roots': [\n"
2191 " { 'type': 'file', 'name': '//root/A',\n"
2192 " 'external-contents': '//root/external/file'\n"
2194 " { 'type': 'file', 'name': '//root/B',\n"
2195 " 'use-external-name': true,\n"
2196 " 'external-contents': '//root/external/file'\n"
2198 " { 'type': 'file', 'name': '//root/C',\n"
2199 " 'use-external-name': false,\n"
2200 " 'external-contents': '//root/external/file'\n"
2204 ASSERT_NE(nullptr, FS
.get());
2207 EXPECT_EQ("//root/external/file", FS
->status("//root/A")->getName());
2209 EXPECT_EQ("//root/external/file", FS
->status("//root/B")->getName());
2210 EXPECT_EQ("//root/C", FS
->status("//root/C")->getName());
2212 // global configuration
2213 FS
= getFromYAMLString("{ 'use-external-names': false,\n"
2215 " { 'type': 'file', 'name': '//root/A',\n"
2216 " 'external-contents': '//root/external/file'\n"
2218 " { 'type': 'file', 'name': '//root/B',\n"
2219 " 'use-external-name': true,\n"
2220 " 'external-contents': '//root/external/file'\n"
2222 " { 'type': 'file', 'name': '//root/C',\n"
2223 " 'use-external-name': false,\n"
2224 " 'external-contents': '//root/external/file'\n"
2228 ASSERT_NE(nullptr, FS
.get());
2231 EXPECT_EQ("//root/A", FS
->status("//root/A")->getName());
2233 EXPECT_EQ("//root/external/file", FS
->status("//root/B")->getName());
2234 EXPECT_EQ("//root/C", FS
->status("//root/C")->getName());
2237 TEST_F(VFSFromYAMLTest
, MultiComponentPath
) {
2238 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
2239 Lower
->addRegularFile("//root/other");
2242 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
=
2243 getFromYAMLString("{ 'roots': [\n"
2244 " { 'type': 'file', 'name': '//root/path/to/file',\n"
2245 " 'external-contents': '//root/other' }]\n"
2248 ASSERT_NE(nullptr, FS
.get());
2249 EXPECT_FALSE(FS
->status("//root/path/to/file").getError());
2250 EXPECT_FALSE(FS
->status("//root/path/to").getError());
2251 EXPECT_FALSE(FS
->status("//root/path").getError());
2252 EXPECT_FALSE(FS
->status("//root/").getError());
2255 FS
= getFromYAMLString(
2257 " { 'type': 'directory', 'name': '//root/path/to',\n"
2258 " 'contents': [ { 'type': 'file', 'name': 'file',\n"
2259 " 'external-contents': '//root/other' }]}]\n"
2262 ASSERT_NE(nullptr, FS
.get());
2263 EXPECT_FALSE(FS
->status("//root/path/to/file").getError());
2264 EXPECT_FALSE(FS
->status("//root/path/to").getError());
2265 EXPECT_FALSE(FS
->status("//root/path").getError());
2266 EXPECT_FALSE(FS
->status("//root/").getError());
2269 FS
= getFromYAMLString(
2271 " { 'type': 'directory', 'name': '//root/',\n"
2272 " 'contents': [ { 'type': 'file', 'name': 'path/to/file',\n"
2273 " 'external-contents': '//root/other' }]}]\n"
2276 ASSERT_NE(nullptr, FS
.get());
2277 EXPECT_FALSE(FS
->status("//root/path/to/file").getError());
2278 EXPECT_FALSE(FS
->status("//root/path/to").getError());
2279 EXPECT_FALSE(FS
->status("//root/path").getError());
2280 EXPECT_FALSE(FS
->status("//root/").getError());
2283 TEST_F(VFSFromYAMLTest
, TrailingSlashes
) {
2284 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
2285 Lower
->addRegularFile("//root/other");
2288 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= getFromYAMLString(
2290 " { 'type': 'directory', 'name': '//root/path/to////',\n"
2291 " 'contents': [ { 'type': 'file', 'name': 'file',\n"
2292 " 'external-contents': '//root/other' }]}]\n"
2295 ASSERT_NE(nullptr, FS
.get());
2296 EXPECT_FALSE(FS
->status("//root/path/to/file").getError());
2297 EXPECT_FALSE(FS
->status("//root/path/to").getError());
2298 EXPECT_FALSE(FS
->status("//root/path").getError());
2299 EXPECT_FALSE(FS
->status("//root/").getError());
2302 TEST_F(VFSFromYAMLTest
, DirectoryIteration
) {
2303 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
2304 Lower
->addDirectory("//root/");
2305 Lower
->addDirectory("//root/foo");
2306 Lower
->addDirectory("//root/foo/bar");
2307 Lower
->addRegularFile("//root/foo/bar/a");
2308 Lower
->addRegularFile("//root/foo/bar/b");
2309 Lower
->addRegularFile("//root/file3");
2310 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= getFromYAMLString(
2311 "{ 'use-external-names': false,\n"
2314 " 'type': 'directory',\n"
2315 " 'name': '//root/',\n"
2316 " 'contents': [ {\n"
2317 " 'type': 'file',\n"
2318 " 'name': 'file1',\n"
2319 " 'external-contents': '//root/foo/bar/a'\n"
2322 " 'type': 'file',\n"
2323 " 'name': 'file2',\n"
2324 " 'external-contents': '//root/foo/bar/b'\n"
2331 ASSERT_NE(FS
.get(), nullptr);
2333 IntrusiveRefCntPtr
<vfs::OverlayFileSystem
> O(
2334 new vfs::OverlayFileSystem(Lower
));
2338 checkContents(O
->dir_begin("//root/", EC
),
2339 {"//root/file1", "//root/file2", "//root/file3", "//root/foo"});
2341 checkContents(O
->dir_begin("//root/foo/bar", EC
),
2342 {"//root/foo/bar/a", "//root/foo/bar/b"});
2345 TEST_F(VFSFromYAMLTest
, DirectoryIterationSameDirMultipleEntries
) {
2346 // https://llvm.org/bugs/show_bug.cgi?id=27725
2347 if (!supportsSameDirMultipleYAMLEntries())
2350 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
2351 Lower
->addDirectory("//root/zab");
2352 Lower
->addDirectory("//root/baz");
2353 Lower
->addRegularFile("//root/zab/a");
2354 Lower
->addRegularFile("//root/zab/b");
2355 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= getFromYAMLString(
2356 "{ 'use-external-names': false,\n"
2359 " 'type': 'directory',\n"
2360 " 'name': '//root/baz/',\n"
2361 " 'contents': [ {\n"
2362 " 'type': 'file',\n"
2364 " 'external-contents': '//root/zab/a'\n"
2369 " 'type': 'directory',\n"
2370 " 'name': '//root/baz/',\n"
2371 " 'contents': [ {\n"
2372 " 'type': 'file',\n"
2374 " 'external-contents': '//root/zab/b'\n"
2381 ASSERT_NE(FS
.get(), nullptr);
2383 IntrusiveRefCntPtr
<vfs::OverlayFileSystem
> O(
2384 new vfs::OverlayFileSystem(Lower
));
2389 checkContents(O
->dir_begin("//root/baz/", EC
),
2390 {"//root/baz/x", "//root/baz/y"});
2393 TEST_F(VFSFromYAMLTest
, RecursiveDirectoryIterationLevel
) {
2395 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
2396 Lower
->addDirectory("//root/a");
2397 Lower
->addDirectory("//root/a/b");
2398 Lower
->addDirectory("//root/a/b/c");
2399 Lower
->addRegularFile("//root/a/b/c/file");
2400 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= getFromYAMLString(
2401 "{ 'use-external-names': false,\n"
2404 " 'type': 'directory',\n"
2405 " 'name': '//root/a/b/c/',\n"
2406 " 'contents': [ {\n"
2407 " 'type': 'file',\n"
2408 " 'name': 'file',\n"
2409 " 'external-contents': '//root/a/b/c/file'\n"
2416 ASSERT_NE(FS
.get(), nullptr);
2418 IntrusiveRefCntPtr
<vfs::OverlayFileSystem
> O(
2419 new vfs::OverlayFileSystem(Lower
));
2424 // Test recursive_directory_iterator level()
2425 vfs::recursive_directory_iterator I
= vfs::recursive_directory_iterator(
2429 for (int l
= 0; I
!= E
; I
.increment(EC
), ++l
) {
2431 EXPECT_EQ(I
.level(), l
);
2436 TEST_F(VFSFromYAMLTest
, RelativePaths
) {
2437 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
2439 SmallString
<128> CWD
;
2440 EC
= llvm::sys::fs::current_path(CWD
);
2443 // Filename at root level without a parent directory.
2444 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= getFromYAMLString(
2446 " { 'type': 'file', 'name': 'file-not-in-directory.h',\n"
2447 " 'external-contents': '//root/external/file'\n"
2451 ASSERT_TRUE(FS
.get() != nullptr);
2452 SmallString
<128> ExpectedPathNotInDir("file-not-in-directory.h");
2453 llvm::sys::fs::make_absolute(ExpectedPathNotInDir
);
2454 checkContents(FS
->dir_begin(CWD
, EC
), {ExpectedPathNotInDir
});
2456 // Relative file path.
2457 FS
= getFromYAMLString("{ 'roots': [\n"
2458 " { 'type': 'file', 'name': 'relative/path.h',\n"
2459 " 'external-contents': '//root/external/file'\n"
2463 ASSERT_TRUE(FS
.get() != nullptr);
2464 SmallString
<128> Parent("relative");
2465 llvm::sys::fs::make_absolute(Parent
);
2466 auto I
= FS
->dir_begin(Parent
, EC
);
2468 // Convert to POSIX path for comparison of windows paths
2469 ASSERT_EQ("relative/path.h",
2470 getPosixPath(std::string(I
->path().substr(CWD
.size() + 1))));
2472 // Relative directory path.
2473 FS
= getFromYAMLString(
2475 " { 'type': 'directory', 'name': 'relative/directory/path.h',\n"
2480 ASSERT_TRUE(FS
.get() != nullptr);
2481 SmallString
<128> Root("relative/directory");
2482 llvm::sys::fs::make_absolute(Root
);
2483 I
= FS
->dir_begin(Root
, EC
);
2485 ASSERT_EQ("path.h", std::string(I
->path().substr(Root
.size() + 1)));
2487 EXPECT_EQ(0, NumDiagnostics
);
2490 TEST_F(VFSFromYAMLTest
, NonFallthroughDirectoryIteration
) {
2491 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
2492 Lower
->addDirectory("//root/");
2493 Lower
->addRegularFile("//root/a");
2494 Lower
->addRegularFile("//root/b");
2495 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= getFromYAMLString(
2496 "{ 'use-external-names': false,\n"
2497 " 'fallthrough': false,\n"
2500 " 'type': 'directory',\n"
2501 " 'name': '//root/',\n"
2502 " 'contents': [ {\n"
2503 " 'type': 'file',\n"
2505 " 'external-contents': '//root/a'\n"
2512 ASSERT_NE(FS
.get(), nullptr);
2515 checkContents(FS
->dir_begin("//root/", EC
),
2519 TEST_F(VFSFromYAMLTest
, DirectoryIterationWithDuplicates
) {
2520 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
2521 Lower
->addDirectory("//root/");
2522 Lower
->addRegularFile("//root/a");
2523 Lower
->addRegularFile("//root/b");
2524 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= getFromYAMLString(
2525 "{ 'use-external-names': false,\n"
2528 " 'type': 'directory',\n"
2529 " 'name': '//root/',\n"
2530 " 'contents': [ {\n"
2531 " 'type': 'file',\n"
2533 " 'external-contents': '//root/a'\n"
2540 ASSERT_NE(FS
.get(), nullptr);
2543 checkContents(FS
->dir_begin("//root/", EC
),
2544 {"//root/a", "//root/b"});
2547 TEST_F(VFSFromYAMLTest
, DirectoryIterationErrorInVFSLayer
) {
2548 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
2549 Lower
->addDirectory("//root/");
2550 Lower
->addDirectory("//root/foo");
2551 Lower
->addRegularFile("//root/foo/a");
2552 Lower
->addRegularFile("//root/foo/b");
2553 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= getFromYAMLString(
2554 "{ 'use-external-names': false,\n"
2557 " 'type': 'directory',\n"
2558 " 'name': '//root/',\n"
2559 " 'contents': [ {\n"
2560 " 'type': 'file',\n"
2561 " 'name': 'bar/a',\n"
2562 " 'external-contents': '//root/foo/a'\n"
2569 ASSERT_NE(FS
.get(), nullptr);
2572 checkContents(FS
->dir_begin("//root/foo", EC
),
2573 {"//root/foo/a", "//root/foo/b"});
2576 TEST_F(VFSFromYAMLTest
, GetRealPath
) {
2577 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
2578 Lower
->addDirectory("//dir/");
2579 Lower
->addRegularFile("/foo");
2580 Lower
->addSymlink("/link");
2581 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= getFromYAMLString(
2582 "{ 'use-external-names': false,\n"
2583 " 'case-sensitive': false,\n"
2586 " 'type': 'directory',\n"
2587 " 'name': '//root/',\n"
2588 " 'contents': [ {\n"
2589 " 'type': 'file',\n"
2591 " 'external-contents': '/link'\n"
2594 " 'type': 'directory',\n"
2601 " 'type': 'directory',\n"
2602 " 'name': '//dir/',\n"
2608 ASSERT_NE(FS
.get(), nullptr);
2610 // Regular file present in underlying file system.
2611 SmallString
<16> RealPath
;
2612 EXPECT_FALSE(FS
->getRealPath("/foo", RealPath
));
2613 EXPECT_EQ(RealPath
.str(), "/foo");
2615 // File present in YAML pointing to symlink in underlying file system.
2616 EXPECT_FALSE(FS
->getRealPath("//root/bar", RealPath
));
2617 EXPECT_EQ(RealPath
.str(), "/symlink");
2619 // Directories should return the virtual path as written in the definition.
2620 EXPECT_FALSE(FS
->getRealPath("//ROOT/baz", RealPath
));
2621 EXPECT_EQ(RealPath
.str(), "//root/baz");
2623 // Try a non-existing file.
2624 EXPECT_EQ(FS
->getRealPath("/non_existing", RealPath
),
2625 errc::no_such_file_or_directory
);
2628 TEST_F(VFSFromYAMLTest
, WorkingDirectory
) {
2629 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
2630 Lower
->addDirectory("//root/");
2631 Lower
->addDirectory("//root/foo");
2632 Lower
->addRegularFile("//root/foo/a");
2633 Lower
->addRegularFile("//root/foo/b");
2634 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= getFromYAMLString(
2635 "{ 'use-external-names': false,\n"
2638 " 'type': 'directory',\n"
2639 " 'name': '//root/bar',\n"
2640 " 'contents': [ {\n"
2641 " 'type': 'file',\n"
2643 " 'external-contents': '//root/foo/a'\n"
2650 ASSERT_NE(FS
.get(), nullptr);
2651 std::error_code EC
= FS
->setCurrentWorkingDirectory("//root/bar");
2654 llvm::ErrorOr
<std::string
> WorkingDir
= FS
->getCurrentWorkingDirectory();
2655 ASSERT_TRUE(WorkingDir
);
2656 EXPECT_EQ(*WorkingDir
, "//root/bar");
2658 llvm::ErrorOr
<vfs::Status
> Status
= FS
->status("./a");
2659 ASSERT_FALSE(Status
.getError());
2660 EXPECT_TRUE(Status
->isStatusKnown());
2661 EXPECT_FALSE(Status
->isDirectory());
2662 EXPECT_TRUE(Status
->isRegularFile());
2663 EXPECT_FALSE(Status
->isSymlink());
2664 EXPECT_FALSE(Status
->isOther());
2665 EXPECT_TRUE(Status
->exists());
2667 EC
= FS
->setCurrentWorkingDirectory("bogus");
2669 WorkingDir
= FS
->getCurrentWorkingDirectory();
2670 ASSERT_TRUE(WorkingDir
);
2671 EXPECT_EQ(*WorkingDir
, "//root/bar");
2673 EC
= FS
->setCurrentWorkingDirectory("//root/");
2675 WorkingDir
= FS
->getCurrentWorkingDirectory();
2676 ASSERT_TRUE(WorkingDir
);
2677 EXPECT_EQ(*WorkingDir
, "//root/");
2679 EC
= FS
->setCurrentWorkingDirectory("bar");
2681 WorkingDir
= FS
->getCurrentWorkingDirectory();
2682 ASSERT_TRUE(WorkingDir
);
2683 EXPECT_EQ(*WorkingDir
, "//root/bar");
2686 TEST_F(VFSFromYAMLTest
, WorkingDirectoryFallthrough
) {
2687 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
2688 Lower
->addDirectory("//root/");
2689 Lower
->addDirectory("//root/foo");
2690 Lower
->addRegularFile("//root/foo/a");
2691 Lower
->addRegularFile("//root/foo/b");
2692 Lower
->addRegularFile("//root/c");
2693 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= getFromYAMLString(
2694 "{ 'use-external-names': false,\n"
2697 " 'type': 'directory',\n"
2698 " 'name': '//root/bar',\n"
2699 " 'contents': [ {\n"
2700 " 'type': 'file',\n"
2702 " 'external-contents': '//root/foo/a'\n"
2707 " 'type': 'directory',\n"
2708 " 'name': '//root/bar/baz',\n"
2709 " 'contents': [ {\n"
2710 " 'type': 'file',\n"
2712 " 'external-contents': '//root/foo/a'\n"
2719 ASSERT_NE(FS
.get(), nullptr);
2720 std::error_code EC
= FS
->setCurrentWorkingDirectory("//root/");
2722 ASSERT_NE(FS
.get(), nullptr);
2724 llvm::ErrorOr
<vfs::Status
> Status
= FS
->status("bar/a");
2725 ASSERT_FALSE(Status
.getError());
2726 EXPECT_TRUE(Status
->exists());
2728 Status
= FS
->status("foo/a");
2729 ASSERT_FALSE(Status
.getError());
2730 EXPECT_TRUE(Status
->exists());
2732 EC
= FS
->setCurrentWorkingDirectory("//root/bar");
2735 Status
= FS
->status("./a");
2736 ASSERT_FALSE(Status
.getError());
2737 EXPECT_TRUE(Status
->exists());
2739 Status
= FS
->status("./b");
2740 ASSERT_TRUE(Status
.getError());
2742 Status
= FS
->status("./c");
2743 ASSERT_TRUE(Status
.getError());
2745 EC
= FS
->setCurrentWorkingDirectory("//root/");
2748 Status
= FS
->status("c");
2749 ASSERT_FALSE(Status
.getError());
2750 EXPECT_TRUE(Status
->exists());
2752 Status
= FS
->status("./bar/baz/a");
2753 ASSERT_FALSE(Status
.getError());
2754 EXPECT_TRUE(Status
->exists());
2756 EC
= FS
->setCurrentWorkingDirectory("//root/bar");
2759 Status
= FS
->status("./baz/a");
2760 ASSERT_FALSE(Status
.getError());
2761 EXPECT_TRUE(Status
->exists());
2763 Status
= FS
->status("../bar/baz/a");
2764 ASSERT_FALSE(Status
.getError());
2765 EXPECT_TRUE(Status
->exists());
2768 TEST_F(VFSFromYAMLTest
, WorkingDirectoryFallthroughInvalid
) {
2769 IntrusiveRefCntPtr
<ErrorDummyFileSystem
> Lower(new ErrorDummyFileSystem());
2770 Lower
->addDirectory("//root/");
2771 Lower
->addDirectory("//root/foo");
2772 Lower
->addRegularFile("//root/foo/a");
2773 Lower
->addRegularFile("//root/foo/b");
2774 Lower
->addRegularFile("//root/c");
2775 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= getFromYAMLString(
2776 "{ 'use-external-names': false,\n"
2779 " 'type': 'directory',\n"
2780 " 'name': '//root/bar',\n"
2781 " 'contents': [ {\n"
2782 " 'type': 'file',\n"
2784 " 'external-contents': '//root/foo/a'\n"
2791 ASSERT_NE(FS
.get(), nullptr);
2792 std::error_code EC
= FS
->setCurrentWorkingDirectory("//root/");
2794 ASSERT_NE(FS
.get(), nullptr);
2796 llvm::ErrorOr
<vfs::Status
> Status
= FS
->status("bar/a");
2797 ASSERT_FALSE(Status
.getError());
2798 EXPECT_TRUE(Status
->exists());
2800 Status
= FS
->status("foo/a");
2801 ASSERT_FALSE(Status
.getError());
2802 EXPECT_TRUE(Status
->exists());
2805 TEST_F(VFSFromYAMLTest
, VirtualWorkingDirectory
) {
2806 IntrusiveRefCntPtr
<ErrorDummyFileSystem
> Lower(new ErrorDummyFileSystem());
2807 Lower
->addDirectory("//root/");
2808 Lower
->addDirectory("//root/foo");
2809 Lower
->addRegularFile("//root/foo/a");
2810 Lower
->addRegularFile("//root/foo/b");
2811 Lower
->addRegularFile("//root/c");
2812 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= getFromYAMLString(
2813 "{ 'use-external-names': false,\n"
2816 " 'type': 'directory',\n"
2817 " 'name': '//root/bar',\n"
2818 " 'contents': [ {\n"
2819 " 'type': 'file',\n"
2821 " 'external-contents': '//root/foo/a'\n"
2828 ASSERT_NE(FS
.get(), nullptr);
2829 std::error_code EC
= FS
->setCurrentWorkingDirectory("//root/bar");
2831 ASSERT_NE(FS
.get(), nullptr);
2833 llvm::ErrorOr
<vfs::Status
> Status
= FS
->status("a");
2834 ASSERT_FALSE(Status
.getError());
2835 EXPECT_TRUE(Status
->exists());
2838 TEST_F(VFSFromYAMLTest
, YAMLVFSWriterTest
) {
2839 TempDir
TestDirectory("virtual-file-system-test", /*Unique*/ true);
2840 TempDir
_a(TestDirectory
.path("a"));
2841 TempFile
_ab(TestDirectory
.path("a, b"));
2842 TempDir
_c(TestDirectory
.path("c"));
2843 TempFile
_cd(TestDirectory
.path("c/d"));
2844 TempDir
_e(TestDirectory
.path("e"));
2845 TempDir
_ef(TestDirectory
.path("e/f"));
2846 TempFile
_g(TestDirectory
.path("g"));
2847 TempDir
_h(TestDirectory
.path("h"));
2849 vfs::YAMLVFSWriter VFSWriter
;
2850 VFSWriter
.addDirectoryMapping(_a
.path(), "//root/a");
2851 VFSWriter
.addFileMapping(_ab
.path(), "//root/a/b");
2852 VFSWriter
.addFileMapping(_cd
.path(), "//root/c/d");
2853 VFSWriter
.addDirectoryMapping(_e
.path(), "//root/e");
2854 VFSWriter
.addDirectoryMapping(_ef
.path(), "//root/e/f");
2855 VFSWriter
.addFileMapping(_g
.path(), "//root/g");
2856 VFSWriter
.addDirectoryMapping(_h
.path(), "//root/h");
2859 raw_string_ostream
OS(Buffer
);
2860 VFSWriter
.write(OS
);
2863 IntrusiveRefCntPtr
<ErrorDummyFileSystem
> Lower(new ErrorDummyFileSystem());
2864 Lower
->addDirectory("//root/");
2865 Lower
->addDirectory("//root/a");
2866 Lower
->addRegularFile("//root/a/b");
2867 Lower
->addDirectory("//root/b");
2868 Lower
->addDirectory("//root/c");
2869 Lower
->addRegularFile("//root/c/d");
2870 Lower
->addDirectory("//root/e");
2871 Lower
->addDirectory("//root/e/f");
2872 Lower
->addRegularFile("//root/g");
2873 Lower
->addDirectory("//root/h");
2875 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= getFromYAMLRawString(Buffer
, Lower
);
2876 ASSERT_NE(FS
.get(), nullptr);
2878 EXPECT_TRUE(FS
->exists(_a
.path()));
2879 EXPECT_TRUE(FS
->exists(_ab
.path()));
2880 EXPECT_TRUE(FS
->exists(_c
.path()));
2881 EXPECT_TRUE(FS
->exists(_cd
.path()));
2882 EXPECT_TRUE(FS
->exists(_e
.path()));
2883 EXPECT_TRUE(FS
->exists(_ef
.path()));
2884 EXPECT_TRUE(FS
->exists(_g
.path()));
2885 EXPECT_TRUE(FS
->exists(_h
.path()));
2888 TEST_F(VFSFromYAMLTest
, YAMLVFSWriterTest2
) {
2889 TempDir
TestDirectory("virtual-file-system-test", /*Unique*/ true);
2890 TempDir
_a(TestDirectory
.path("a"));
2891 TempFile
_ab(TestDirectory
.path("a/b"));
2892 TempDir
_ac(TestDirectory
.path("a/c"));
2893 TempFile
_acd(TestDirectory
.path("a/c/d"));
2894 TempFile
_ace(TestDirectory
.path("a/c/e"));
2895 TempFile
_acf(TestDirectory
.path("a/c/f"));
2896 TempDir
_ag(TestDirectory
.path("a/g"));
2897 TempFile
_agh(TestDirectory
.path("a/g/h"));
2899 vfs::YAMLVFSWriter VFSWriter
;
2900 VFSWriter
.addDirectoryMapping(_a
.path(), "//root/a");
2901 VFSWriter
.addFileMapping(_ab
.path(), "//root/a/b");
2902 VFSWriter
.addDirectoryMapping(_ac
.path(), "//root/a/c");
2903 VFSWriter
.addFileMapping(_acd
.path(), "//root/a/c/d");
2904 VFSWriter
.addFileMapping(_ace
.path(), "//root/a/c/e");
2905 VFSWriter
.addFileMapping(_acf
.path(), "//root/a/c/f");
2906 VFSWriter
.addDirectoryMapping(_ag
.path(), "//root/a/g");
2907 VFSWriter
.addFileMapping(_agh
.path(), "//root/a/g/h");
2910 raw_string_ostream
OS(Buffer
);
2911 VFSWriter
.write(OS
);
2914 IntrusiveRefCntPtr
<ErrorDummyFileSystem
> Lower(new ErrorDummyFileSystem());
2915 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= getFromYAMLRawString(Buffer
, Lower
);
2916 EXPECT_NE(FS
.get(), nullptr);
2919 TEST_F(VFSFromYAMLTest
, YAMLVFSWriterTest3
) {
2920 TempDir
TestDirectory("virtual-file-system-test", /*Unique*/ true);
2921 TempDir
_a(TestDirectory
.path("a"));
2922 TempFile
_ab(TestDirectory
.path("a/b"));
2923 TempDir
_ac(TestDirectory
.path("a/c"));
2924 TempDir
_acd(TestDirectory
.path("a/c/d"));
2925 TempDir
_acde(TestDirectory
.path("a/c/d/e"));
2926 TempFile
_acdef(TestDirectory
.path("a/c/d/e/f"));
2927 TempFile
_acdeg(TestDirectory
.path("a/c/d/e/g"));
2928 TempDir
_ah(TestDirectory
.path("a/h"));
2929 TempFile
_ahi(TestDirectory
.path("a/h/i"));
2931 vfs::YAMLVFSWriter VFSWriter
;
2932 VFSWriter
.addDirectoryMapping(_a
.path(), "//root/a");
2933 VFSWriter
.addFileMapping(_ab
.path(), "//root/a/b");
2934 VFSWriter
.addDirectoryMapping(_ac
.path(), "//root/a/c");
2935 VFSWriter
.addDirectoryMapping(_acd
.path(), "//root/a/c/d");
2936 VFSWriter
.addDirectoryMapping(_acde
.path(), "//root/a/c/d/e");
2937 VFSWriter
.addFileMapping(_acdef
.path(), "//root/a/c/d/e/f");
2938 VFSWriter
.addFileMapping(_acdeg
.path(), "//root/a/c/d/e/g");
2939 VFSWriter
.addDirectoryMapping(_ahi
.path(), "//root/a/h");
2940 VFSWriter
.addFileMapping(_ahi
.path(), "//root/a/h/i");
2943 raw_string_ostream
OS(Buffer
);
2944 VFSWriter
.write(OS
);
2947 IntrusiveRefCntPtr
<ErrorDummyFileSystem
> Lower(new ErrorDummyFileSystem());
2948 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= getFromYAMLRawString(Buffer
, Lower
);
2949 EXPECT_NE(FS
.get(), nullptr);
2952 TEST_F(VFSFromYAMLTest
, YAMLVFSWriterTestHandleDirs
) {
2953 TempDir
TestDirectory("virtual-file-system-test", /*Unique*/ true);
2954 TempDir
_a(TestDirectory
.path("a"));
2955 TempDir
_b(TestDirectory
.path("b"));
2956 TempDir
_c(TestDirectory
.path("c"));
2958 vfs::YAMLVFSWriter VFSWriter
;
2959 VFSWriter
.addDirectoryMapping(_a
.path(), "//root/a");
2960 VFSWriter
.addDirectoryMapping(_b
.path(), "//root/b");
2961 VFSWriter
.addDirectoryMapping(_c
.path(), "//root/c");
2964 raw_string_ostream
OS(Buffer
);
2965 VFSWriter
.write(OS
);
2968 // We didn't add a single file - only directories.
2969 EXPECT_EQ(Buffer
.find("'type': 'file'"), std::string::npos
);
2971 IntrusiveRefCntPtr
<ErrorDummyFileSystem
> Lower(new ErrorDummyFileSystem());
2972 Lower
->addDirectory("//root/a");
2973 Lower
->addDirectory("//root/b");
2974 Lower
->addDirectory("//root/c");
2976 Lower
->addRegularFile("//root/a/a");
2977 Lower
->addRegularFile("//root/b/b");
2978 Lower
->addRegularFile("//root/c/c");
2980 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= getFromYAMLRawString(Buffer
, Lower
);
2981 ASSERT_NE(FS
.get(), nullptr);
2983 EXPECT_FALSE(FS
->exists(_a
.path("a")));
2984 EXPECT_FALSE(FS
->exists(_b
.path("b")));
2985 EXPECT_FALSE(FS
->exists(_c
.path("c")));
2988 TEST_F(VFSFromYAMLTest
, RedirectingWith
) {
2989 IntrusiveRefCntPtr
<DummyFileSystem
> Both(new DummyFileSystem());
2990 Both
->addDirectory("//root/a");
2991 Both
->addRegularFile("//root/a/f");
2992 Both
->addDirectory("//root/b");
2993 Both
->addRegularFile("//root/b/f");
2995 IntrusiveRefCntPtr
<DummyFileSystem
> AOnly(new DummyFileSystem());
2996 AOnly
->addDirectory("//root/a");
2997 AOnly
->addRegularFile("//root/a/f");
2999 IntrusiveRefCntPtr
<DummyFileSystem
> BOnly(new DummyFileSystem());
3000 BOnly
->addDirectory("//root/b");
3001 BOnly
->addRegularFile("//root/b/f");
3003 auto BaseStr
= std::string(" 'roots': [\n"
3005 " 'type': 'directory-remap',\n"
3006 " 'name': '//root/a',\n"
3007 " 'external-contents': '//root/b'\n"
3011 auto FallthroughStr
= "{ 'redirecting-with': 'fallthrough',\n" + BaseStr
;
3012 auto FallbackStr
= "{ 'redirecting-with': 'fallback',\n" + BaseStr
;
3013 auto RedirectOnlyStr
= "{ 'redirecting-with': 'redirect-only',\n" + BaseStr
;
3015 auto ExpectPath
= [&](vfs::FileSystem
&FS
, StringRef Expected
,
3016 StringRef Message
) {
3017 auto AF
= FS
.openFileForRead("//root/a/f");
3018 ASSERT_FALSE(AF
.getError()) << Message
;
3019 auto AFName
= (*AF
)->getName();
3020 ASSERT_FALSE(AFName
.getError()) << Message
;
3021 EXPECT_EQ(Expected
.str(), AFName
.get()) << Message
;
3023 auto AS
= FS
.status("//root/a/f");
3024 ASSERT_FALSE(AS
.getError()) << Message
;
3025 EXPECT_EQ(Expected
.str(), AS
->getName()) << Message
;
3028 auto ExpectFailure
= [&](vfs::FileSystem
&FS
, StringRef Message
) {
3029 EXPECT_TRUE(FS
.openFileForRead("//root/a/f").getError()) << Message
;
3030 EXPECT_TRUE(FS
.status("//root/a/f").getError()) << Message
;
3034 // `f` in both `a` and `b`
3036 // `fallthrough` tries `external-name` first, so should be `b`
3037 IntrusiveRefCntPtr
<vfs::FileSystem
> Fallthrough
=
3038 getFromYAMLString(FallthroughStr
, Both
);
3039 ASSERT_TRUE(Fallthrough
.get() != nullptr);
3040 ExpectPath(*Fallthrough
, "//root/b/f", "fallthrough, both exist");
3042 // `fallback` tries the original name first, so should be `a`
3043 IntrusiveRefCntPtr
<vfs::FileSystem
> Fallback
=
3044 getFromYAMLString(FallbackStr
, Both
);
3045 ASSERT_TRUE(Fallback
.get() != nullptr);
3046 ExpectPath(*Fallback
, "//root/a/f", "fallback, both exist");
3048 // `redirect-only` is the same as `fallthrough` but doesn't try the
3049 // original on failure, so no change here (ie. `b`)
3050 IntrusiveRefCntPtr
<vfs::FileSystem
> Redirect
=
3051 getFromYAMLString(RedirectOnlyStr
, Both
);
3052 ASSERT_TRUE(Redirect
.get() != nullptr);
3053 ExpectPath(*Redirect
, "//root/b/f", "redirect-only, both exist");
3059 // Fallthrough to the original path, `a`
3060 IntrusiveRefCntPtr
<vfs::FileSystem
> Fallthrough
=
3061 getFromYAMLString(FallthroughStr
, AOnly
);
3062 ASSERT_TRUE(Fallthrough
.get() != nullptr);
3063 ExpectPath(*Fallthrough
, "//root/a/f", "fallthrough, a only");
3065 // Original first, so still `a`
3066 IntrusiveRefCntPtr
<vfs::FileSystem
> Fallback
=
3067 getFromYAMLString(FallbackStr
, AOnly
);
3068 ASSERT_TRUE(Fallback
.get() != nullptr);
3069 ExpectPath(*Fallback
, "//root/a/f", "fallback, a only");
3071 // Fails since no fallthrough
3072 IntrusiveRefCntPtr
<vfs::FileSystem
> Redirect
=
3073 getFromYAMLString(RedirectOnlyStr
, AOnly
);
3074 ASSERT_TRUE(Redirect
.get() != nullptr);
3075 ExpectFailure(*Redirect
, "redirect-only, a only");
3081 // Tries `b` first (no fallthrough)
3082 IntrusiveRefCntPtr
<vfs::FileSystem
> Fallthrough
=
3083 getFromYAMLString(FallthroughStr
, BOnly
);
3084 ASSERT_TRUE(Fallthrough
.get() != nullptr);
3085 ExpectPath(*Fallthrough
, "//root/b/f", "fallthrough, b only");
3087 // Tries original first but then fallsback to `b`
3088 IntrusiveRefCntPtr
<vfs::FileSystem
> Fallback
=
3089 getFromYAMLString(FallbackStr
, BOnly
);
3090 ASSERT_TRUE(Fallback
.get() != nullptr);
3091 ExpectPath(*Fallback
, "//root/b/f", "fallback, b only");
3093 // Redirect exists, so uses it (`b`)
3094 IntrusiveRefCntPtr
<vfs::FileSystem
> Redirect
=
3095 getFromYAMLString(RedirectOnlyStr
, BOnly
);
3096 ASSERT_TRUE(Redirect
.get() != nullptr);
3097 ExpectPath(*Redirect
, "//root/b/f", "redirect-only, b only");
3100 EXPECT_EQ(0, NumDiagnostics
);
3103 TEST(VFSFromRemappedFilesTest
, Basic
) {
3104 IntrusiveRefCntPtr
<vfs::InMemoryFileSystem
> BaseFS
=
3105 new vfs::InMemoryFileSystem
;
3106 BaseFS
->addFile("//root/b", 0, MemoryBuffer::getMemBuffer("contents of b"));
3107 BaseFS
->addFile("//root/c", 0, MemoryBuffer::getMemBuffer("contents of c"));
3109 std::vector
<std::pair
<std::string
, std::string
>> RemappedFiles
= {
3110 {"//root/a/a", "//root/b"},
3111 {"//root/a/b/c", "//root/c"},
3113 auto RemappedFS
= vfs::RedirectingFileSystem::create(
3114 RemappedFiles
, /*UseExternalNames=*/false, *BaseFS
);
3116 auto StatA
= RemappedFS
->status("//root/a/a");
3117 auto StatB
= RemappedFS
->status("//root/a/b/c");
3120 EXPECT_EQ("//root/a/a", StatA
->getName());
3121 EXPECT_EQ("//root/a/b/c", StatB
->getName());
3123 auto BufferA
= RemappedFS
->getBufferForFile("//root/a/a");
3124 auto BufferB
= RemappedFS
->getBufferForFile("//root/a/b/c");
3125 ASSERT_TRUE(BufferA
);
3126 ASSERT_TRUE(BufferB
);
3127 EXPECT_EQ("contents of b", (*BufferA
)->getBuffer());
3128 EXPECT_EQ("contents of c", (*BufferB
)->getBuffer());
3131 TEST(VFSFromRemappedFilesTest
, UseExternalNames
) {
3132 IntrusiveRefCntPtr
<vfs::InMemoryFileSystem
> BaseFS
=
3133 new vfs::InMemoryFileSystem
;
3134 BaseFS
->addFile("//root/b", 0, MemoryBuffer::getMemBuffer("contents of b"));
3135 BaseFS
->addFile("//root/c", 0, MemoryBuffer::getMemBuffer("contents of c"));
3137 std::vector
<std::pair
<std::string
, std::string
>> RemappedFiles
= {
3138 {"//root/a/a", "//root/b"},
3139 {"//root/a/b/c", "//root/c"},
3141 auto RemappedFS
= vfs::RedirectingFileSystem::create(
3142 RemappedFiles
, /*UseExternalNames=*/true, *BaseFS
);
3144 auto StatA
= RemappedFS
->status("//root/a/a");
3145 auto StatB
= RemappedFS
->status("//root/a/b/c");
3148 EXPECT_EQ("//root/b", StatA
->getName());
3149 EXPECT_EQ("//root/c", StatB
->getName());
3151 auto BufferA
= RemappedFS
->getBufferForFile("//root/a/a");
3152 auto BufferB
= RemappedFS
->getBufferForFile("//root/a/b/c");
3153 ASSERT_TRUE(BufferA
);
3154 ASSERT_TRUE(BufferB
);
3155 EXPECT_EQ("contents of b", (*BufferA
)->getBuffer());
3156 EXPECT_EQ("contents of c", (*BufferB
)->getBuffer());
3159 TEST(VFSFromRemappedFilesTest
, LastMappingWins
) {
3160 IntrusiveRefCntPtr
<vfs::InMemoryFileSystem
> BaseFS
=
3161 new vfs::InMemoryFileSystem
;
3162 BaseFS
->addFile("//root/b", 0, MemoryBuffer::getMemBuffer("contents of b"));
3163 BaseFS
->addFile("//root/c", 0, MemoryBuffer::getMemBuffer("contents of c"));
3165 std::vector
<std::pair
<std::string
, std::string
>> RemappedFiles
= {
3166 {"//root/a", "//root/b"},
3167 {"//root/a", "//root/c"},
3169 auto RemappedFSKeepName
= vfs::RedirectingFileSystem::create(
3170 RemappedFiles
, /*UseExternalNames=*/false, *BaseFS
);
3171 auto RemappedFSExternalName
= vfs::RedirectingFileSystem::create(
3172 RemappedFiles
, /*UseExternalNames=*/true, *BaseFS
);
3174 auto StatKeepA
= RemappedFSKeepName
->status("//root/a");
3175 auto StatExternalA
= RemappedFSExternalName
->status("//root/a");
3176 ASSERT_TRUE(StatKeepA
);
3177 ASSERT_TRUE(StatExternalA
);
3178 EXPECT_EQ("//root/a", StatKeepA
->getName());
3179 EXPECT_EQ("//root/c", StatExternalA
->getName());
3181 auto BufferKeepA
= RemappedFSKeepName
->getBufferForFile("//root/a");
3182 auto BufferExternalA
= RemappedFSExternalName
->getBufferForFile("//root/a");
3183 ASSERT_TRUE(BufferKeepA
);
3184 ASSERT_TRUE(BufferExternalA
);
3185 EXPECT_EQ("contents of c", (*BufferKeepA
)->getBuffer());
3186 EXPECT_EQ("contents of c", (*BufferExternalA
)->getBuffer());
3189 TEST(RedirectingFileSystemTest
, PrintOutput
) {
3191 MemoryBuffer::getMemBuffer("{\n"
3195 " 'type': 'directory-remap',\n"
3196 " 'name': '/dremap',\n"
3197 " 'external-contents': '/a',\n"
3200 " 'type': 'directory',\n"
3201 " 'name': '/vdir',\n"
3204 " 'type': 'directory-remap',\n"
3205 " 'name': 'dremap',\n"
3206 " 'external-contents': '/b'\n"
3207 " 'use-external-name': 'true'\n"
3210 " 'type': 'file',\n"
3211 " 'name': 'vfile',\n"
3212 " 'external-contents': '/c'\n"
3213 " 'use-external-name': 'false'\n"
3218 auto Dummy
= makeIntrusiveRefCnt
<DummyFileSystem
>();
3219 auto Redirecting
= vfs::RedirectingFileSystem::create(
3220 std::move(Buffer
), nullptr, "", nullptr, Dummy
);
3222 SmallString
<0> Output
;
3223 raw_svector_ostream OuputStream
{Output
};
3225 Redirecting
->print(OuputStream
, vfs::FileSystem::PrintType::Summary
);
3226 ASSERT_EQ("RedirectingFileSystem (UseExternalNames: true)\n", Output
);
3229 Redirecting
->print(OuputStream
, vfs::FileSystem::PrintType::Contents
);
3230 ASSERT_EQ("RedirectingFileSystem (UseExternalNames: true)\n"
3232 " 'dremap' -> '/a'\n"
3234 " 'dremap' -> '/b' (UseExternalName: true)\n"
3235 " 'vfile' -> '/c' (UseExternalName: false)\n"
3237 " DummyFileSystem (Summary)\n",
3241 Redirecting
->print(OuputStream
, vfs::FileSystem::PrintType::Contents
, 1);
3242 ASSERT_EQ(" RedirectingFileSystem (UseExternalNames: true)\n"
3244 " 'dremap' -> '/a'\n"
3246 " 'dremap' -> '/b' (UseExternalName: true)\n"
3247 " 'vfile' -> '/c' (UseExternalName: false)\n"
3249 " DummyFileSystem (Summary)\n",
3253 Redirecting
->print(OuputStream
,
3254 vfs::FileSystem::PrintType::RecursiveContents
);
3255 ASSERT_EQ("RedirectingFileSystem (UseExternalNames: true)\n"
3257 " 'dremap' -> '/a'\n"
3259 " 'dremap' -> '/b' (UseExternalName: true)\n"
3260 " 'vfile' -> '/c' (UseExternalName: false)\n"
3262 " DummyFileSystem (RecursiveContents)\n",