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/IntrusiveRefCntPtr.h"
11 #include "llvm/ADT/ScopeExit.h"
12 #include "llvm/Config/llvm-config.h"
13 #include "llvm/Support/Errc.h"
14 #include "llvm/Support/FileSystem.h"
15 #include "llvm/Support/MemoryBuffer.h"
16 #include "llvm/Support/Path.h"
17 #include "llvm/Support/SourceMgr.h"
18 #include "llvm/TargetParser/Host.h"
19 #include "llvm/TargetParser/Triple.h"
20 #include "llvm/Testing/Support/SupportHelpers.h"
21 #include "gmock/gmock.h"
22 #include "gtest/gtest.h"
27 using llvm::sys::fs::UniqueID
;
28 using llvm::unittest::TempDir
;
29 using llvm::unittest::TempFile
;
30 using llvm::unittest::TempLink
;
31 using testing::ElementsAre
;
33 using testing::UnorderedElementsAre
;
36 struct DummyFile
: public vfs::File
{
38 explicit DummyFile(vfs::Status S
) : S(S
) {}
39 llvm::ErrorOr
<vfs::Status
> status() override
{ return S
; }
40 llvm::ErrorOr
<std::unique_ptr
<llvm::MemoryBuffer
>>
41 getBuffer(const Twine
&Name
, int64_t FileSize
, bool RequiresNullTerminator
,
42 bool IsVolatile
) override
{
43 llvm_unreachable("unimplemented");
45 std::error_code
close() override
{ return std::error_code(); }
48 class DummyFileSystem
: public vfs::FileSystem
{
49 int FSID
; // used to produce UniqueIDs
50 int FileID
; // used to produce UniqueIDs
51 std::string WorkingDirectory
;
52 std::map
<std::string
, vfs::Status
> FilesAndDirs
;
53 typedef std::map
<std::string
, vfs::Status
>::const_iterator const_iterator
;
55 static int getNextFSID() {
61 DummyFileSystem() : FSID(getNextFSID()), FileID(0) {}
63 ErrorOr
<vfs::Status
> status(const Twine
&Path
) override
{
64 auto I
= findEntry(Path
);
65 if (I
== FilesAndDirs
.end())
66 return make_error_code(llvm::errc::no_such_file_or_directory
);
69 ErrorOr
<std::unique_ptr
<vfs::File
>>
70 openFileForRead(const Twine
&Path
) override
{
71 auto S
= status(Path
);
73 return std::unique_ptr
<vfs::File
>(new DummyFile
{*S
});
76 llvm::ErrorOr
<std::string
> getCurrentWorkingDirectory() const override
{
77 return WorkingDirectory
;
79 std::error_code
setCurrentWorkingDirectory(const Twine
&Path
) override
{
80 WorkingDirectory
= Path
.str();
81 return std::error_code();
83 // Map any symlink to "/symlink".
84 std::error_code
getRealPath(const Twine
&Path
,
85 SmallVectorImpl
<char> &Output
) override
{
86 auto I
= findEntry(Path
);
87 if (I
== FilesAndDirs
.end())
88 return make_error_code(llvm::errc::no_such_file_or_directory
);
89 if (I
->second
.isSymlink()) {
91 Twine("/symlink").toVector(Output
);
92 return std::error_code();
95 Path
.toVector(Output
);
96 return std::error_code();
99 struct DirIterImpl
: public llvm::vfs::detail::DirIterImpl
{
100 std::map
<std::string
, vfs::Status
> &FilesAndDirs
;
101 std::map
<std::string
, vfs::Status
>::iterator I
;
103 bool isInPath(StringRef S
) {
104 if (Path
.size() < S
.size() && S
.starts_with(Path
)) {
105 auto LastSep
= S
.find_last_of('/');
106 if (LastSep
== Path
.size() || LastSep
== Path
.size() - 1)
111 DirIterImpl(std::map
<std::string
, vfs::Status
> &FilesAndDirs
,
113 : FilesAndDirs(FilesAndDirs
), I(FilesAndDirs
.begin()),
115 for (; I
!= FilesAndDirs
.end(); ++I
) {
116 if (isInPath(I
->first
)) {
117 CurrentEntry
= vfs::directory_entry(std::string(I
->second
.getName()),
118 I
->second
.getType());
123 std::error_code
increment() override
{
125 for (; I
!= FilesAndDirs
.end(); ++I
) {
126 if (isInPath(I
->first
)) {
127 CurrentEntry
= vfs::directory_entry(std::string(I
->second
.getName()),
128 I
->second
.getType());
132 if (I
== FilesAndDirs
.end())
133 CurrentEntry
= vfs::directory_entry();
134 return std::error_code();
138 vfs::directory_iterator
dir_begin(const Twine
&Dir
,
139 std::error_code
&EC
) override
{
140 return vfs::directory_iterator(
141 std::make_shared
<DirIterImpl
>(FilesAndDirs
, Dir
));
144 void addEntry(StringRef Path
, const vfs::Status
&Status
) {
145 FilesAndDirs
[std::string(Path
)] = Status
;
148 const_iterator
findEntry(const Twine
&Path
) const {
151 std::error_code EC
= makeAbsolute(P
);
154 return FilesAndDirs
.find(std::string(P
.str()));
157 void addRegularFile(StringRef Path
, sys::fs::perms Perms
= sys::fs::all_all
) {
158 vfs::Status
S(Path
, UniqueID(FSID
, FileID
++),
159 std::chrono::system_clock::now(), 0, 0, 1024,
160 sys::fs::file_type::regular_file
, Perms
);
164 void addDirectory(StringRef Path
, sys::fs::perms Perms
= sys::fs::all_all
) {
165 vfs::Status
S(Path
, UniqueID(FSID
, FileID
++),
166 std::chrono::system_clock::now(), 0, 0, 0,
167 sys::fs::file_type::directory_file
, Perms
);
171 void addSymlink(StringRef Path
) {
172 vfs::Status
S(Path
, UniqueID(FSID
, FileID
++),
173 std::chrono::system_clock::now(), 0, 0, 0,
174 sys::fs::file_type::symlink_file
, sys::fs::all_all
);
179 void printImpl(raw_ostream
&OS
, PrintType Type
,
180 unsigned IndentLevel
) const override
{
181 printIndent(OS
, IndentLevel
);
182 OS
<< "DummyFileSystem (";
184 case vfs::FileSystem::PrintType::Summary
:
187 case vfs::FileSystem::PrintType::Contents
:
190 case vfs::FileSystem::PrintType::RecursiveContents
:
191 OS
<< "RecursiveContents";
198 class ErrorDummyFileSystem
: public DummyFileSystem
{
199 std::error_code
setCurrentWorkingDirectory(const Twine
&Path
) override
{
200 return llvm::errc::no_such_file_or_directory
;
204 /// A version of \c DummyFileSystem that aborts on \c status() to test that
205 /// \c exists() is being used.
206 class NoStatusDummyFileSystem
: public DummyFileSystem
{
208 ErrorOr
<vfs::Status
> status(const Twine
&Path
) override
{
209 llvm::report_fatal_error(
210 "unexpected call to NoStatusDummyFileSystem::status");
213 bool exists(const Twine
&Path
) override
{
214 auto Status
= DummyFileSystem::status(Path
);
215 return Status
&& Status
->exists();
219 /// Replace back-slashes by front-slashes.
220 std::string
getPosixPath(const Twine
&S
) {
221 SmallString
<128> Result
;
222 llvm::sys::path::native(S
, Result
, llvm::sys::path::Style::posix
);
223 return std::string(Result
.str());
225 } // end anonymous namespace
227 TEST(VirtualFileSystemTest
, StatusQueries
) {
228 IntrusiveRefCntPtr
<DummyFileSystem
> D(new DummyFileSystem());
229 ErrorOr
<vfs::Status
> Status((std::error_code()));
231 D
->addRegularFile("/foo");
232 Status
= D
->status("/foo");
233 ASSERT_FALSE(Status
.getError());
234 EXPECT_TRUE(Status
->isStatusKnown());
235 EXPECT_FALSE(Status
->isDirectory());
236 EXPECT_TRUE(Status
->isRegularFile());
237 EXPECT_FALSE(Status
->isSymlink());
238 EXPECT_FALSE(Status
->isOther());
239 EXPECT_TRUE(Status
->exists());
241 D
->addDirectory("/bar");
242 Status
= D
->status("/bar");
243 ASSERT_FALSE(Status
.getError());
244 EXPECT_TRUE(Status
->isStatusKnown());
245 EXPECT_TRUE(Status
->isDirectory());
246 EXPECT_FALSE(Status
->isRegularFile());
247 EXPECT_FALSE(Status
->isSymlink());
248 EXPECT_FALSE(Status
->isOther());
249 EXPECT_TRUE(Status
->exists());
251 D
->addSymlink("/baz");
252 Status
= D
->status("/baz");
253 ASSERT_FALSE(Status
.getError());
254 EXPECT_TRUE(Status
->isStatusKnown());
255 EXPECT_FALSE(Status
->isDirectory());
256 EXPECT_FALSE(Status
->isRegularFile());
257 EXPECT_TRUE(Status
->isSymlink());
258 EXPECT_FALSE(Status
->isOther());
259 EXPECT_TRUE(Status
->exists());
261 EXPECT_TRUE(Status
->equivalent(*Status
));
262 ErrorOr
<vfs::Status
> Status2
= D
->status("/foo");
263 ASSERT_FALSE(Status2
.getError());
264 EXPECT_FALSE(Status
->equivalent(*Status2
));
267 TEST(VirtualFileSystemTest
, BaseOnlyOverlay
) {
268 IntrusiveRefCntPtr
<DummyFileSystem
> D(new DummyFileSystem());
269 ErrorOr
<vfs::Status
> Status((std::error_code()));
270 EXPECT_FALSE(Status
= D
->status("/foo"));
272 IntrusiveRefCntPtr
<vfs::OverlayFileSystem
> O(new vfs::OverlayFileSystem(D
));
273 EXPECT_FALSE(Status
= O
->status("/foo"));
275 D
->addRegularFile("/foo");
276 Status
= D
->status("/foo");
277 EXPECT_FALSE(Status
.getError());
279 ErrorOr
<vfs::Status
> Status2((std::error_code()));
280 Status2
= O
->status("/foo");
281 EXPECT_FALSE(Status2
.getError());
282 EXPECT_TRUE(Status
->equivalent(*Status2
));
285 TEST(VirtualFileSystemTest
, GetRealPathInOverlay
) {
286 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
287 Lower
->addRegularFile("/foo");
288 Lower
->addSymlink("/lower_link");
289 IntrusiveRefCntPtr
<DummyFileSystem
> Upper(new DummyFileSystem());
291 IntrusiveRefCntPtr
<vfs::OverlayFileSystem
> O(
292 new vfs::OverlayFileSystem(Lower
));
293 O
->pushOverlay(Upper
);
296 SmallString
<16> RealPath
;
297 EXPECT_FALSE(O
->getRealPath("/foo", RealPath
));
298 EXPECT_EQ(RealPath
.str(), "/foo");
300 // Expect no error getting real path for symlink in lower overlay.
301 EXPECT_FALSE(O
->getRealPath("/lower_link", RealPath
));
302 EXPECT_EQ(RealPath
.str(), "/symlink");
304 // Try a non-existing link.
305 EXPECT_EQ(O
->getRealPath("/upper_link", RealPath
),
306 errc::no_such_file_or_directory
);
308 // Add a new symlink in upper.
309 Upper
->addSymlink("/upper_link");
310 EXPECT_FALSE(O
->getRealPath("/upper_link", RealPath
));
311 EXPECT_EQ(RealPath
.str(), "/symlink");
314 TEST(VirtualFileSystemTest
, OverlayFiles
) {
315 IntrusiveRefCntPtr
<DummyFileSystem
> Base(new DummyFileSystem());
316 IntrusiveRefCntPtr
<DummyFileSystem
> Middle(new DummyFileSystem());
317 IntrusiveRefCntPtr
<DummyFileSystem
> Top(new DummyFileSystem());
318 IntrusiveRefCntPtr
<vfs::OverlayFileSystem
> O(
319 new vfs::OverlayFileSystem(Base
));
320 O
->pushOverlay(Middle
);
323 ErrorOr
<vfs::Status
> Status1((std::error_code())),
324 Status2((std::error_code())), Status3((std::error_code())),
325 StatusB((std::error_code())), StatusM((std::error_code())),
326 StatusT((std::error_code()));
328 Base
->addRegularFile("/foo");
329 StatusB
= Base
->status("/foo");
330 ASSERT_FALSE(StatusB
.getError());
331 Status1
= O
->status("/foo");
332 ASSERT_FALSE(Status1
.getError());
333 Middle
->addRegularFile("/foo");
334 StatusM
= Middle
->status("/foo");
335 ASSERT_FALSE(StatusM
.getError());
336 Status2
= O
->status("/foo");
337 ASSERT_FALSE(Status2
.getError());
338 Top
->addRegularFile("/foo");
339 StatusT
= Top
->status("/foo");
340 ASSERT_FALSE(StatusT
.getError());
341 Status3
= O
->status("/foo");
342 ASSERT_FALSE(Status3
.getError());
344 EXPECT_TRUE(Status1
->equivalent(*StatusB
));
345 EXPECT_TRUE(Status2
->equivalent(*StatusM
));
346 EXPECT_TRUE(Status3
->equivalent(*StatusT
));
348 EXPECT_FALSE(Status1
->equivalent(*Status2
));
349 EXPECT_FALSE(Status2
->equivalent(*Status3
));
350 EXPECT_FALSE(Status1
->equivalent(*Status3
));
353 TEST(VirtualFileSystemTest
, OverlayDirsNonMerged
) {
354 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
355 IntrusiveRefCntPtr
<DummyFileSystem
> Upper(new DummyFileSystem());
356 IntrusiveRefCntPtr
<vfs::OverlayFileSystem
> O(
357 new vfs::OverlayFileSystem(Lower
));
358 O
->pushOverlay(Upper
);
360 Lower
->addDirectory("/lower-only");
361 Upper
->addDirectory("/upper-only");
363 // non-merged paths should be the same
364 ErrorOr
<vfs::Status
> Status1
= Lower
->status("/lower-only");
365 ASSERT_FALSE(Status1
.getError());
366 ErrorOr
<vfs::Status
> Status2
= O
->status("/lower-only");
367 ASSERT_FALSE(Status2
.getError());
368 EXPECT_TRUE(Status1
->equivalent(*Status2
));
370 Status1
= Upper
->status("/upper-only");
371 ASSERT_FALSE(Status1
.getError());
372 Status2
= O
->status("/upper-only");
373 ASSERT_FALSE(Status2
.getError());
374 EXPECT_TRUE(Status1
->equivalent(*Status2
));
377 TEST(VirtualFileSystemTest
, MergedDirPermissions
) {
378 // merged directories get the permissions of the upper dir
379 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
380 IntrusiveRefCntPtr
<DummyFileSystem
> Upper(new DummyFileSystem());
381 IntrusiveRefCntPtr
<vfs::OverlayFileSystem
> O(
382 new vfs::OverlayFileSystem(Lower
));
383 O
->pushOverlay(Upper
);
385 ErrorOr
<vfs::Status
> Status((std::error_code()));
386 Lower
->addDirectory("/both", sys::fs::owner_read
);
387 Upper
->addDirectory("/both", sys::fs::owner_all
| sys::fs::group_read
);
388 Status
= O
->status("/both");
389 ASSERT_FALSE(Status
.getError());
390 EXPECT_EQ(0740, Status
->getPermissions());
392 // permissions (as usual) are not recursively applied
393 Lower
->addRegularFile("/both/foo", sys::fs::owner_read
);
394 Upper
->addRegularFile("/both/bar", sys::fs::owner_write
);
395 Status
= O
->status("/both/foo");
396 ASSERT_FALSE(Status
.getError());
397 EXPECT_EQ(0400, Status
->getPermissions());
398 Status
= O
->status("/both/bar");
399 ASSERT_FALSE(Status
.getError());
400 EXPECT_EQ(0200, Status
->getPermissions());
403 TEST(VirtualFileSystemTest
, OverlayIterator
) {
404 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
405 Lower
->addRegularFile("/foo");
406 IntrusiveRefCntPtr
<DummyFileSystem
> Upper(new DummyFileSystem());
408 IntrusiveRefCntPtr
<vfs::OverlayFileSystem
> O(
409 new vfs::OverlayFileSystem(Lower
));
410 O
->pushOverlay(Upper
);
412 ErrorOr
<vfs::Status
> Status((std::error_code()));
414 auto it
= O
->overlays_begin();
415 auto end
= O
->overlays_end();
419 Status
= (*it
)->status("/foo");
420 ASSERT_TRUE(Status
.getError());
425 Status
= (*it
)->status("/foo");
426 ASSERT_FALSE(Status
.getError());
427 EXPECT_TRUE(Status
->exists());
434 auto it
= O
->overlays_rbegin();
435 auto end
= O
->overlays_rend();
439 Status
= (*it
)->status("/foo");
440 ASSERT_FALSE(Status
.getError());
441 EXPECT_TRUE(Status
->exists());
446 Status
= (*it
)->status("/foo");
447 ASSERT_TRUE(Status
.getError());
454 TEST(VirtualFileSystemTest
, BasicRealFSIteration
) {
455 TempDir
TestDirectory("virtual-file-system-test", /*Unique*/ true);
456 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= vfs::getRealFileSystem();
459 vfs::directory_iterator I
= FS
->dir_begin(Twine(TestDirectory
.path()), EC
);
461 EXPECT_EQ(vfs::directory_iterator(), I
); // empty directory is empty
463 TempDir
_a(TestDirectory
.path("a"));
464 TempDir
_ab(TestDirectory
.path("a/b"));
465 TempDir
_c(TestDirectory
.path("c"));
466 TempDir
_cd(TestDirectory
.path("c/d"));
468 I
= FS
->dir_begin(Twine(TestDirectory
.path()), EC
);
470 ASSERT_NE(vfs::directory_iterator(), I
);
471 // Check either a or c, since we can't rely on the iteration order.
472 EXPECT_TRUE(I
->path().ends_with("a") || I
->path().ends_with("c"));
475 ASSERT_NE(vfs::directory_iterator(), I
);
476 EXPECT_TRUE(I
->path().ends_with("a") || I
->path().ends_with("c"));
478 EXPECT_EQ(vfs::directory_iterator(), I
);
482 TEST(VirtualFileSystemTest
, MultipleWorkingDirs
) {
483 // Our root contains a/aa, b/bb, c, where c is a link to a/.
484 // Run tests both in root/b/ and root/c/ (to test "normal" and symlink dirs).
485 // Interleave operations to show the working directories are independent.
486 TempDir
Root("r", /*Unique*/ true);
487 TempDir
ADir(Root
.path("a"));
488 TempDir
BDir(Root
.path("b"));
489 TempLink
C(ADir
.path(), Root
.path("c"));
490 TempFile
AA(ADir
.path("aa"), "", "aaaa");
491 TempFile
BB(BDir
.path("bb"), "", "bbbb");
492 std::unique_ptr
<vfs::FileSystem
> BFS
= vfs::createPhysicalFileSystem(),
493 CFS
= vfs::createPhysicalFileSystem();
495 ASSERT_FALSE(BFS
->setCurrentWorkingDirectory(BDir
.path()));
496 ASSERT_FALSE(CFS
->setCurrentWorkingDirectory(C
.path()));
497 EXPECT_EQ(BDir
.path(), *BFS
->getCurrentWorkingDirectory());
498 EXPECT_EQ(C
.path(), *CFS
->getCurrentWorkingDirectory());
500 // openFileForRead(), indirectly.
501 auto BBuf
= BFS
->getBufferForFile("bb");
503 EXPECT_EQ("bbbb", (*BBuf
)->getBuffer());
505 auto ABuf
= CFS
->getBufferForFile("aa");
507 EXPECT_EQ("aaaa", (*ABuf
)->getBuffer());
510 auto BStat
= BFS
->status("bb");
512 EXPECT_EQ("bb", BStat
->getName());
514 auto AStat
= CFS
->status("aa");
516 EXPECT_EQ("aa", AStat
->getName()); // unresolved name
519 SmallString
<128> BPath
;
520 ASSERT_FALSE(BFS
->getRealPath("bb", BPath
));
521 EXPECT_EQ(BB
.path(), BPath
);
523 SmallString
<128> APath
;
524 ASSERT_FALSE(CFS
->getRealPath("aa", APath
));
525 EXPECT_EQ(AA
.path(), APath
); // Reports resolved name.
529 auto BIt
= BFS
->dir_begin(".", EC
);
531 ASSERT_NE(BIt
, vfs::directory_iterator());
532 EXPECT_EQ((BDir
.path() + "/./bb").str(), BIt
->path());
535 ASSERT_EQ(BIt
, vfs::directory_iterator());
537 auto CIt
= CFS
->dir_begin(".", EC
);
539 ASSERT_NE(CIt
, vfs::directory_iterator());
540 EXPECT_EQ((ADir
.path() + "/./aa").str(),
541 CIt
->path()); // Partly resolved name!
542 CIt
.increment(EC
); // Because likely to read through this path.
544 ASSERT_EQ(CIt
, vfs::directory_iterator());
547 TEST(VirtualFileSystemTest
, PhysicalFileSystemWorkingDirFailure
) {
548 TempDir
D2("d2", /*Unique*/ true);
549 SmallString
<128> WD
, PrevWD
;
550 ASSERT_EQ(sys::fs::current_path(PrevWD
), std::error_code());
551 ASSERT_EQ(sys::fs::createUniqueDirectory("d1", WD
), std::error_code());
552 ASSERT_EQ(sys::fs::set_current_path(WD
), std::error_code());
554 llvm::make_scope_exit([&] { sys::fs::set_current_path(PrevWD
); });
556 // Delete the working directory to create an error.
557 if (sys::fs::remove_directories(WD
, /*IgnoreErrors=*/false))
558 // Some platforms (e.g. Solaris) disallow removal of the working directory.
559 GTEST_SKIP() << "test requires deletion of working directory";
561 // Verify that we still get two separate working directories.
562 auto FS1
= vfs::createPhysicalFileSystem();
563 auto FS2
= vfs::createPhysicalFileSystem();
564 ASSERT_EQ(FS1
->getCurrentWorkingDirectory().getError(),
565 errc::no_such_file_or_directory
);
566 ASSERT_EQ(FS1
->setCurrentWorkingDirectory(D2
.path()), std::error_code());
567 ASSERT_EQ(FS1
->getCurrentWorkingDirectory().get(), D2
.path());
568 EXPECT_EQ(FS2
->getCurrentWorkingDirectory().getError(),
569 errc::no_such_file_or_directory
);
570 SmallString
<128> WD2
;
571 EXPECT_EQ(sys::fs::current_path(WD2
), errc::no_such_file_or_directory
);
574 TEST(VirtualFileSystemTest
, BrokenSymlinkRealFSIteration
) {
575 TempDir
TestDirectory("virtual-file-system-test", /*Unique*/ true);
576 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= vfs::getRealFileSystem();
578 TempLink
_a("no_such_file", TestDirectory
.path("a"));
579 TempDir
_b(TestDirectory
.path("b"));
580 TempLink
_c("no_such_file", TestDirectory
.path("c"));
582 // Should get no iteration error, but a stat error for the broken symlinks.
583 std::map
<std::string
, std::error_code
> StatResults
;
585 for (vfs::directory_iterator
586 I
= FS
->dir_begin(Twine(TestDirectory
.path()), EC
),
588 I
!= E
; I
.increment(EC
)) {
590 StatResults
[std::string(sys::path::filename(I
->path()))] =
591 FS
->status(I
->path()).getError();
596 Pair("a", std::make_error_code(std::errc::no_such_file_or_directory
)),
597 Pair("b", std::error_code()),
599 std::make_error_code(std::errc::no_such_file_or_directory
))));
603 TEST(VirtualFileSystemTest
, BasicRealFSRecursiveIteration
) {
604 TempDir
TestDirectory("virtual-file-system-test", /*Unique*/ true);
605 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= vfs::getRealFileSystem();
609 vfs::recursive_directory_iterator(*FS
, Twine(TestDirectory
.path()), EC
);
611 EXPECT_EQ(vfs::recursive_directory_iterator(), I
); // empty directory is empty
613 TempDir
_a(TestDirectory
.path("a"));
614 TempDir
_ab(TestDirectory
.path("a/b"));
615 TempDir
_c(TestDirectory
.path("c"));
616 TempDir
_cd(TestDirectory
.path("c/d"));
618 I
= vfs::recursive_directory_iterator(*FS
, Twine(TestDirectory
.path()), EC
);
620 ASSERT_NE(vfs::recursive_directory_iterator(), I
);
622 std::vector
<std::string
> Contents
;
623 for (auto E
= vfs::recursive_directory_iterator(); !EC
&& I
!= E
;
625 Contents
.push_back(std::string(I
->path()));
628 // Check contents, which may be in any order
629 EXPECT_EQ(4U, Contents
.size());
630 int Counts
[4] = {0, 0, 0, 0};
631 for (const std::string
&Name
: Contents
) {
632 ASSERT_FALSE(Name
.empty());
633 int Index
= Name
[Name
.size() - 1] - 'a';
638 EXPECT_EQ(1, Counts
[0]); // a
639 EXPECT_EQ(1, Counts
[1]); // b
640 EXPECT_EQ(1, Counts
[2]); // c
641 EXPECT_EQ(1, Counts
[3]); // d
644 TEST(VirtualFileSystemTest
, BasicRealFSRecursiveIterationNoPush
) {
645 TempDir
TestDirectory("virtual-file-system-test", /*Unique*/ true);
647 TempDir
_a(TestDirectory
.path("a"));
648 TempDir
_ab(TestDirectory
.path("a/b"));
649 TempDir
_c(TestDirectory
.path("c"));
650 TempDir
_cd(TestDirectory
.path("c/d"));
651 TempDir
_e(TestDirectory
.path("e"));
652 TempDir
_ef(TestDirectory
.path("e/f"));
653 TempDir
_g(TestDirectory
.path("g"));
655 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= vfs::getRealFileSystem();
657 // Test that calling no_push on entries without subdirectories has no effect.
661 vfs::recursive_directory_iterator(*FS
, Twine(TestDirectory
.path()), EC
);
664 std::vector
<std::string
> Contents
;
665 for (auto E
= vfs::recursive_directory_iterator(); !EC
&& I
!= E
;
667 Contents
.push_back(std::string(I
->path()));
668 char last
= I
->path().back();
680 EXPECT_EQ(7U, Contents
.size());
683 // Test that calling no_push skips subdirectories.
687 vfs::recursive_directory_iterator(*FS
, Twine(TestDirectory
.path()), EC
);
690 std::vector
<std::string
> Contents
;
691 for (auto E
= vfs::recursive_directory_iterator(); !EC
&& I
!= E
;
693 Contents
.push_back(std::string(I
->path()));
694 char last
= I
->path().back();
706 // Check contents, which may be in any order
707 EXPECT_EQ(4U, Contents
.size());
708 int Counts
[7] = {0, 0, 0, 0, 0, 0, 0};
709 for (const std::string
&Name
: Contents
) {
710 ASSERT_FALSE(Name
.empty());
711 int Index
= Name
[Name
.size() - 1] - 'a';
716 EXPECT_EQ(1, Counts
[0]); // a
717 EXPECT_EQ(0, Counts
[1]); // b
718 EXPECT_EQ(1, Counts
[2]); // c
719 EXPECT_EQ(0, Counts
[3]); // d
720 EXPECT_EQ(1, Counts
[4]); // e
721 EXPECT_EQ(0, Counts
[5]); // f
722 EXPECT_EQ(1, Counts
[6]); // g
727 TEST(VirtualFileSystemTest
, BrokenSymlinkRealFSRecursiveIteration
) {
728 TempDir
TestDirectory("virtual-file-system-test", /*Unique*/ true);
729 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= vfs::getRealFileSystem();
731 TempLink
_a("no_such_file", TestDirectory
.path("a"));
732 TempDir
_b(TestDirectory
.path("b"));
733 TempLink
_ba("no_such_file", TestDirectory
.path("b/a"));
734 TempDir
_bb(TestDirectory
.path("b/b"));
735 TempLink
_bc("no_such_file", TestDirectory
.path("b/c"));
736 TempLink
_c("no_such_file", TestDirectory
.path("c"));
737 TempDir
_d(TestDirectory
.path("d"));
738 TempDir
_dd(TestDirectory
.path("d/d"));
739 TempDir
_ddd(TestDirectory
.path("d/d/d"));
740 TempLink
_e("no_such_file", TestDirectory
.path("e"));
742 std::vector
<std::string
> VisitedBrokenSymlinks
;
743 std::vector
<std::string
> VisitedNonBrokenSymlinks
;
745 for (vfs::recursive_directory_iterator
746 I(*FS
, Twine(TestDirectory
.path()), EC
),
748 I
!= E
; I
.increment(EC
)) {
750 (FS
->status(I
->path()) ? VisitedNonBrokenSymlinks
: VisitedBrokenSymlinks
)
751 .push_back(std::string(I
->path()));
754 // Check visited file names.
755 EXPECT_THAT(VisitedBrokenSymlinks
,
756 UnorderedElementsAre(_a
.path().str(), _ba
.path().str(),
757 _bc
.path().str(), _c
.path().str(),
759 EXPECT_THAT(VisitedNonBrokenSymlinks
,
760 UnorderedElementsAre(_b
.path().str(), _bb
.path().str(),
761 _d
.path().str(), _dd
.path().str(),
766 template <typename DirIter
>
767 static void checkContents(DirIter I
, ArrayRef
<StringRef
> ExpectedOut
) {
769 SmallVector
<StringRef
, 4> Expected(ExpectedOut
.begin(), ExpectedOut
.end());
770 SmallVector
<std::string
, 4> InputToCheck
;
772 // Do not rely on iteration order to check for contents, sort both
773 // content vectors before comparison.
774 for (DirIter E
; !EC
&& I
!= E
; I
.increment(EC
))
775 InputToCheck
.push_back(std::string(I
->path()));
777 llvm::sort(InputToCheck
);
778 llvm::sort(Expected
);
779 EXPECT_EQ(InputToCheck
.size(), Expected
.size());
781 unsigned LastElt
= std::min(InputToCheck
.size(), Expected
.size());
782 for (unsigned Idx
= 0; Idx
!= LastElt
; ++Idx
)
783 EXPECT_EQ(StringRef(InputToCheck
[Idx
]), Expected
[Idx
]);
786 TEST(VirtualFileSystemTest
, OverlayIteration
) {
787 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
788 IntrusiveRefCntPtr
<DummyFileSystem
> Upper(new DummyFileSystem());
789 IntrusiveRefCntPtr
<vfs::OverlayFileSystem
> O(
790 new vfs::OverlayFileSystem(Lower
));
791 O
->pushOverlay(Upper
);
794 checkContents(O
->dir_begin("/", EC
), ArrayRef
<StringRef
>());
796 Lower
->addRegularFile("/file1");
797 checkContents(O
->dir_begin("/", EC
), ArrayRef
<StringRef
>("/file1"));
799 Upper
->addRegularFile("/file2");
800 checkContents(O
->dir_begin("/", EC
), {"/file2", "/file1"});
802 Lower
->addDirectory("/dir1");
803 Lower
->addRegularFile("/dir1/foo");
804 Upper
->addDirectory("/dir2");
805 Upper
->addRegularFile("/dir2/foo");
806 checkContents(O
->dir_begin("/dir2", EC
), ArrayRef
<StringRef
>("/dir2/foo"));
807 checkContents(O
->dir_begin("/", EC
), {"/dir2", "/file2", "/dir1", "/file1"});
810 TEST(VirtualFileSystemTest
, OverlayRecursiveIteration
) {
811 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
812 IntrusiveRefCntPtr
<DummyFileSystem
> Middle(new DummyFileSystem());
813 IntrusiveRefCntPtr
<DummyFileSystem
> Upper(new DummyFileSystem());
814 IntrusiveRefCntPtr
<vfs::OverlayFileSystem
> O(
815 new vfs::OverlayFileSystem(Lower
));
816 O
->pushOverlay(Middle
);
817 O
->pushOverlay(Upper
);
820 checkContents(vfs::recursive_directory_iterator(*O
, "/", EC
),
821 ArrayRef
<StringRef
>());
823 Lower
->addRegularFile("/file1");
824 checkContents(vfs::recursive_directory_iterator(*O
, "/", EC
),
825 ArrayRef
<StringRef
>("/file1"));
827 Upper
->addDirectory("/dir");
828 Upper
->addRegularFile("/dir/file2");
829 checkContents(vfs::recursive_directory_iterator(*O
, "/", EC
),
830 {"/dir", "/dir/file2", "/file1"});
832 Lower
->addDirectory("/dir1");
833 Lower
->addRegularFile("/dir1/foo");
834 Lower
->addDirectory("/dir1/a");
835 Lower
->addRegularFile("/dir1/a/b");
836 Middle
->addDirectory("/a");
837 Middle
->addDirectory("/a/b");
838 Middle
->addDirectory("/a/b/c");
839 Middle
->addRegularFile("/a/b/c/d");
840 Middle
->addRegularFile("/hiddenByUp");
841 Upper
->addDirectory("/dir2");
842 Upper
->addRegularFile("/dir2/foo");
843 Upper
->addRegularFile("/hiddenByUp");
844 checkContents(vfs::recursive_directory_iterator(*O
, "/dir2", EC
),
845 ArrayRef
<StringRef
>("/dir2/foo"));
846 checkContents(vfs::recursive_directory_iterator(*O
, "/", EC
),
847 {"/dir", "/dir/file2", "/dir2", "/dir2/foo", "/hiddenByUp",
848 "/a", "/a/b", "/a/b/c", "/a/b/c/d", "/dir1", "/dir1/a",
849 "/dir1/a/b", "/dir1/foo", "/file1"});
852 TEST(VirtualFileSystemTest
, ThreeLevelIteration
) {
853 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
854 IntrusiveRefCntPtr
<DummyFileSystem
> Middle(new DummyFileSystem());
855 IntrusiveRefCntPtr
<DummyFileSystem
> Upper(new DummyFileSystem());
856 IntrusiveRefCntPtr
<vfs::OverlayFileSystem
> O(
857 new vfs::OverlayFileSystem(Lower
));
858 O
->pushOverlay(Middle
);
859 O
->pushOverlay(Upper
);
862 checkContents(O
->dir_begin("/", EC
), ArrayRef
<StringRef
>());
864 Middle
->addRegularFile("/file2");
865 checkContents(O
->dir_begin("/", EC
), ArrayRef
<StringRef
>("/file2"));
867 Lower
->addRegularFile("/file1");
868 Upper
->addRegularFile("/file3");
869 checkContents(O
->dir_begin("/", EC
), {"/file3", "/file2", "/file1"});
872 TEST(VirtualFileSystemTest
, HiddenInIteration
) {
873 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
874 IntrusiveRefCntPtr
<DummyFileSystem
> Middle(new DummyFileSystem());
875 IntrusiveRefCntPtr
<DummyFileSystem
> Upper(new DummyFileSystem());
876 IntrusiveRefCntPtr
<vfs::OverlayFileSystem
> O(
877 new vfs::OverlayFileSystem(Lower
));
878 O
->pushOverlay(Middle
);
879 O
->pushOverlay(Upper
);
882 Lower
->addRegularFile("/onlyInLow");
883 Lower
->addDirectory("/hiddenByMid");
884 Lower
->addDirectory("/hiddenByUp");
885 Middle
->addRegularFile("/onlyInMid");
886 Middle
->addRegularFile("/hiddenByMid");
887 Middle
->addDirectory("/hiddenByUp");
888 Upper
->addRegularFile("/onlyInUp");
889 Upper
->addRegularFile("/hiddenByUp");
891 O
->dir_begin("/", EC
),
892 {"/hiddenByUp", "/onlyInUp", "/hiddenByMid", "/onlyInMid", "/onlyInLow"});
894 // Make sure we get the top-most entry
897 vfs::directory_iterator I
= O
->dir_begin("/", EC
), E
;
898 for (; !EC
&& I
!= E
; I
.increment(EC
))
899 if (I
->path() == "/hiddenByUp")
902 EXPECT_EQ(sys::fs::file_type::regular_file
, I
->type());
906 vfs::directory_iterator I
= O
->dir_begin("/", EC
), E
;
907 for (; !EC
&& I
!= E
; I
.increment(EC
))
908 if (I
->path() == "/hiddenByMid")
911 EXPECT_EQ(sys::fs::file_type::regular_file
, I
->type());
915 TEST(VirtualFileSystemTest
, Visit
) {
916 IntrusiveRefCntPtr
<DummyFileSystem
> Base(new DummyFileSystem());
917 IntrusiveRefCntPtr
<DummyFileSystem
> Middle(new DummyFileSystem());
918 IntrusiveRefCntPtr
<DummyFileSystem
> Top(new DummyFileSystem());
919 IntrusiveRefCntPtr
<vfs::OverlayFileSystem
> O(
920 new vfs::OverlayFileSystem(Base
));
921 O
->pushOverlay(Middle
);
925 MemoryBuffer::getMemBuffer("{\n"
927 " 'redirecting-with': 'redirect-only',\n"
931 " 'name': '/vfile',\n"
932 " 'external-contents': '/a',\n"
937 IntrusiveRefCntPtr
<vfs::RedirectingFileSystem
> Redirecting
=
938 vfs::RedirectingFileSystem::create(std::move(YAML
), nullptr, "", nullptr,
942 vfs::ProxyFileSystem
PFS(Redirecting
);
944 std::vector
<const vfs::FileSystem
*> FSs
;
945 PFS
.visit([&](const vfs::FileSystem
&FS
) { FSs
.push_back(&FS
); });
947 ASSERT_EQ(size_t(6), FSs
.size());
948 EXPECT_TRUE(isa
<vfs::ProxyFileSystem
>(FSs
[0]));
949 EXPECT_TRUE(isa
<vfs::RedirectingFileSystem
>(FSs
[1]));
950 EXPECT_TRUE(isa
<vfs::OverlayFileSystem
>(FSs
[2]));
951 EXPECT_TRUE(isa
<vfs::FileSystem
>(FSs
[3]));
952 EXPECT_TRUE(isa
<vfs::FileSystem
>(FSs
[4]));
953 EXPECT_TRUE(isa
<vfs::FileSystem
>(FSs
[5]));
956 TEST(OverlayFileSystemTest
, PrintOutput
) {
957 auto Dummy
= makeIntrusiveRefCnt
<DummyFileSystem
>();
958 auto Overlay1
= makeIntrusiveRefCnt
<vfs::OverlayFileSystem
>(Dummy
);
959 Overlay1
->pushOverlay(Dummy
);
960 auto Overlay2
= makeIntrusiveRefCnt
<vfs::OverlayFileSystem
>(Overlay1
);
961 Overlay2
->pushOverlay(Dummy
);
963 SmallString
<0> Output
;
964 raw_svector_ostream OuputStream
{Output
};
966 Overlay2
->print(OuputStream
, vfs::FileSystem::PrintType::Summary
);
967 ASSERT_EQ("OverlayFileSystem\n", Output
);
970 Overlay2
->print(OuputStream
, vfs::FileSystem::PrintType::Contents
);
971 ASSERT_EQ("OverlayFileSystem\n"
972 " DummyFileSystem (Summary)\n"
973 " OverlayFileSystem\n",
977 Overlay2
->print(OuputStream
, vfs::FileSystem::PrintType::RecursiveContents
);
978 ASSERT_EQ("OverlayFileSystem\n"
979 " DummyFileSystem (RecursiveContents)\n"
980 " OverlayFileSystem\n"
981 " DummyFileSystem (RecursiveContents)\n"
982 " DummyFileSystem (RecursiveContents)\n",
986 TEST(OverlayFileSystemTest
, Exists
) {
987 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new NoStatusDummyFileSystem());
988 IntrusiveRefCntPtr
<DummyFileSystem
> Upper(new NoStatusDummyFileSystem());
989 IntrusiveRefCntPtr
<vfs::OverlayFileSystem
> O(
990 new vfs::OverlayFileSystem(Lower
));
991 O
->pushOverlay(Upper
);
993 Lower
->addDirectory("/both");
994 Upper
->addDirectory("/both");
995 Lower
->addRegularFile("/both/lower_file");
996 Upper
->addRegularFile("/both/upper_file");
997 Lower
->addDirectory("/lower");
998 Upper
->addDirectory("/upper");
1000 EXPECT_TRUE(O
->exists("/both"));
1001 EXPECT_TRUE(O
->exists("/both"));
1002 EXPECT_TRUE(O
->exists("/both/lower_file"));
1003 EXPECT_TRUE(O
->exists("/both/upper_file"));
1004 EXPECT_TRUE(O
->exists("/lower"));
1005 EXPECT_TRUE(O
->exists("/upper"));
1006 EXPECT_FALSE(O
->exists("/both/nope"));
1007 EXPECT_FALSE(O
->exists("/nope"));
1010 TEST(ProxyFileSystemTest
, Basic
) {
1011 IntrusiveRefCntPtr
<vfs::InMemoryFileSystem
> Base(
1012 new vfs::InMemoryFileSystem());
1013 vfs::ProxyFileSystem
PFS(Base
);
1015 Base
->addFile("/a", 0, MemoryBuffer::getMemBuffer("test"));
1017 auto Stat
= PFS
.status("/a");
1018 ASSERT_FALSE(Stat
.getError());
1020 auto File
= PFS
.openFileForRead("/a");
1021 ASSERT_FALSE(File
.getError());
1022 EXPECT_EQ("test", (*(*File
)->getBuffer("ignored"))->getBuffer());
1025 vfs::directory_iterator I
= PFS
.dir_begin("/", EC
);
1027 ASSERT_EQ("/a", I
->path());
1030 ASSERT_EQ(vfs::directory_iterator(), I
);
1032 ASSERT_FALSE(PFS
.setCurrentWorkingDirectory("/"));
1034 auto PWD
= PFS
.getCurrentWorkingDirectory();
1035 ASSERT_FALSE(PWD
.getError());
1036 ASSERT_EQ("/", getPosixPath(*PWD
));
1038 SmallString
<16> Path
;
1039 ASSERT_FALSE(PFS
.getRealPath("a", Path
));
1040 ASSERT_EQ("/a", getPosixPath(Path
));
1043 ASSERT_FALSE(PFS
.isLocal("/a", Local
));
1044 EXPECT_FALSE(Local
);
1047 class InMemoryFileSystemTest
: public ::testing::Test
{
1049 llvm::vfs::InMemoryFileSystem FS
;
1050 llvm::vfs::InMemoryFileSystem NormalizedFS
;
1052 InMemoryFileSystemTest()
1053 : FS(/*UseNormalizedPaths=*/false),
1054 NormalizedFS(/*UseNormalizedPaths=*/true) {}
1057 MATCHER_P2(IsHardLinkTo
, FS
, Target
, "") {
1058 StringRef From
= arg
;
1059 StringRef To
= Target
;
1060 auto OpenedFrom
= FS
->openFileForRead(From
);
1061 auto OpenedTo
= FS
->openFileForRead(To
);
1062 return !OpenedFrom
.getError() && !OpenedTo
.getError() &&
1063 (*OpenedFrom
)->status()->getUniqueID() ==
1064 (*OpenedTo
)->status()->getUniqueID();
1067 TEST_F(InMemoryFileSystemTest
, IsEmpty
) {
1068 auto Stat
= FS
.status("/a");
1069 ASSERT_EQ(Stat
.getError(), errc::no_such_file_or_directory
) << FS
.toString();
1070 Stat
= FS
.status("/");
1071 ASSERT_EQ(Stat
.getError(), errc::no_such_file_or_directory
) << FS
.toString();
1074 TEST_F(InMemoryFileSystemTest
, WindowsPath
) {
1075 FS
.addFile("c:/windows/system128/foo.cpp", 0, MemoryBuffer::getMemBuffer(""));
1076 auto Stat
= FS
.status("c:");
1077 #if !defined(_WIN32)
1078 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << FS
.toString();
1080 Stat
= FS
.status("c:/windows/system128/foo.cpp");
1081 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << FS
.toString();
1082 FS
.addFile("d:/windows/foo.cpp", 0, MemoryBuffer::getMemBuffer(""));
1083 Stat
= FS
.status("d:/windows/foo.cpp");
1084 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << FS
.toString();
1087 TEST_F(InMemoryFileSystemTest
, OverlayFile
) {
1088 FS
.addFile("/a", 0, MemoryBuffer::getMemBuffer("a"));
1089 NormalizedFS
.addFile("/a", 0, MemoryBuffer::getMemBuffer("a"));
1090 auto Stat
= FS
.status("/");
1091 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << FS
.toString();
1092 Stat
= FS
.status("/.");
1094 Stat
= NormalizedFS
.status("/.");
1095 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << FS
.toString();
1096 Stat
= FS
.status("/a");
1097 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n" << FS
.toString();
1098 ASSERT_EQ("/a", Stat
->getName());
1101 TEST_F(InMemoryFileSystemTest
, OverlayFileNoOwn
) {
1102 auto Buf
= MemoryBuffer::getMemBuffer("a");
1103 FS
.addFileNoOwn("/a", 0, *Buf
);
1104 auto Stat
= FS
.status("/a");
1105 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n" << FS
.toString();
1106 ASSERT_EQ("/a", Stat
->getName());
1109 TEST_F(InMemoryFileSystemTest
, OpenFileForRead
) {
1110 FS
.addFile("/a", 0, MemoryBuffer::getMemBuffer("a"));
1111 FS
.addFile("././c", 0, MemoryBuffer::getMemBuffer("c"));
1112 FS
.addFile("./d/../d", 0, MemoryBuffer::getMemBuffer("d"));
1113 NormalizedFS
.addFile("/a", 0, MemoryBuffer::getMemBuffer("a"));
1114 NormalizedFS
.addFile("././c", 0, MemoryBuffer::getMemBuffer("c"));
1115 NormalizedFS
.addFile("./d/../d", 0, MemoryBuffer::getMemBuffer("d"));
1116 auto File
= FS
.openFileForRead("/a");
1117 ASSERT_EQ("a", (*(*File
)->getBuffer("ignored"))->getBuffer());
1118 File
= FS
.openFileForRead("/a"); // Open again.
1119 ASSERT_EQ("a", (*(*File
)->getBuffer("ignored"))->getBuffer());
1120 File
= NormalizedFS
.openFileForRead("/././a"); // Open again.
1121 ASSERT_EQ("a", (*(*File
)->getBuffer("ignored"))->getBuffer());
1122 File
= FS
.openFileForRead("/");
1123 ASSERT_EQ(File
.getError(), errc::invalid_argument
) << FS
.toString();
1124 File
= FS
.openFileForRead("/b");
1125 ASSERT_EQ(File
.getError(), errc::no_such_file_or_directory
) << FS
.toString();
1126 File
= FS
.openFileForRead("./c");
1128 File
= FS
.openFileForRead("e/../d");
1130 File
= NormalizedFS
.openFileForRead("./c");
1131 ASSERT_EQ("c", (*(*File
)->getBuffer("ignored"))->getBuffer());
1132 File
= NormalizedFS
.openFileForRead("e/../d");
1133 ASSERT_EQ("d", (*(*File
)->getBuffer("ignored"))->getBuffer());
1136 TEST_F(InMemoryFileSystemTest
, DuplicatedFile
) {
1137 ASSERT_TRUE(FS
.addFile("/a", 0, MemoryBuffer::getMemBuffer("a")));
1138 ASSERT_FALSE(FS
.addFile("/a/b", 0, MemoryBuffer::getMemBuffer("a")));
1139 ASSERT_TRUE(FS
.addFile("/a", 0, MemoryBuffer::getMemBuffer("a")));
1140 ASSERT_FALSE(FS
.addFile("/a", 0, MemoryBuffer::getMemBuffer("b")));
1141 ASSERT_TRUE(FS
.addFile("/b/c/d", 0, MemoryBuffer::getMemBuffer("a")));
1142 ASSERT_FALSE(FS
.addFile("/b/c", 0, MemoryBuffer::getMemBuffer("a")));
1143 ASSERT_TRUE(FS
.addFile(
1144 "/b/c", 0, MemoryBuffer::getMemBuffer(""), /*User=*/std::nullopt
,
1145 /*Group=*/std::nullopt
, sys::fs::file_type::directory_file
));
1148 TEST_F(InMemoryFileSystemTest
, DirectoryIteration
) {
1149 FS
.addFile("/a", 0, MemoryBuffer::getMemBuffer(""));
1150 FS
.addFile("/b/c", 0, MemoryBuffer::getMemBuffer(""));
1153 vfs::directory_iterator I
= FS
.dir_begin("/", EC
);
1155 ASSERT_EQ("/a", I
->path());
1158 ASSERT_EQ("/b", I
->path());
1161 ASSERT_EQ(vfs::directory_iterator(), I
);
1163 I
= FS
.dir_begin("/b", EC
);
1165 // When on Windows, we end up with "/b\\c" as the name. Convert to Posix
1166 // path for the sake of the comparison.
1167 ASSERT_EQ("/b/c", getPosixPath(std::string(I
->path())));
1170 ASSERT_EQ(vfs::directory_iterator(), I
);
1173 TEST_F(InMemoryFileSystemTest
, WorkingDirectory
) {
1174 FS
.setCurrentWorkingDirectory("/b");
1175 FS
.addFile("c", 0, MemoryBuffer::getMemBuffer(""));
1177 auto Stat
= FS
.status("/b/c");
1178 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n" << FS
.toString();
1179 ASSERT_EQ("/b/c", Stat
->getName());
1180 ASSERT_EQ("/b", *FS
.getCurrentWorkingDirectory());
1182 Stat
= FS
.status("c");
1183 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n" << FS
.toString();
1185 NormalizedFS
.setCurrentWorkingDirectory("/b/c");
1186 NormalizedFS
.setCurrentWorkingDirectory(".");
1188 getPosixPath(NormalizedFS
.getCurrentWorkingDirectory().get()));
1189 NormalizedFS
.setCurrentWorkingDirectory("..");
1191 getPosixPath(NormalizedFS
.getCurrentWorkingDirectory().get()));
1194 TEST_F(InMemoryFileSystemTest
, IsLocal
) {
1195 FS
.setCurrentWorkingDirectory("/b");
1196 FS
.addFile("c", 0, MemoryBuffer::getMemBuffer(""));
1199 bool IsLocal
= true;
1200 EC
= FS
.isLocal("c", IsLocal
);
1202 ASSERT_FALSE(IsLocal
);
1205 #if !defined(_WIN32)
1206 TEST_F(InMemoryFileSystemTest
, GetRealPath
) {
1207 SmallString
<16> Path
;
1208 EXPECT_EQ(FS
.getRealPath("b", Path
), errc::operation_not_permitted
);
1210 auto GetRealPath
= [this](StringRef P
) {
1211 SmallString
<16> Output
;
1212 auto EC
= FS
.getRealPath(P
, Output
);
1214 return std::string(Output
);
1217 FS
.setCurrentWorkingDirectory("a");
1218 EXPECT_EQ(GetRealPath("b"), "a/b");
1219 EXPECT_EQ(GetRealPath("../b"), "b");
1220 EXPECT_EQ(GetRealPath("b/./c"), "a/b/c");
1222 FS
.setCurrentWorkingDirectory("/a");
1223 EXPECT_EQ(GetRealPath("b"), "/a/b");
1224 EXPECT_EQ(GetRealPath("../b"), "/b");
1225 EXPECT_EQ(GetRealPath("b/./c"), "/a/b/c");
1229 TEST_F(InMemoryFileSystemTest
, AddFileWithUser
) {
1230 FS
.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"), 0xFEEDFACE);
1231 auto Stat
= FS
.status("/a");
1232 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n" << FS
.toString();
1233 ASSERT_TRUE(Stat
->isDirectory());
1234 ASSERT_EQ(0xFEEDFACE, Stat
->getUser());
1235 Stat
= FS
.status("/a/b");
1236 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n" << FS
.toString();
1237 ASSERT_TRUE(Stat
->isDirectory());
1238 ASSERT_EQ(0xFEEDFACE, Stat
->getUser());
1239 Stat
= FS
.status("/a/b/c");
1240 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n" << FS
.toString();
1241 ASSERT_TRUE(Stat
->isRegularFile());
1242 ASSERT_EQ(sys::fs::perms::all_all
, Stat
->getPermissions());
1243 ASSERT_EQ(0xFEEDFACE, Stat
->getUser());
1246 TEST_F(InMemoryFileSystemTest
, AddFileWithGroup
) {
1247 FS
.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"), std::nullopt
,
1249 auto Stat
= FS
.status("/a");
1250 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n" << FS
.toString();
1251 ASSERT_TRUE(Stat
->isDirectory());
1252 ASSERT_EQ(0xDABBAD00, Stat
->getGroup());
1253 Stat
= FS
.status("/a/b");
1254 ASSERT_TRUE(Stat
->isDirectory());
1255 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n" << FS
.toString();
1256 ASSERT_EQ(0xDABBAD00, Stat
->getGroup());
1257 Stat
= FS
.status("/a/b/c");
1258 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n" << FS
.toString();
1259 ASSERT_TRUE(Stat
->isRegularFile());
1260 ASSERT_EQ(sys::fs::perms::all_all
, Stat
->getPermissions());
1261 ASSERT_EQ(0xDABBAD00, Stat
->getGroup());
1264 TEST_F(InMemoryFileSystemTest
, AddFileWithFileType
) {
1265 FS
.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"), std::nullopt
,
1266 std::nullopt
, sys::fs::file_type::socket_file
);
1267 auto Stat
= FS
.status("/a");
1268 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n" << FS
.toString();
1269 ASSERT_TRUE(Stat
->isDirectory());
1270 Stat
= FS
.status("/a/b");
1271 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n" << FS
.toString();
1272 ASSERT_TRUE(Stat
->isDirectory());
1273 Stat
= FS
.status("/a/b/c");
1274 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n" << FS
.toString();
1275 ASSERT_EQ(sys::fs::file_type::socket_file
, Stat
->getType());
1276 ASSERT_EQ(sys::fs::perms::all_all
, Stat
->getPermissions());
1279 TEST_F(InMemoryFileSystemTest
, AddFileWithPerms
) {
1280 FS
.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"), std::nullopt
,
1281 std::nullopt
, std::nullopt
,
1282 sys::fs::perms::owner_read
| sys::fs::perms::owner_write
);
1283 auto Stat
= FS
.status("/a");
1284 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n" << FS
.toString();
1285 ASSERT_TRUE(Stat
->isDirectory());
1286 ASSERT_EQ(sys::fs::perms::owner_read
| sys::fs::perms::owner_write
|
1287 sys::fs::perms::owner_exe
,
1288 Stat
->getPermissions());
1289 Stat
= FS
.status("/a/b");
1290 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n" << FS
.toString();
1291 ASSERT_TRUE(Stat
->isDirectory());
1292 ASSERT_EQ(sys::fs::perms::owner_read
| sys::fs::perms::owner_write
|
1293 sys::fs::perms::owner_exe
,
1294 Stat
->getPermissions());
1295 Stat
= FS
.status("/a/b/c");
1296 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n" << FS
.toString();
1297 ASSERT_TRUE(Stat
->isRegularFile());
1298 ASSERT_EQ(sys::fs::perms::owner_read
| sys::fs::perms::owner_write
,
1299 Stat
->getPermissions());
1302 TEST_F(InMemoryFileSystemTest
, AddDirectoryThenAddChild
) {
1303 FS
.addFile("/a", 0, MemoryBuffer::getMemBuffer(""), /*User=*/std::nullopt
,
1304 /*Group=*/std::nullopt
, sys::fs::file_type::directory_file
);
1305 FS
.addFile("/a/b", 0, MemoryBuffer::getMemBuffer("abc"),
1306 /*User=*/std::nullopt
,
1307 /*Group=*/std::nullopt
, sys::fs::file_type::regular_file
);
1308 auto Stat
= FS
.status("/a");
1309 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n" << FS
.toString();
1310 ASSERT_TRUE(Stat
->isDirectory());
1311 Stat
= FS
.status("/a/b");
1312 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n" << FS
.toString();
1313 ASSERT_TRUE(Stat
->isRegularFile());
1316 // Test that the name returned by status() is in the same form as the path that
1317 // was requested (to match the behavior of RealFileSystem).
1318 TEST_F(InMemoryFileSystemTest
, StatusName
) {
1319 NormalizedFS
.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"),
1320 /*User=*/std::nullopt
,
1321 /*Group=*/std::nullopt
,
1322 sys::fs::file_type::regular_file
);
1323 NormalizedFS
.setCurrentWorkingDirectory("/a/b");
1325 // Access using InMemoryFileSystem::status.
1326 auto Stat
= NormalizedFS
.status("../b/c");
1327 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n"
1328 << NormalizedFS
.toString();
1329 ASSERT_TRUE(Stat
->isRegularFile());
1330 ASSERT_EQ("../b/c", Stat
->getName());
1332 // Access using InMemoryFileAdaptor::status.
1333 auto File
= NormalizedFS
.openFileForRead("../b/c");
1334 ASSERT_FALSE(File
.getError()) << File
.getError() << "\n"
1335 << NormalizedFS
.toString();
1336 Stat
= (*File
)->status();
1337 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n"
1338 << NormalizedFS
.toString();
1339 ASSERT_TRUE(Stat
->isRegularFile());
1340 ASSERT_EQ("../b/c", Stat
->getName());
1342 // Access using a directory iterator.
1344 llvm::vfs::directory_iterator It
= NormalizedFS
.dir_begin("../b", EC
);
1345 // When on Windows, we end up with "../b\\c" as the name. Convert to Posix
1346 // path for the sake of the comparison.
1347 ASSERT_EQ("../b/c", getPosixPath(std::string(It
->path())));
1350 TEST_F(InMemoryFileSystemTest
, AddHardLinkToFile
) {
1351 StringRef FromLink
= "/path/to/FROM/link";
1352 StringRef Target
= "/path/to/TO/file";
1353 FS
.addFile(Target
, 0, MemoryBuffer::getMemBuffer("content of target"));
1354 EXPECT_TRUE(FS
.addHardLink(FromLink
, Target
));
1355 EXPECT_THAT(FromLink
, IsHardLinkTo(&FS
, Target
));
1356 EXPECT_EQ(FS
.status(FromLink
)->getSize(), FS
.status(Target
)->getSize());
1357 EXPECT_EQ(FS
.getBufferForFile(FromLink
)->get()->getBuffer(),
1358 FS
.getBufferForFile(Target
)->get()->getBuffer());
1361 TEST_F(InMemoryFileSystemTest
, AddHardLinkInChainPattern
) {
1362 StringRef Link0
= "/path/to/0/link";
1363 StringRef Link1
= "/path/to/1/link";
1364 StringRef Link2
= "/path/to/2/link";
1365 StringRef Target
= "/path/to/target";
1366 FS
.addFile(Target
, 0, MemoryBuffer::getMemBuffer("content of target file"));
1367 EXPECT_TRUE(FS
.addHardLink(Link2
, Target
));
1368 EXPECT_TRUE(FS
.addHardLink(Link1
, Link2
));
1369 EXPECT_TRUE(FS
.addHardLink(Link0
, Link1
));
1370 EXPECT_THAT(Link0
, IsHardLinkTo(&FS
, Target
));
1371 EXPECT_THAT(Link1
, IsHardLinkTo(&FS
, Target
));
1372 EXPECT_THAT(Link2
, IsHardLinkTo(&FS
, Target
));
1375 TEST_F(InMemoryFileSystemTest
, AddHardLinkToAFileThatWasNotAddedBefore
) {
1376 EXPECT_FALSE(FS
.addHardLink("/path/to/link", "/path/to/target"));
1379 TEST_F(InMemoryFileSystemTest
, AddHardLinkFromAFileThatWasAddedBefore
) {
1380 StringRef Link
= "/path/to/link";
1381 StringRef Target
= "/path/to/target";
1382 FS
.addFile(Target
, 0, MemoryBuffer::getMemBuffer("content of target"));
1383 FS
.addFile(Link
, 0, MemoryBuffer::getMemBuffer("content of link"));
1384 EXPECT_FALSE(FS
.addHardLink(Link
, Target
));
1387 TEST_F(InMemoryFileSystemTest
, AddSameHardLinkMoreThanOnce
) {
1388 StringRef Link
= "/path/to/link";
1389 StringRef Target
= "/path/to/target";
1390 FS
.addFile(Target
, 0, MemoryBuffer::getMemBuffer("content of target"));
1391 EXPECT_TRUE(FS
.addHardLink(Link
, Target
));
1392 EXPECT_FALSE(FS
.addHardLink(Link
, Target
));
1395 TEST_F(InMemoryFileSystemTest
, AddFileInPlaceOfAHardLinkWithSameContent
) {
1396 StringRef Link
= "/path/to/link";
1397 StringRef Target
= "/path/to/target";
1398 StringRef Content
= "content of target";
1399 EXPECT_TRUE(FS
.addFile(Target
, 0, MemoryBuffer::getMemBuffer(Content
)));
1400 EXPECT_TRUE(FS
.addHardLink(Link
, Target
));
1401 EXPECT_TRUE(FS
.addFile(Link
, 0, MemoryBuffer::getMemBuffer(Content
)));
1404 TEST_F(InMemoryFileSystemTest
, AddFileInPlaceOfAHardLinkWithDifferentContent
) {
1405 StringRef Link
= "/path/to/link";
1406 StringRef Target
= "/path/to/target";
1407 StringRef Content
= "content of target";
1408 StringRef LinkContent
= "different content of link";
1409 EXPECT_TRUE(FS
.addFile(Target
, 0, MemoryBuffer::getMemBuffer(Content
)));
1410 EXPECT_TRUE(FS
.addHardLink(Link
, Target
));
1411 EXPECT_FALSE(FS
.addFile(Link
, 0, MemoryBuffer::getMemBuffer(LinkContent
)));
1414 TEST_F(InMemoryFileSystemTest
, AddHardLinkToADirectory
) {
1415 StringRef Dir
= "path/to/dummy/dir";
1416 StringRef Link
= "/path/to/link";
1417 StringRef File
= "path/to/dummy/dir/target";
1418 StringRef Content
= "content of target";
1419 EXPECT_TRUE(FS
.addFile(File
, 0, MemoryBuffer::getMemBuffer(Content
)));
1420 EXPECT_FALSE(FS
.addHardLink(Link
, Dir
));
1423 TEST_F(InMemoryFileSystemTest
, AddHardLinkToASymlink
) {
1424 EXPECT_TRUE(FS
.addFile("/file", 0, MemoryBuffer::getMemBuffer("content")));
1425 EXPECT_TRUE(FS
.addSymbolicLink("/symlink", "/file", 0));
1426 EXPECT_TRUE(FS
.addHardLink("/hardlink", "/symlink"));
1427 EXPECT_EQ((*FS
.getBufferForFile("/hardlink"))->getBuffer(), "content");
1430 TEST_F(InMemoryFileSystemTest
, AddHardLinkFromADirectory
) {
1431 StringRef Dir
= "path/to/dummy/dir";
1432 StringRef Target
= "path/to/dummy/dir/target";
1433 StringRef Content
= "content of target";
1434 EXPECT_TRUE(FS
.addFile(Target
, 0, MemoryBuffer::getMemBuffer(Content
)));
1435 EXPECT_FALSE(FS
.addHardLink(Dir
, Target
));
1438 TEST_F(InMemoryFileSystemTest
, AddHardLinkUnderAFile
) {
1439 StringRef CommonContent
= "content string";
1440 FS
.addFile("/a/b", 0, MemoryBuffer::getMemBuffer(CommonContent
));
1441 FS
.addFile("/c/d", 0, MemoryBuffer::getMemBuffer(CommonContent
));
1442 EXPECT_FALSE(FS
.addHardLink("/c/d/e", "/a/b"));
1445 TEST_F(InMemoryFileSystemTest
, RecursiveIterationWithHardLink
) {
1447 FS
.addFile("/a/b", 0, MemoryBuffer::getMemBuffer("content string"));
1448 EXPECT_TRUE(FS
.addHardLink("/c/d", "/a/b"));
1449 auto I
= vfs::recursive_directory_iterator(FS
, "/", EC
);
1451 std::vector
<std::string
> Nodes
;
1452 for (auto E
= vfs::recursive_directory_iterator(); !EC
&& I
!= E
;
1454 Nodes
.push_back(getPosixPath(std::string(I
->path())));
1456 EXPECT_THAT(Nodes
, testing::UnorderedElementsAre("/a", "/a/b", "/c", "/c/d"));
1459 TEST_F(InMemoryFileSystemTest
, UniqueID
) {
1460 ASSERT_TRUE(FS
.addFile("/a/b", 0, MemoryBuffer::getMemBuffer("text")));
1461 ASSERT_TRUE(FS
.addFile("/c/d", 0, MemoryBuffer::getMemBuffer("text")));
1462 ASSERT_TRUE(FS
.addHardLink("/e/f", "/a/b"));
1464 EXPECT_EQ(FS
.status("/a/b")->getUniqueID(), FS
.status("/a/b")->getUniqueID());
1465 EXPECT_NE(FS
.status("/a/b")->getUniqueID(), FS
.status("/c/d")->getUniqueID());
1466 EXPECT_EQ(FS
.status("/a/b")->getUniqueID(), FS
.status("/e/f")->getUniqueID());
1467 EXPECT_EQ(FS
.status("/a")->getUniqueID(), FS
.status("/a")->getUniqueID());
1468 EXPECT_NE(FS
.status("/a")->getUniqueID(), FS
.status("/c")->getUniqueID());
1469 EXPECT_NE(FS
.status("/a")->getUniqueID(), FS
.status("/e")->getUniqueID());
1471 // Recreating the "same" FS yields the same UniqueIDs.
1472 // Note: FS2 should match FS with respect to path normalization.
1473 vfs::InMemoryFileSystem
FS2(/*UseNormalizedPath=*/false);
1474 ASSERT_TRUE(FS2
.addFile("/a/b", 0, MemoryBuffer::getMemBuffer("text")));
1475 EXPECT_EQ(FS
.status("/a/b")->getUniqueID(),
1476 FS2
.status("/a/b")->getUniqueID());
1477 EXPECT_EQ(FS
.status("/a")->getUniqueID(), FS2
.status("/a")->getUniqueID());
1480 TEST_F(InMemoryFileSystemTest
, AddSymlinkToAFile
) {
1482 FS
.addFile("/some/file", 0, MemoryBuffer::getMemBuffer("contents")));
1483 EXPECT_TRUE(FS
.addSymbolicLink("/other/file/link", "/some/file", 0));
1484 ErrorOr
<vfs::Status
> Stat
= FS
.status("/some/file");
1485 EXPECT_TRUE(Stat
->isRegularFile());
1488 TEST_F(InMemoryFileSystemTest
, AddSymlinkToADirectory
) {
1489 EXPECT_TRUE(FS
.addSymbolicLink("/link", "/target", 0));
1491 FS
.addFile("/target/foo.h", 0, MemoryBuffer::getMemBuffer("foo")));
1492 ErrorOr
<vfs::Status
> Stat
= FS
.status("/link/foo.h");
1494 EXPECT_EQ((*Stat
).getName(), "/link/foo.h");
1495 EXPECT_TRUE(Stat
->isRegularFile());
1498 TEST_F(InMemoryFileSystemTest
, AddSymlinkToASymlink
) {
1499 EXPECT_TRUE(FS
.addSymbolicLink("/first", "/second", 0));
1500 EXPECT_TRUE(FS
.addSymbolicLink("/second", "/third", 0));
1501 EXPECT_TRUE(FS
.addFile("/third", 0, MemoryBuffer::getMemBuffer("")));
1502 ErrorOr
<vfs::Status
> Stat
= FS
.status("/first");
1504 EXPECT_EQ((*Stat
).getName(), "/first");
1505 // Follow-through symlinks by default. This matches RealFileSystem's
1507 EXPECT_TRUE(Stat
->isRegularFile());
1508 Stat
= FS
.status("/second");
1510 EXPECT_EQ((*Stat
).getName(), "/second");
1511 EXPECT_TRUE(Stat
->isRegularFile());
1512 Stat
= FS
.status("/third");
1514 EXPECT_EQ((*Stat
).getName(), "/third");
1515 EXPECT_TRUE(Stat
->isRegularFile());
1518 TEST_F(InMemoryFileSystemTest
, AddRecursiveSymlink
) {
1519 EXPECT_TRUE(FS
.addSymbolicLink("/link-a", "/link-b", 0));
1520 EXPECT_TRUE(FS
.addSymbolicLink("/link-b", "/link-a", 0));
1521 ErrorOr
<vfs::Status
> Stat
= FS
.status("/link-a/foo");
1523 EXPECT_EQ(Stat
.getError(), errc::no_such_file_or_directory
);
1526 TEST_F(InMemoryFileSystemTest
, DirectoryIteratorWithSymlinkToAFile
) {
1529 EXPECT_TRUE(FS
.addFile("/file", 0, MemoryBuffer::getMemBuffer("")));
1530 EXPECT_TRUE(FS
.addSymbolicLink("/symlink", "/file", 0));
1532 vfs::directory_iterator I
= FS
.dir_begin("/", EC
), E
;
1535 std::vector
<std::string
> Nodes
;
1536 for (; !EC
&& I
!= E
; I
.increment(EC
))
1537 Nodes
.push_back(getPosixPath(std::string(I
->path())));
1539 EXPECT_THAT(Nodes
, testing::UnorderedElementsAre("/file", "/file"));
1542 TEST_F(InMemoryFileSystemTest
, RecursiveDirectoryIteratorWithSymlinkToADir
) {
1545 EXPECT_TRUE(FS
.addFile("/dir/file", 0, MemoryBuffer::getMemBuffer("")));
1546 EXPECT_TRUE(FS
.addSymbolicLink("/dir_symlink", "/dir", 0));
1548 vfs::recursive_directory_iterator
I(FS
, "/", EC
), E
;
1551 std::vector
<std::string
> Nodes
;
1552 for (; !EC
&& I
!= E
; I
.increment(EC
))
1553 Nodes
.push_back(getPosixPath(std::string(I
->path())));
1555 EXPECT_THAT(Nodes
, testing::UnorderedElementsAre("/dir", "/dir/file", "/dir",
1559 // NOTE: in the tests below, we use '//root/' as our root directory, since it is
1560 // a legal *absolute* path on Windows as well as *nix.
1561 class VFSFromYAMLTest
: public ::testing::Test
{
1565 void SetUp() override
{ NumDiagnostics
= 0; }
1567 static void CountingDiagHandler(const SMDiagnostic
&, void *Context
) {
1568 VFSFromYAMLTest
*Test
= static_cast<VFSFromYAMLTest
*>(Context
);
1569 ++Test
->NumDiagnostics
;
1572 std::unique_ptr
<vfs::FileSystem
>
1573 getFromYAMLRawString(StringRef Content
,
1574 IntrusiveRefCntPtr
<vfs::FileSystem
> ExternalFS
,
1575 StringRef YAMLFilePath
= "") {
1576 std::unique_ptr
<MemoryBuffer
> Buffer
= MemoryBuffer::getMemBuffer(Content
);
1577 return getVFSFromYAML(std::move(Buffer
), CountingDiagHandler
, YAMLFilePath
,
1581 std::unique_ptr
<vfs::FileSystem
> getFromYAMLString(
1583 IntrusiveRefCntPtr
<vfs::FileSystem
> ExternalFS
= new DummyFileSystem(),
1584 StringRef YAMLFilePath
= "") {
1585 std::string
VersionPlusContent("{\n 'version':0,\n");
1586 VersionPlusContent
+= Content
.slice(Content
.find('{') + 1, StringRef::npos
);
1587 return getFromYAMLRawString(VersionPlusContent
, ExternalFS
, YAMLFilePath
);
1590 // This is intended as a "XFAIL" for windows hosts.
1591 bool supportsSameDirMultipleYAMLEntries() {
1592 Triple
Host(Triple::normalize(sys::getProcessTriple()));
1593 return !Host
.isOSWindows();
1597 TEST_F(VFSFromYAMLTest
, BasicVFSFromYAML
) {
1598 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
;
1599 FS
= getFromYAMLString("");
1600 EXPECT_EQ(nullptr, FS
.get());
1601 FS
= getFromYAMLString("[]");
1602 EXPECT_EQ(nullptr, FS
.get());
1603 FS
= getFromYAMLString("'string'");
1604 EXPECT_EQ(nullptr, FS
.get());
1605 EXPECT_EQ(3, NumDiagnostics
);
1608 TEST_F(VFSFromYAMLTest
, MappedFiles
) {
1609 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
1610 Lower
->addDirectory("//root/foo/bar");
1611 Lower
->addRegularFile("//root/foo/bar/a");
1612 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= getFromYAMLString(
1615 " 'type': 'directory',\n"
1616 " 'name': '//root/',\n"
1617 " 'contents': [ {\n"
1618 " 'type': 'file',\n"
1619 " 'name': 'file1',\n"
1620 " 'external-contents': '//root/foo/bar/a'\n"
1623 " 'type': 'file',\n"
1624 " 'name': 'file2',\n"
1625 " 'external-contents': '//root/foo/b'\n"
1628 " 'type': 'directory-remap',\n"
1629 " 'name': 'mappeddir',\n"
1630 " 'external-contents': '//root/foo/bar'\n"
1633 " 'type': 'directory-remap',\n"
1634 " 'name': 'mappeddir2',\n"
1635 " 'use-external-name': false,\n"
1636 " 'external-contents': '//root/foo/bar'\n"
1643 ASSERT_NE(FS
.get(), nullptr);
1645 IntrusiveRefCntPtr
<vfs::OverlayFileSystem
> O(
1646 new vfs::OverlayFileSystem(Lower
));
1650 ErrorOr
<vfs::Status
> S
= O
->status("//root/file1");
1651 ASSERT_FALSE(S
.getError());
1652 EXPECT_EQ("//root/foo/bar/a", S
->getName());
1653 EXPECT_TRUE(S
->ExposesExternalVFSPath
);
1655 ErrorOr
<vfs::Status
> SLower
= O
->status("//root/foo/bar/a");
1656 EXPECT_EQ("//root/foo/bar/a", SLower
->getName());
1657 EXPECT_TRUE(S
->equivalent(*SLower
));
1658 EXPECT_FALSE(SLower
->ExposesExternalVFSPath
);
1660 // file after opening
1661 auto OpenedF
= O
->openFileForRead("//root/file1");
1662 ASSERT_FALSE(OpenedF
.getError());
1663 auto OpenedS
= (*OpenedF
)->status();
1664 ASSERT_FALSE(OpenedS
.getError());
1665 EXPECT_EQ("//root/foo/bar/a", OpenedS
->getName());
1666 EXPECT_TRUE(OpenedS
->ExposesExternalVFSPath
);
1669 S
= O
->status("//root/");
1670 ASSERT_FALSE(S
.getError());
1671 EXPECT_TRUE(S
->isDirectory());
1672 EXPECT_TRUE(S
->equivalent(*O
->status("//root/"))); // non-volatile UniqueID
1674 // remapped directory
1675 S
= O
->status("//root/mappeddir");
1676 ASSERT_FALSE(S
.getError());
1677 EXPECT_TRUE(S
->isDirectory());
1678 EXPECT_TRUE(S
->ExposesExternalVFSPath
);
1679 EXPECT_TRUE(S
->equivalent(*O
->status("//root/foo/bar")));
1681 SLower
= O
->status("//root/foo/bar");
1682 EXPECT_EQ("//root/foo/bar", SLower
->getName());
1683 EXPECT_TRUE(S
->equivalent(*SLower
));
1684 EXPECT_FALSE(SLower
->ExposesExternalVFSPath
);
1686 // file in remapped directory
1687 S
= O
->status("//root/mappeddir/a");
1688 ASSERT_FALSE(S
.getError());
1689 EXPECT_FALSE(S
->isDirectory());
1690 EXPECT_TRUE(S
->ExposesExternalVFSPath
);
1691 EXPECT_EQ("//root/foo/bar/a", S
->getName());
1693 // file in remapped directory, with use-external-name=false
1694 S
= O
->status("//root/mappeddir2/a");
1695 ASSERT_FALSE(S
.getError());
1696 EXPECT_FALSE(S
->isDirectory());
1697 EXPECT_FALSE(S
->ExposesExternalVFSPath
);
1698 EXPECT_EQ("//root/mappeddir2/a", S
->getName());
1700 // file contents in remapped directory
1701 OpenedF
= O
->openFileForRead("//root/mappeddir/a");
1702 ASSERT_FALSE(OpenedF
.getError());
1703 OpenedS
= (*OpenedF
)->status();
1704 ASSERT_FALSE(OpenedS
.getError());
1705 EXPECT_EQ("//root/foo/bar/a", OpenedS
->getName());
1706 EXPECT_TRUE(OpenedS
->ExposesExternalVFSPath
);
1708 // file contents in remapped directory, with use-external-name=false
1709 OpenedF
= O
->openFileForRead("//root/mappeddir2/a");
1710 ASSERT_FALSE(OpenedF
.getError());
1711 OpenedS
= (*OpenedF
)->status();
1712 ASSERT_FALSE(OpenedS
.getError());
1713 EXPECT_EQ("//root/mappeddir2/a", OpenedS
->getName());
1714 EXPECT_FALSE(OpenedS
->ExposesExternalVFSPath
);
1717 EXPECT_EQ(O
->status("//root/file2").getError(),
1718 llvm::errc::no_such_file_or_directory
);
1719 EXPECT_EQ(0, NumDiagnostics
);
1722 TEST_F(VFSFromYAMLTest
, MappedRoot
) {
1723 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
1724 Lower
->addDirectory("//root/foo/bar");
1725 Lower
->addRegularFile("//root/foo/bar/a");
1726 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
=
1727 getFromYAMLString("{ 'roots': [\n"
1729 " 'type': 'directory-remap',\n"
1730 " 'name': '//mappedroot/',\n"
1731 " 'external-contents': '//root/foo/bar'\n"
1736 ASSERT_NE(FS
.get(), nullptr);
1738 IntrusiveRefCntPtr
<vfs::OverlayFileSystem
> O(
1739 new vfs::OverlayFileSystem(Lower
));
1743 ErrorOr
<vfs::Status
> S
= O
->status("//mappedroot/a");
1744 ASSERT_FALSE(S
.getError());
1745 EXPECT_EQ("//root/foo/bar/a", S
->getName());
1746 EXPECT_TRUE(S
->ExposesExternalVFSPath
);
1748 ErrorOr
<vfs::Status
> SLower
= O
->status("//root/foo/bar/a");
1749 EXPECT_EQ("//root/foo/bar/a", SLower
->getName());
1750 EXPECT_TRUE(S
->equivalent(*SLower
));
1751 EXPECT_FALSE(SLower
->ExposesExternalVFSPath
);
1753 // file after opening
1754 auto OpenedF
= O
->openFileForRead("//mappedroot/a");
1755 ASSERT_FALSE(OpenedF
.getError());
1756 auto OpenedS
= (*OpenedF
)->status();
1757 ASSERT_FALSE(OpenedS
.getError());
1758 EXPECT_EQ("//root/foo/bar/a", OpenedS
->getName());
1759 EXPECT_TRUE(OpenedS
->ExposesExternalVFSPath
);
1761 EXPECT_EQ(0, NumDiagnostics
);
1764 TEST_F(VFSFromYAMLTest
, RemappedDirectoryOverlay
) {
1765 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
1766 Lower
->addDirectory("//root/foo");
1767 Lower
->addRegularFile("//root/foo/a");
1768 Lower
->addDirectory("//root/bar");
1769 Lower
->addRegularFile("//root/bar/b");
1770 Lower
->addRegularFile("//root/bar/c");
1771 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
=
1772 getFromYAMLString("{ 'roots': [\n"
1774 " 'type': 'directory',\n"
1775 " 'name': '//root/',\n"
1776 " 'contents': [ {\n"
1777 " 'type': 'directory-remap',\n"
1779 " 'external-contents': '//root/foo'\n"
1784 ASSERT_NE(FS
.get(), nullptr);
1786 IntrusiveRefCntPtr
<vfs::OverlayFileSystem
> O(
1787 new vfs::OverlayFileSystem(Lower
));
1790 ErrorOr
<vfs::Status
> S
= O
->status("//root/foo");
1791 ASSERT_FALSE(S
.getError());
1793 ErrorOr
<vfs::Status
> SS
= O
->status("//root/bar");
1794 ASSERT_FALSE(SS
.getError());
1795 EXPECT_TRUE(S
->equivalent(*SS
));
1798 checkContents(O
->dir_begin("//root/bar", EC
),
1799 {"//root/foo/a", "//root/bar/b", "//root/bar/c"});
1801 Lower
->addRegularFile("//root/foo/b");
1802 checkContents(O
->dir_begin("//root/bar", EC
),
1803 {"//root/foo/a", "//root/foo/b", "//root/bar/c"});
1805 EXPECT_EQ(0, NumDiagnostics
);
1808 TEST_F(VFSFromYAMLTest
, RemappedDirectoryOverlayNoExternalNames
) {
1809 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
1810 Lower
->addDirectory("//root/foo");
1811 Lower
->addRegularFile("//root/foo/a");
1812 Lower
->addDirectory("//root/bar");
1813 Lower
->addRegularFile("//root/bar/b");
1814 Lower
->addRegularFile("//root/bar/c");
1815 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
=
1816 getFromYAMLString("{ 'use-external-names': false,\n"
1819 " 'type': 'directory',\n"
1820 " 'name': '//root/',\n"
1821 " 'contents': [ {\n"
1822 " 'type': 'directory-remap',\n"
1824 " 'external-contents': '//root/foo'\n"
1829 ASSERT_NE(FS
.get(), nullptr);
1831 ErrorOr
<vfs::Status
> S
= FS
->status("//root/foo");
1832 ASSERT_FALSE(S
.getError());
1834 ErrorOr
<vfs::Status
> SS
= FS
->status("//root/bar");
1835 ASSERT_FALSE(SS
.getError());
1836 EXPECT_TRUE(S
->equivalent(*SS
));
1839 checkContents(FS
->dir_begin("//root/bar", EC
),
1840 {"//root/bar/a", "//root/bar/b", "//root/bar/c"});
1842 Lower
->addRegularFile("//root/foo/b");
1843 checkContents(FS
->dir_begin("//root/bar", EC
),
1844 {"//root/bar/a", "//root/bar/b", "//root/bar/c"});
1846 EXPECT_EQ(0, NumDiagnostics
);
1849 TEST_F(VFSFromYAMLTest
, RemappedDirectoryOverlayNoFallthrough
) {
1850 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
1851 Lower
->addDirectory("//root/foo");
1852 Lower
->addRegularFile("//root/foo/a");
1853 Lower
->addDirectory("//root/bar");
1854 Lower
->addRegularFile("//root/bar/b");
1855 Lower
->addRegularFile("//root/bar/c");
1856 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
=
1857 getFromYAMLString("{ 'fallthrough': false,\n"
1860 " 'type': 'directory',\n"
1861 " 'name': '//root/',\n"
1862 " 'contents': [ {\n"
1863 " 'type': 'directory-remap',\n"
1865 " 'external-contents': '//root/foo'\n"
1870 ASSERT_NE(FS
.get(), nullptr);
1872 ErrorOr
<vfs::Status
> S
= Lower
->status("//root/foo");
1873 ASSERT_FALSE(S
.getError());
1875 ErrorOr
<vfs::Status
> SS
= FS
->status("//root/bar");
1876 ASSERT_FALSE(SS
.getError());
1877 EXPECT_TRUE(S
->equivalent(*SS
));
1880 checkContents(FS
->dir_begin("//root/bar", EC
), {"//root/foo/a"});
1882 Lower
->addRegularFile("//root/foo/b");
1883 checkContents(FS
->dir_begin("//root/bar", EC
),
1884 {"//root/foo/a", "//root/foo/b"});
1886 EXPECT_EQ(0, NumDiagnostics
);
1889 TEST_F(VFSFromYAMLTest
, ReturnsRequestedPathVFSMiss
) {
1890 IntrusiveRefCntPtr
<vfs::InMemoryFileSystem
> BaseFS(
1891 new vfs::InMemoryFileSystem
);
1892 BaseFS
->addFile("//root/foo/a", 0,
1893 MemoryBuffer::getMemBuffer("contents of a"));
1894 ASSERT_FALSE(BaseFS
->setCurrentWorkingDirectory("//root/foo"));
1895 auto RemappedFS
= vfs::RedirectingFileSystem::create(
1896 {}, /*UseExternalNames=*/false, *BaseFS
);
1898 auto OpenedF
= RemappedFS
->openFileForRead("a");
1899 ASSERT_FALSE(OpenedF
.getError());
1900 llvm::ErrorOr
<std::string
> Name
= (*OpenedF
)->getName();
1901 ASSERT_FALSE(Name
.getError());
1902 EXPECT_EQ("a", Name
.get());
1904 auto OpenedS
= (*OpenedF
)->status();
1905 ASSERT_FALSE(OpenedS
.getError());
1906 EXPECT_EQ("a", OpenedS
->getName());
1907 EXPECT_FALSE(OpenedS
->ExposesExternalVFSPath
);
1909 auto DirectS
= RemappedFS
->status("a");
1910 ASSERT_FALSE(DirectS
.getError());
1911 EXPECT_EQ("a", DirectS
->getName());
1912 EXPECT_FALSE(DirectS
->ExposesExternalVFSPath
);
1914 EXPECT_EQ(0, NumDiagnostics
);
1917 TEST_F(VFSFromYAMLTest
, ReturnsExternalPathVFSHit
) {
1918 IntrusiveRefCntPtr
<vfs::InMemoryFileSystem
> BaseFS(
1919 new vfs::InMemoryFileSystem
);
1920 BaseFS
->addFile("//root/foo/realname", 0,
1921 MemoryBuffer::getMemBuffer("contents of a"));
1923 getFromYAMLString("{ 'use-external-names': true,\n"
1926 " 'type': 'directory',\n"
1927 " 'name': '//root/foo',\n"
1928 " 'contents': [ {\n"
1929 " 'type': 'file',\n"
1930 " 'name': 'vfsname',\n"
1931 " 'external-contents': 'realname'\n"
1936 ASSERT_FALSE(FS
->setCurrentWorkingDirectory("//root/foo"));
1938 auto OpenedF
= FS
->openFileForRead("vfsname");
1939 ASSERT_FALSE(OpenedF
.getError());
1940 llvm::ErrorOr
<std::string
> Name
= (*OpenedF
)->getName();
1941 ASSERT_FALSE(Name
.getError());
1942 EXPECT_EQ("realname", Name
.get());
1944 auto OpenedS
= (*OpenedF
)->status();
1945 ASSERT_FALSE(OpenedS
.getError());
1946 EXPECT_EQ("realname", OpenedS
->getName());
1947 EXPECT_TRUE(OpenedS
->ExposesExternalVFSPath
);
1949 auto DirectS
= FS
->status("vfsname");
1950 ASSERT_FALSE(DirectS
.getError());
1951 EXPECT_EQ("realname", DirectS
->getName());
1952 EXPECT_TRUE(DirectS
->ExposesExternalVFSPath
);
1954 EXPECT_EQ(0, NumDiagnostics
);
1957 TEST_F(VFSFromYAMLTest
, RootRelativeTest
) {
1958 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
1959 Lower
->addDirectory("//root/foo/bar");
1960 Lower
->addRegularFile("//root/foo/bar/a");
1961 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
=
1962 getFromYAMLString("{\n"
1963 " 'case-sensitive': false,\n"
1964 " 'root-relative': 'overlay-dir',\n"
1966 " { 'name': 'b', 'type': 'file',\n"
1967 " 'external-contents': '//root/foo/bar/a'\n"
1971 Lower
, "//root/foo/bar/overlay");
1973 ASSERT_NE(FS
.get(), nullptr);
1974 ErrorOr
<vfs::Status
> S
= FS
->status("//root/foo/bar/b");
1975 ASSERT_FALSE(S
.getError());
1976 EXPECT_EQ("//root/foo/bar/a", S
->getName());
1978 // On Windows, with overlay-relative set to true, the relative
1979 // path in external-contents field will be prepend by OverlayDir
1980 // with native path separator, regardless of the actual path separator
1981 // used in YAMLFilePath field.
1983 FS
= getFromYAMLString("{\n"
1984 " 'case-sensitive': false,\n"
1985 " 'overlay-relative': true,\n"
1986 " 'root-relative': 'overlay-dir',\n"
1988 " { 'name': 'b', 'type': 'file',\n"
1989 " 'external-contents': 'a'\n"
1993 Lower
, "//root/foo/bar/overlay");
1994 ASSERT_NE(FS
.get(), nullptr);
1995 S
= FS
->status("//root/foo/bar/b");
1996 ASSERT_FALSE(S
.getError());
1997 EXPECT_EQ("//root/foo/bar/a", S
->getName());
1999 IntrusiveRefCntPtr
<DummyFileSystem
> LowerWindows(new DummyFileSystem());
2000 LowerWindows
->addDirectory("\\\\root\\foo\\bar");
2001 LowerWindows
->addRegularFile("\\\\root\\foo\\bar\\a");
2002 FS
= getFromYAMLString("{\n"
2003 " 'case-sensitive': false,\n"
2004 " 'overlay-relative': true,\n"
2005 " 'root-relative': 'overlay-dir',\n"
2007 " { 'name': 'b', 'type': 'file',\n"
2008 " 'external-contents': 'a'\n"
2012 LowerWindows
, "\\\\root\\foo\\bar\\overlay");
2013 ASSERT_NE(FS
.get(), nullptr);
2014 S
= FS
->status("\\\\root\\foo\\bar\\b");
2015 ASSERT_FALSE(S
.getError());
2016 EXPECT_EQ("\\\\root\\foo\\bar\\a", S
->getName());
2020 TEST_F(VFSFromYAMLTest
, ReturnsInternalPathVFSHit
) {
2021 IntrusiveRefCntPtr
<vfs::InMemoryFileSystem
> BaseFS(
2022 new vfs::InMemoryFileSystem
);
2023 BaseFS
->addFile("//root/foo/realname", 0,
2024 MemoryBuffer::getMemBuffer("contents of a"));
2026 getFromYAMLString("{ 'use-external-names': false,\n"
2029 " 'type': 'directory',\n"
2030 " 'name': '//root/foo',\n"
2031 " 'contents': [ {\n"
2032 " 'type': 'file',\n"
2033 " 'name': 'vfsname',\n"
2034 " 'external-contents': 'realname'\n"
2039 ASSERT_FALSE(FS
->setCurrentWorkingDirectory("//root/foo"));
2041 auto OpenedF
= FS
->openFileForRead("vfsname");
2042 ASSERT_FALSE(OpenedF
.getError());
2043 llvm::ErrorOr
<std::string
> Name
= (*OpenedF
)->getName();
2044 ASSERT_FALSE(Name
.getError());
2045 EXPECT_EQ("vfsname", Name
.get());
2047 auto OpenedS
= (*OpenedF
)->status();
2048 ASSERT_FALSE(OpenedS
.getError());
2049 EXPECT_EQ("vfsname", OpenedS
->getName());
2050 EXPECT_FALSE(OpenedS
->ExposesExternalVFSPath
);
2052 auto DirectS
= FS
->status("vfsname");
2053 ASSERT_FALSE(DirectS
.getError());
2054 EXPECT_EQ("vfsname", DirectS
->getName());
2055 EXPECT_FALSE(DirectS
->ExposesExternalVFSPath
);
2057 EXPECT_EQ(0, NumDiagnostics
);
2060 TEST_F(VFSFromYAMLTest
, CaseInsensitive
) {
2061 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
2062 Lower
->addRegularFile("//root/foo/bar/a");
2063 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= getFromYAMLString(
2064 "{ 'case-sensitive': 'false',\n"
2067 " 'type': 'directory',\n"
2068 " 'name': '//root/',\n"
2069 " 'contents': [ {\n"
2070 " 'type': 'file',\n"
2072 " 'external-contents': '//root/foo/bar/a'\n"
2077 ASSERT_NE(FS
.get(), nullptr);
2079 IntrusiveRefCntPtr
<vfs::OverlayFileSystem
> O(
2080 new vfs::OverlayFileSystem(Lower
));
2083 ErrorOr
<vfs::Status
> S
= O
->status("//root/XX");
2084 ASSERT_FALSE(S
.getError());
2086 ErrorOr
<vfs::Status
> SS
= O
->status("//root/xx");
2087 ASSERT_FALSE(SS
.getError());
2088 EXPECT_TRUE(S
->equivalent(*SS
));
2089 SS
= O
->status("//root/xX");
2090 EXPECT_TRUE(S
->equivalent(*SS
));
2091 SS
= O
->status("//root/Xx");
2092 EXPECT_TRUE(S
->equivalent(*SS
));
2093 EXPECT_EQ(0, NumDiagnostics
);
2096 TEST_F(VFSFromYAMLTest
, CaseSensitive
) {
2097 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
2098 Lower
->addRegularFile("//root/foo/bar/a");
2099 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= getFromYAMLString(
2100 "{ 'case-sensitive': 'true',\n"
2103 " 'type': 'directory',\n"
2104 " 'name': '//root/',\n"
2105 " 'contents': [ {\n"
2106 " 'type': 'file',\n"
2108 " 'external-contents': '//root/foo/bar/a'\n"
2113 ASSERT_NE(FS
.get(), nullptr);
2115 IntrusiveRefCntPtr
<vfs::OverlayFileSystem
> O(
2116 new vfs::OverlayFileSystem(Lower
));
2119 ErrorOr
<vfs::Status
> SS
= O
->status("//root/xx");
2120 EXPECT_EQ(SS
.getError(), llvm::errc::no_such_file_or_directory
);
2121 SS
= O
->status("//root/xX");
2122 EXPECT_EQ(SS
.getError(), llvm::errc::no_such_file_or_directory
);
2123 SS
= O
->status("//root/Xx");
2124 EXPECT_EQ(SS
.getError(), llvm::errc::no_such_file_or_directory
);
2125 EXPECT_EQ(0, NumDiagnostics
);
2128 TEST_F(VFSFromYAMLTest
, IllegalVFSFile
) {
2129 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
2131 // invalid YAML at top-level
2132 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= getFromYAMLString("{]", Lower
);
2133 EXPECT_EQ(nullptr, FS
.get());
2134 // invalid YAML in roots
2135 FS
= getFromYAMLString("{ 'roots':[}", Lower
);
2136 // invalid YAML in directory
2137 FS
= getFromYAMLString(
2138 "{ 'roots':[ { 'name': 'foo', 'type': 'directory', 'contents': [}",
2140 EXPECT_EQ(nullptr, FS
.get());
2142 // invalid configuration
2143 FS
= getFromYAMLString("{ 'knobular': 'true', 'roots':[] }", Lower
);
2144 EXPECT_EQ(nullptr, FS
.get());
2145 FS
= getFromYAMLString("{ 'case-sensitive': 'maybe', 'roots':[] }", Lower
);
2146 EXPECT_EQ(nullptr, FS
.get());
2149 FS
= getFromYAMLString("{ 'roots':'' }", Lower
);
2150 EXPECT_EQ(nullptr, FS
.get());
2151 FS
= getFromYAMLString("{ 'roots':{} }", Lower
);
2152 EXPECT_EQ(nullptr, FS
.get());
2155 FS
= getFromYAMLString(
2156 "{ 'roots':[ { 'type': 'other', 'name': 'me', 'contents': '' }", Lower
);
2157 EXPECT_EQ(nullptr, FS
.get());
2158 FS
= getFromYAMLString("{ 'roots':[ { 'type': 'file', 'name': [], "
2159 "'external-contents': 'other' }",
2161 EXPECT_EQ(nullptr, FS
.get());
2162 FS
= getFromYAMLString(
2163 "{ 'roots':[ { 'type': 'file', 'name': 'me', 'external-contents': [] }",
2165 EXPECT_EQ(nullptr, FS
.get());
2166 FS
= getFromYAMLString(
2167 "{ 'roots':[ { 'type': 'file', 'name': 'me', 'external-contents': {} }",
2169 EXPECT_EQ(nullptr, FS
.get());
2170 FS
= getFromYAMLString(
2171 "{ 'roots':[ { 'type': 'directory', 'name': 'me', 'contents': {} }",
2173 EXPECT_EQ(nullptr, FS
.get());
2174 FS
= getFromYAMLString(
2175 "{ 'roots':[ { 'type': 'directory', 'name': 'me', 'contents': '' }",
2177 EXPECT_EQ(nullptr, FS
.get());
2178 FS
= getFromYAMLString(
2179 "{ 'roots':[ { 'thingy': 'directory', 'name': 'me', 'contents': [] }",
2181 EXPECT_EQ(nullptr, FS
.get());
2183 // missing mandatory fields
2184 FS
= getFromYAMLString("{ 'roots':[ { 'type': 'file', 'name': 'me' }", Lower
);
2185 EXPECT_EQ(nullptr, FS
.get());
2186 FS
= getFromYAMLString(
2187 "{ 'roots':[ { 'type': 'file', 'external-contents': 'other' }", Lower
);
2188 EXPECT_EQ(nullptr, FS
.get());
2189 FS
= getFromYAMLString("{ 'roots':[ { 'name': 'me', 'contents': [] }", Lower
);
2190 EXPECT_EQ(nullptr, FS
.get());
2193 FS
= getFromYAMLString("{ 'roots':[], 'roots':[] }", Lower
);
2194 EXPECT_EQ(nullptr, FS
.get());
2195 FS
= getFromYAMLString(
2196 "{ 'case-sensitive':'true', 'case-sensitive':'true', 'roots':[] }",
2198 EXPECT_EQ(nullptr, FS
.get());
2200 getFromYAMLString("{ 'roots':[{'name':'me', 'name':'you', 'type':'file', "
2201 "'external-contents':'blah' } ] }",
2203 EXPECT_EQ(nullptr, FS
.get());
2206 FS
= getFromYAMLRawString("{ 'roots':[] }", Lower
);
2207 EXPECT_EQ(nullptr, FS
.get());
2209 // bad version number
2210 FS
= getFromYAMLRawString("{ 'version':'foo', 'roots':[] }", Lower
);
2211 EXPECT_EQ(nullptr, FS
.get());
2212 FS
= getFromYAMLRawString("{ 'version':-1, 'roots':[] }", Lower
);
2213 EXPECT_EQ(nullptr, FS
.get());
2214 FS
= getFromYAMLRawString("{ 'version':100000, 'roots':[] }", Lower
);
2215 EXPECT_EQ(nullptr, FS
.get());
2217 // both 'external-contents' and 'contents' specified
2218 Lower
->addDirectory("//root/external/dir");
2219 FS
= getFromYAMLString(
2221 "{ 'type': 'directory', 'name': '//root/A', 'contents': [],\n"
2222 " 'external-contents': '//root/external/dir'}]}",
2224 EXPECT_EQ(nullptr, FS
.get());
2226 // 'directory-remap' with 'contents'
2227 FS
= getFromYAMLString(
2229 "{ 'type': 'directory-remap', 'name': '//root/A', 'contents': [] }]}",
2231 EXPECT_EQ(nullptr, FS
.get());
2233 // invalid redirect kind
2234 FS
= getFromYAMLString("{ 'redirecting-with': 'none', 'roots': [{\n"
2235 " 'type': 'directory-remap',\n"
2236 " 'name': '//root/A',\n"
2237 " 'external-contents': '//root/B' }]}",
2239 EXPECT_EQ(nullptr, FS
.get());
2241 // redirect and fallthrough passed
2242 FS
= getFromYAMLString("{ 'redirecting-with': 'fallthrough',\n"
2243 " 'fallthrough': true,\n"
2245 " 'type': 'directory-remap',\n"
2246 " 'name': '//root/A',\n"
2247 " 'external-contents': '//root/B' }]}",
2249 EXPECT_EQ(nullptr, FS
.get());
2251 EXPECT_EQ(28, NumDiagnostics
);
2254 TEST_F(VFSFromYAMLTest
, UseExternalName
) {
2255 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
2256 Lower
->addRegularFile("//root/external/file");
2258 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
=
2259 getFromYAMLString("{ 'roots': [\n"
2260 " { 'type': 'file', 'name': '//root/A',\n"
2261 " 'external-contents': '//root/external/file'\n"
2263 " { 'type': 'file', 'name': '//root/B',\n"
2264 " 'use-external-name': true,\n"
2265 " 'external-contents': '//root/external/file'\n"
2267 " { 'type': 'file', 'name': '//root/C',\n"
2268 " 'use-external-name': false,\n"
2269 " 'external-contents': '//root/external/file'\n"
2273 ASSERT_NE(nullptr, FS
.get());
2276 EXPECT_EQ("//root/external/file", FS
->status("//root/A")->getName());
2278 EXPECT_EQ("//root/external/file", FS
->status("//root/B")->getName());
2279 EXPECT_EQ("//root/C", FS
->status("//root/C")->getName());
2281 // global configuration
2282 FS
= getFromYAMLString("{ 'use-external-names': false,\n"
2284 " { 'type': 'file', 'name': '//root/A',\n"
2285 " 'external-contents': '//root/external/file'\n"
2287 " { 'type': 'file', 'name': '//root/B',\n"
2288 " 'use-external-name': true,\n"
2289 " 'external-contents': '//root/external/file'\n"
2291 " { 'type': 'file', 'name': '//root/C',\n"
2292 " 'use-external-name': false,\n"
2293 " 'external-contents': '//root/external/file'\n"
2297 ASSERT_NE(nullptr, FS
.get());
2300 EXPECT_EQ("//root/A", FS
->status("//root/A")->getName());
2302 EXPECT_EQ("//root/external/file", FS
->status("//root/B")->getName());
2303 EXPECT_EQ("//root/C", FS
->status("//root/C")->getName());
2306 TEST_F(VFSFromYAMLTest
, MultiComponentPath
) {
2307 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
2308 Lower
->addRegularFile("//root/other");
2311 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
=
2312 getFromYAMLString("{ 'roots': [\n"
2313 " { 'type': 'file', 'name': '//root/path/to/file',\n"
2314 " 'external-contents': '//root/other' }]\n"
2317 ASSERT_NE(nullptr, FS
.get());
2318 EXPECT_FALSE(FS
->status("//root/path/to/file").getError());
2319 EXPECT_FALSE(FS
->status("//root/path/to").getError());
2320 EXPECT_FALSE(FS
->status("//root/path").getError());
2321 EXPECT_FALSE(FS
->status("//root/").getError());
2324 FS
= getFromYAMLString(
2326 " { 'type': 'directory', 'name': '//root/path/to',\n"
2327 " 'contents': [ { 'type': 'file', 'name': 'file',\n"
2328 " 'external-contents': '//root/other' }]}]\n"
2331 ASSERT_NE(nullptr, FS
.get());
2332 EXPECT_FALSE(FS
->status("//root/path/to/file").getError());
2333 EXPECT_FALSE(FS
->status("//root/path/to").getError());
2334 EXPECT_FALSE(FS
->status("//root/path").getError());
2335 EXPECT_FALSE(FS
->status("//root/").getError());
2338 FS
= getFromYAMLString(
2340 " { 'type': 'directory', 'name': '//root/',\n"
2341 " 'contents': [ { 'type': 'file', 'name': 'path/to/file',\n"
2342 " 'external-contents': '//root/other' }]}]\n"
2345 ASSERT_NE(nullptr, FS
.get());
2346 EXPECT_FALSE(FS
->status("//root/path/to/file").getError());
2347 EXPECT_FALSE(FS
->status("//root/path/to").getError());
2348 EXPECT_FALSE(FS
->status("//root/path").getError());
2349 EXPECT_FALSE(FS
->status("//root/").getError());
2352 TEST_F(VFSFromYAMLTest
, TrailingSlashes
) {
2353 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
2354 Lower
->addRegularFile("//root/other");
2357 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= getFromYAMLString(
2359 " { 'type': 'directory', 'name': '//root/path/to////',\n"
2360 " 'contents': [ { 'type': 'file', 'name': 'file',\n"
2361 " 'external-contents': '//root/other' }]}]\n"
2364 ASSERT_NE(nullptr, FS
.get());
2365 EXPECT_FALSE(FS
->status("//root/path/to/file").getError());
2366 EXPECT_FALSE(FS
->status("//root/path/to").getError());
2367 EXPECT_FALSE(FS
->status("//root/path").getError());
2368 EXPECT_FALSE(FS
->status("//root/").getError());
2371 TEST_F(VFSFromYAMLTest
, DirectoryIteration
) {
2372 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
2373 Lower
->addDirectory("//root/");
2374 Lower
->addDirectory("//root/foo");
2375 Lower
->addDirectory("//root/foo/bar");
2376 Lower
->addRegularFile("//root/foo/bar/a");
2377 Lower
->addRegularFile("//root/foo/bar/b");
2378 Lower
->addRegularFile("//root/file3");
2379 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= getFromYAMLString(
2380 "{ 'use-external-names': false,\n"
2383 " 'type': 'directory',\n"
2384 " 'name': '//root/',\n"
2385 " 'contents': [ {\n"
2386 " 'type': 'file',\n"
2387 " 'name': 'file1',\n"
2388 " 'external-contents': '//root/foo/bar/a'\n"
2391 " 'type': 'file',\n"
2392 " 'name': 'file2',\n"
2393 " 'external-contents': '//root/foo/bar/b'\n"
2400 ASSERT_NE(FS
.get(), nullptr);
2402 IntrusiveRefCntPtr
<vfs::OverlayFileSystem
> O(
2403 new vfs::OverlayFileSystem(Lower
));
2407 checkContents(O
->dir_begin("//root/", EC
),
2408 {"//root/file1", "//root/file2", "//root/file3", "//root/foo"});
2410 checkContents(O
->dir_begin("//root/foo/bar", EC
),
2411 {"//root/foo/bar/a", "//root/foo/bar/b"});
2414 TEST_F(VFSFromYAMLTest
, DirectoryIterationSameDirMultipleEntries
) {
2415 // https://llvm.org/bugs/show_bug.cgi?id=27725
2416 if (!supportsSameDirMultipleYAMLEntries())
2419 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
2420 Lower
->addDirectory("//root/zab");
2421 Lower
->addDirectory("//root/baz");
2422 Lower
->addRegularFile("//root/zab/a");
2423 Lower
->addRegularFile("//root/zab/b");
2424 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= getFromYAMLString(
2425 "{ 'use-external-names': false,\n"
2428 " 'type': 'directory',\n"
2429 " 'name': '//root/baz/',\n"
2430 " 'contents': [ {\n"
2431 " 'type': 'file',\n"
2433 " 'external-contents': '//root/zab/a'\n"
2438 " 'type': 'directory',\n"
2439 " 'name': '//root/baz/',\n"
2440 " 'contents': [ {\n"
2441 " 'type': 'file',\n"
2443 " 'external-contents': '//root/zab/b'\n"
2450 ASSERT_NE(FS
.get(), nullptr);
2452 IntrusiveRefCntPtr
<vfs::OverlayFileSystem
> O(
2453 new vfs::OverlayFileSystem(Lower
));
2458 checkContents(O
->dir_begin("//root/baz/", EC
),
2459 {"//root/baz/x", "//root/baz/y"});
2462 TEST_F(VFSFromYAMLTest
, RecursiveDirectoryIterationLevel
) {
2464 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
2465 Lower
->addDirectory("//root/a");
2466 Lower
->addDirectory("//root/a/b");
2467 Lower
->addDirectory("//root/a/b/c");
2468 Lower
->addRegularFile("//root/a/b/c/file");
2469 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= getFromYAMLString(
2470 "{ 'use-external-names': false,\n"
2473 " 'type': 'directory',\n"
2474 " 'name': '//root/a/b/c/',\n"
2475 " 'contents': [ {\n"
2476 " 'type': 'file',\n"
2477 " 'name': 'file',\n"
2478 " 'external-contents': '//root/a/b/c/file'\n"
2485 ASSERT_NE(FS
.get(), nullptr);
2487 IntrusiveRefCntPtr
<vfs::OverlayFileSystem
> O(
2488 new vfs::OverlayFileSystem(Lower
));
2493 // Test recursive_directory_iterator level()
2494 vfs::recursive_directory_iterator I
= vfs::recursive_directory_iterator(
2498 for (int l
= 0; I
!= E
; I
.increment(EC
), ++l
) {
2500 EXPECT_EQ(I
.level(), l
);
2505 TEST_F(VFSFromYAMLTest
, RelativePaths
) {
2506 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
2508 SmallString
<128> CWD
;
2509 EC
= llvm::sys::fs::current_path(CWD
);
2512 // Filename at root level without a parent directory.
2513 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= getFromYAMLString(
2515 " { 'type': 'file', 'name': 'file-not-in-directory.h',\n"
2516 " 'external-contents': '//root/external/file'\n"
2520 ASSERT_TRUE(FS
.get() != nullptr);
2521 SmallString
<128> ExpectedPathNotInDir("file-not-in-directory.h");
2522 llvm::sys::fs::make_absolute(ExpectedPathNotInDir
);
2523 checkContents(FS
->dir_begin(CWD
, EC
), {ExpectedPathNotInDir
});
2525 // Relative file path.
2526 FS
= getFromYAMLString("{ 'roots': [\n"
2527 " { 'type': 'file', 'name': 'relative/path.h',\n"
2528 " 'external-contents': '//root/external/file'\n"
2532 ASSERT_TRUE(FS
.get() != nullptr);
2533 SmallString
<128> Parent("relative");
2534 llvm::sys::fs::make_absolute(Parent
);
2535 auto I
= FS
->dir_begin(Parent
, EC
);
2537 // Convert to POSIX path for comparison of windows paths
2538 ASSERT_EQ("relative/path.h",
2539 getPosixPath(std::string(I
->path().substr(CWD
.size() + 1))));
2541 // Relative directory path.
2542 FS
= getFromYAMLString(
2544 " { 'type': 'directory', 'name': 'relative/directory/path.h',\n"
2549 ASSERT_TRUE(FS
.get() != nullptr);
2550 SmallString
<128> Root("relative/directory");
2551 llvm::sys::fs::make_absolute(Root
);
2552 I
= FS
->dir_begin(Root
, EC
);
2554 ASSERT_EQ("path.h", std::string(I
->path().substr(Root
.size() + 1)));
2556 EXPECT_EQ(0, NumDiagnostics
);
2559 TEST_F(VFSFromYAMLTest
, NonFallthroughDirectoryIteration
) {
2560 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
2561 Lower
->addDirectory("//root/");
2562 Lower
->addRegularFile("//root/a");
2563 Lower
->addRegularFile("//root/b");
2564 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= getFromYAMLString(
2565 "{ 'use-external-names': false,\n"
2566 " 'fallthrough': false,\n"
2569 " 'type': 'directory',\n"
2570 " 'name': '//root/',\n"
2571 " 'contents': [ {\n"
2572 " 'type': 'file',\n"
2574 " 'external-contents': '//root/a'\n"
2581 ASSERT_NE(FS
.get(), nullptr);
2584 checkContents(FS
->dir_begin("//root/", EC
),
2588 TEST_F(VFSFromYAMLTest
, DirectoryIterationWithDuplicates
) {
2589 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
2590 Lower
->addDirectory("//root/");
2591 Lower
->addRegularFile("//root/a");
2592 Lower
->addRegularFile("//root/b");
2593 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= getFromYAMLString(
2594 "{ 'use-external-names': false,\n"
2597 " 'type': 'directory',\n"
2598 " 'name': '//root/',\n"
2599 " 'contents': [ {\n"
2600 " 'type': 'file',\n"
2602 " 'external-contents': '//root/a'\n"
2609 ASSERT_NE(FS
.get(), nullptr);
2612 checkContents(FS
->dir_begin("//root/", EC
),
2613 {"//root/a", "//root/b"});
2616 TEST_F(VFSFromYAMLTest
, DirectoryIterationErrorInVFSLayer
) {
2617 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
2618 Lower
->addDirectory("//root/");
2619 Lower
->addDirectory("//root/foo");
2620 Lower
->addRegularFile("//root/foo/a");
2621 Lower
->addRegularFile("//root/foo/b");
2622 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= getFromYAMLString(
2623 "{ 'use-external-names': false,\n"
2626 " 'type': 'directory',\n"
2627 " 'name': '//root/',\n"
2628 " 'contents': [ {\n"
2629 " 'type': 'file',\n"
2630 " 'name': 'bar/a',\n"
2631 " 'external-contents': '//root/foo/a'\n"
2638 ASSERT_NE(FS
.get(), nullptr);
2641 checkContents(FS
->dir_begin("//root/foo", EC
),
2642 {"//root/foo/a", "//root/foo/b"});
2645 TEST_F(VFSFromYAMLTest
, GetRealPath
) {
2646 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
2647 Lower
->addDirectory("//dir/");
2648 Lower
->addRegularFile("/foo");
2649 Lower
->addSymlink("/link");
2650 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= getFromYAMLString(
2651 "{ 'use-external-names': false,\n"
2652 " 'case-sensitive': false,\n"
2655 " 'type': 'directory',\n"
2656 " 'name': '//root/',\n"
2657 " 'contents': [ {\n"
2658 " 'type': 'file',\n"
2660 " 'external-contents': '/link'\n"
2663 " 'type': 'directory',\n"
2670 " 'type': 'directory',\n"
2671 " 'name': '//dir/',\n"
2677 ASSERT_NE(FS
.get(), nullptr);
2679 // Regular file present in underlying file system.
2680 SmallString
<16> RealPath
;
2681 EXPECT_FALSE(FS
->getRealPath("/foo", RealPath
));
2682 EXPECT_EQ(RealPath
.str(), "/foo");
2684 // File present in YAML pointing to symlink in underlying file system.
2685 EXPECT_FALSE(FS
->getRealPath("//root/bar", RealPath
));
2686 EXPECT_EQ(RealPath
.str(), "/symlink");
2688 // Directories should return the virtual path as written in the definition.
2689 EXPECT_FALSE(FS
->getRealPath("//ROOT/baz", RealPath
));
2690 EXPECT_EQ(RealPath
.str(), "//root/baz");
2692 // Try a non-existing file.
2693 EXPECT_EQ(FS
->getRealPath("/non_existing", RealPath
),
2694 errc::no_such_file_or_directory
);
2697 TEST_F(VFSFromYAMLTest
, WorkingDirectory
) {
2698 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
2699 Lower
->addDirectory("//root/");
2700 Lower
->addDirectory("//root/foo");
2701 Lower
->addRegularFile("//root/foo/a");
2702 Lower
->addRegularFile("//root/foo/b");
2703 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= getFromYAMLString(
2704 "{ 'use-external-names': false,\n"
2707 " 'type': 'directory',\n"
2708 " 'name': '//root/bar',\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/bar");
2723 llvm::ErrorOr
<std::string
> WorkingDir
= FS
->getCurrentWorkingDirectory();
2724 ASSERT_TRUE(WorkingDir
);
2725 EXPECT_EQ(*WorkingDir
, "//root/bar");
2727 llvm::ErrorOr
<vfs::Status
> Status
= FS
->status("./a");
2728 ASSERT_FALSE(Status
.getError());
2729 EXPECT_TRUE(Status
->isStatusKnown());
2730 EXPECT_FALSE(Status
->isDirectory());
2731 EXPECT_TRUE(Status
->isRegularFile());
2732 EXPECT_FALSE(Status
->isSymlink());
2733 EXPECT_FALSE(Status
->isOther());
2734 EXPECT_TRUE(Status
->exists());
2736 EC
= FS
->setCurrentWorkingDirectory("bogus");
2738 WorkingDir
= FS
->getCurrentWorkingDirectory();
2739 ASSERT_TRUE(WorkingDir
);
2740 EXPECT_EQ(*WorkingDir
, "//root/bar");
2742 EC
= FS
->setCurrentWorkingDirectory("//root/");
2744 WorkingDir
= FS
->getCurrentWorkingDirectory();
2745 ASSERT_TRUE(WorkingDir
);
2746 EXPECT_EQ(*WorkingDir
, "//root/");
2748 EC
= FS
->setCurrentWorkingDirectory("bar");
2750 WorkingDir
= FS
->getCurrentWorkingDirectory();
2751 ASSERT_TRUE(WorkingDir
);
2752 EXPECT_EQ(*WorkingDir
, "//root/bar");
2755 TEST_F(VFSFromYAMLTest
, WorkingDirectoryFallthrough
) {
2756 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
2757 Lower
->addDirectory("//root/");
2758 Lower
->addDirectory("//root/foo");
2759 Lower
->addRegularFile("//root/foo/a");
2760 Lower
->addRegularFile("//root/foo/b");
2761 Lower
->addRegularFile("//root/c");
2762 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= getFromYAMLString(
2763 "{ 'use-external-names': false,\n"
2766 " 'type': 'directory',\n"
2767 " 'name': '//root/bar',\n"
2768 " 'contents': [ {\n"
2769 " 'type': 'file',\n"
2771 " 'external-contents': '//root/foo/a'\n"
2776 " 'type': 'directory',\n"
2777 " 'name': '//root/bar/baz',\n"
2778 " 'contents': [ {\n"
2779 " 'type': 'file',\n"
2781 " 'external-contents': '//root/foo/a'\n"
2788 ASSERT_NE(FS
.get(), nullptr);
2789 std::error_code EC
= FS
->setCurrentWorkingDirectory("//root/");
2791 ASSERT_NE(FS
.get(), nullptr);
2793 llvm::ErrorOr
<vfs::Status
> Status
= FS
->status("bar/a");
2794 ASSERT_FALSE(Status
.getError());
2795 EXPECT_TRUE(Status
->exists());
2797 Status
= FS
->status("foo/a");
2798 ASSERT_FALSE(Status
.getError());
2799 EXPECT_TRUE(Status
->exists());
2801 EC
= FS
->setCurrentWorkingDirectory("//root/bar");
2804 Status
= FS
->status("./a");
2805 ASSERT_FALSE(Status
.getError());
2806 EXPECT_TRUE(Status
->exists());
2808 Status
= FS
->status("./b");
2809 ASSERT_TRUE(Status
.getError());
2811 Status
= FS
->status("./c");
2812 ASSERT_TRUE(Status
.getError());
2814 EC
= FS
->setCurrentWorkingDirectory("//root/");
2817 Status
= FS
->status("c");
2818 ASSERT_FALSE(Status
.getError());
2819 EXPECT_TRUE(Status
->exists());
2821 Status
= FS
->status("./bar/baz/a");
2822 ASSERT_FALSE(Status
.getError());
2823 EXPECT_TRUE(Status
->exists());
2825 EC
= FS
->setCurrentWorkingDirectory("//root/bar");
2828 Status
= FS
->status("./baz/a");
2829 ASSERT_FALSE(Status
.getError());
2830 EXPECT_TRUE(Status
->exists());
2832 Status
= FS
->status("../bar/baz/a");
2833 ASSERT_FALSE(Status
.getError());
2834 EXPECT_TRUE(Status
->exists());
2837 TEST_F(VFSFromYAMLTest
, WorkingDirectoryFallthroughInvalid
) {
2838 IntrusiveRefCntPtr
<ErrorDummyFileSystem
> Lower(new ErrorDummyFileSystem());
2839 Lower
->addDirectory("//root/");
2840 Lower
->addDirectory("//root/foo");
2841 Lower
->addRegularFile("//root/foo/a");
2842 Lower
->addRegularFile("//root/foo/b");
2843 Lower
->addRegularFile("//root/c");
2844 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= getFromYAMLString(
2845 "{ 'use-external-names': false,\n"
2848 " 'type': 'directory',\n"
2849 " 'name': '//root/bar',\n"
2850 " 'contents': [ {\n"
2851 " 'type': 'file',\n"
2853 " 'external-contents': '//root/foo/a'\n"
2860 ASSERT_NE(FS
.get(), nullptr);
2861 std::error_code EC
= FS
->setCurrentWorkingDirectory("//root/");
2863 ASSERT_NE(FS
.get(), nullptr);
2865 llvm::ErrorOr
<vfs::Status
> Status
= FS
->status("bar/a");
2866 ASSERT_FALSE(Status
.getError());
2867 EXPECT_TRUE(Status
->exists());
2869 Status
= FS
->status("foo/a");
2870 ASSERT_FALSE(Status
.getError());
2871 EXPECT_TRUE(Status
->exists());
2874 TEST_F(VFSFromYAMLTest
, VirtualWorkingDirectory
) {
2875 IntrusiveRefCntPtr
<ErrorDummyFileSystem
> Lower(new ErrorDummyFileSystem());
2876 Lower
->addDirectory("//root/");
2877 Lower
->addDirectory("//root/foo");
2878 Lower
->addRegularFile("//root/foo/a");
2879 Lower
->addRegularFile("//root/foo/b");
2880 Lower
->addRegularFile("//root/c");
2881 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= getFromYAMLString(
2882 "{ 'use-external-names': false,\n"
2885 " 'type': 'directory',\n"
2886 " 'name': '//root/bar',\n"
2887 " 'contents': [ {\n"
2888 " 'type': 'file',\n"
2890 " 'external-contents': '//root/foo/a'\n"
2897 ASSERT_NE(FS
.get(), nullptr);
2898 std::error_code EC
= FS
->setCurrentWorkingDirectory("//root/bar");
2900 ASSERT_NE(FS
.get(), nullptr);
2902 llvm::ErrorOr
<vfs::Status
> Status
= FS
->status("a");
2903 ASSERT_FALSE(Status
.getError());
2904 EXPECT_TRUE(Status
->exists());
2907 TEST_F(VFSFromYAMLTest
, YAMLVFSWriterTest
) {
2908 TempDir
TestDirectory("virtual-file-system-test", /*Unique*/ true);
2909 TempDir
_a(TestDirectory
.path("a"));
2910 TempFile
_ab(TestDirectory
.path("a, b"));
2911 TempDir
_c(TestDirectory
.path("c"));
2912 TempFile
_cd(TestDirectory
.path("c/d"));
2913 TempDir
_e(TestDirectory
.path("e"));
2914 TempDir
_ef(TestDirectory
.path("e/f"));
2915 TempFile
_g(TestDirectory
.path("g"));
2916 TempDir
_h(TestDirectory
.path("h"));
2918 vfs::YAMLVFSWriter VFSWriter
;
2919 VFSWriter
.addDirectoryMapping(_a
.path(), "//root/a");
2920 VFSWriter
.addFileMapping(_ab
.path(), "//root/a/b");
2921 VFSWriter
.addFileMapping(_cd
.path(), "//root/c/d");
2922 VFSWriter
.addDirectoryMapping(_e
.path(), "//root/e");
2923 VFSWriter
.addDirectoryMapping(_ef
.path(), "//root/e/f");
2924 VFSWriter
.addFileMapping(_g
.path(), "//root/g");
2925 VFSWriter
.addDirectoryMapping(_h
.path(), "//root/h");
2928 raw_string_ostream
OS(Buffer
);
2929 VFSWriter
.write(OS
);
2932 IntrusiveRefCntPtr
<ErrorDummyFileSystem
> Lower(new ErrorDummyFileSystem());
2933 Lower
->addDirectory("//root/");
2934 Lower
->addDirectory("//root/a");
2935 Lower
->addRegularFile("//root/a/b");
2936 Lower
->addDirectory("//root/b");
2937 Lower
->addDirectory("//root/c");
2938 Lower
->addRegularFile("//root/c/d");
2939 Lower
->addDirectory("//root/e");
2940 Lower
->addDirectory("//root/e/f");
2941 Lower
->addRegularFile("//root/g");
2942 Lower
->addDirectory("//root/h");
2944 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= getFromYAMLRawString(Buffer
, Lower
);
2945 ASSERT_NE(FS
.get(), nullptr);
2947 EXPECT_TRUE(FS
->exists(_a
.path()));
2948 EXPECT_TRUE(FS
->exists(_ab
.path()));
2949 EXPECT_TRUE(FS
->exists(_c
.path()));
2950 EXPECT_TRUE(FS
->exists(_cd
.path()));
2951 EXPECT_TRUE(FS
->exists(_e
.path()));
2952 EXPECT_TRUE(FS
->exists(_ef
.path()));
2953 EXPECT_TRUE(FS
->exists(_g
.path()));
2954 EXPECT_TRUE(FS
->exists(_h
.path()));
2957 TEST_F(VFSFromYAMLTest
, YAMLVFSWriterTest2
) {
2958 TempDir
TestDirectory("virtual-file-system-test", /*Unique*/ true);
2959 TempDir
_a(TestDirectory
.path("a"));
2960 TempFile
_ab(TestDirectory
.path("a/b"));
2961 TempDir
_ac(TestDirectory
.path("a/c"));
2962 TempFile
_acd(TestDirectory
.path("a/c/d"));
2963 TempFile
_ace(TestDirectory
.path("a/c/e"));
2964 TempFile
_acf(TestDirectory
.path("a/c/f"));
2965 TempDir
_ag(TestDirectory
.path("a/g"));
2966 TempFile
_agh(TestDirectory
.path("a/g/h"));
2968 vfs::YAMLVFSWriter VFSWriter
;
2969 VFSWriter
.addDirectoryMapping(_a
.path(), "//root/a");
2970 VFSWriter
.addFileMapping(_ab
.path(), "//root/a/b");
2971 VFSWriter
.addDirectoryMapping(_ac
.path(), "//root/a/c");
2972 VFSWriter
.addFileMapping(_acd
.path(), "//root/a/c/d");
2973 VFSWriter
.addFileMapping(_ace
.path(), "//root/a/c/e");
2974 VFSWriter
.addFileMapping(_acf
.path(), "//root/a/c/f");
2975 VFSWriter
.addDirectoryMapping(_ag
.path(), "//root/a/g");
2976 VFSWriter
.addFileMapping(_agh
.path(), "//root/a/g/h");
2979 raw_string_ostream
OS(Buffer
);
2980 VFSWriter
.write(OS
);
2983 IntrusiveRefCntPtr
<ErrorDummyFileSystem
> Lower(new ErrorDummyFileSystem());
2984 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= getFromYAMLRawString(Buffer
, Lower
);
2985 EXPECT_NE(FS
.get(), nullptr);
2988 TEST_F(VFSFromYAMLTest
, YAMLVFSWriterTest3
) {
2989 TempDir
TestDirectory("virtual-file-system-test", /*Unique*/ true);
2990 TempDir
_a(TestDirectory
.path("a"));
2991 TempFile
_ab(TestDirectory
.path("a/b"));
2992 TempDir
_ac(TestDirectory
.path("a/c"));
2993 TempDir
_acd(TestDirectory
.path("a/c/d"));
2994 TempDir
_acde(TestDirectory
.path("a/c/d/e"));
2995 TempFile
_acdef(TestDirectory
.path("a/c/d/e/f"));
2996 TempFile
_acdeg(TestDirectory
.path("a/c/d/e/g"));
2997 TempDir
_ah(TestDirectory
.path("a/h"));
2998 TempFile
_ahi(TestDirectory
.path("a/h/i"));
3000 vfs::YAMLVFSWriter VFSWriter
;
3001 VFSWriter
.addDirectoryMapping(_a
.path(), "//root/a");
3002 VFSWriter
.addFileMapping(_ab
.path(), "//root/a/b");
3003 VFSWriter
.addDirectoryMapping(_ac
.path(), "//root/a/c");
3004 VFSWriter
.addDirectoryMapping(_acd
.path(), "//root/a/c/d");
3005 VFSWriter
.addDirectoryMapping(_acde
.path(), "//root/a/c/d/e");
3006 VFSWriter
.addFileMapping(_acdef
.path(), "//root/a/c/d/e/f");
3007 VFSWriter
.addFileMapping(_acdeg
.path(), "//root/a/c/d/e/g");
3008 VFSWriter
.addDirectoryMapping(_ahi
.path(), "//root/a/h");
3009 VFSWriter
.addFileMapping(_ahi
.path(), "//root/a/h/i");
3012 raw_string_ostream
OS(Buffer
);
3013 VFSWriter
.write(OS
);
3016 IntrusiveRefCntPtr
<ErrorDummyFileSystem
> Lower(new ErrorDummyFileSystem());
3017 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= getFromYAMLRawString(Buffer
, Lower
);
3018 EXPECT_NE(FS
.get(), nullptr);
3021 TEST_F(VFSFromYAMLTest
, YAMLVFSWriterTestHandleDirs
) {
3022 TempDir
TestDirectory("virtual-file-system-test", /*Unique*/ true);
3023 TempDir
_a(TestDirectory
.path("a"));
3024 TempDir
_b(TestDirectory
.path("b"));
3025 TempDir
_c(TestDirectory
.path("c"));
3027 vfs::YAMLVFSWriter VFSWriter
;
3028 VFSWriter
.addDirectoryMapping(_a
.path(), "//root/a");
3029 VFSWriter
.addDirectoryMapping(_b
.path(), "//root/b");
3030 VFSWriter
.addDirectoryMapping(_c
.path(), "//root/c");
3033 raw_string_ostream
OS(Buffer
);
3034 VFSWriter
.write(OS
);
3037 // We didn't add a single file - only directories.
3038 EXPECT_EQ(Buffer
.find("'type': 'file'"), std::string::npos
);
3040 IntrusiveRefCntPtr
<ErrorDummyFileSystem
> Lower(new ErrorDummyFileSystem());
3041 Lower
->addDirectory("//root/a");
3042 Lower
->addDirectory("//root/b");
3043 Lower
->addDirectory("//root/c");
3045 Lower
->addRegularFile("//root/a/a");
3046 Lower
->addRegularFile("//root/b/b");
3047 Lower
->addRegularFile("//root/c/c");
3049 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= getFromYAMLRawString(Buffer
, Lower
);
3050 ASSERT_NE(FS
.get(), nullptr);
3052 EXPECT_FALSE(FS
->exists(_a
.path("a")));
3053 EXPECT_FALSE(FS
->exists(_b
.path("b")));
3054 EXPECT_FALSE(FS
->exists(_c
.path("c")));
3057 TEST_F(VFSFromYAMLTest
, RedirectingWith
) {
3058 IntrusiveRefCntPtr
<DummyFileSystem
> Both(new DummyFileSystem());
3059 Both
->addDirectory("//root/a");
3060 Both
->addRegularFile("//root/a/f");
3061 Both
->addDirectory("//root/b");
3062 Both
->addRegularFile("//root/b/f");
3064 IntrusiveRefCntPtr
<DummyFileSystem
> AOnly(new DummyFileSystem());
3065 AOnly
->addDirectory("//root/a");
3066 AOnly
->addRegularFile("//root/a/f");
3068 IntrusiveRefCntPtr
<DummyFileSystem
> BOnly(new DummyFileSystem());
3069 BOnly
->addDirectory("//root/b");
3070 BOnly
->addRegularFile("//root/b/f");
3072 auto BaseStr
= std::string(" 'roots': [\n"
3074 " 'type': 'directory-remap',\n"
3075 " 'name': '//root/a',\n"
3076 " 'external-contents': '//root/b'\n"
3080 auto FallthroughStr
= "{ 'redirecting-with': 'fallthrough',\n" + BaseStr
;
3081 auto FallbackStr
= "{ 'redirecting-with': 'fallback',\n" + BaseStr
;
3082 auto RedirectOnlyStr
= "{ 'redirecting-with': 'redirect-only',\n" + BaseStr
;
3084 auto ExpectPath
= [&](vfs::FileSystem
&FS
, StringRef Expected
,
3085 StringRef Message
) {
3086 auto AF
= FS
.openFileForRead("//root/a/f");
3087 ASSERT_FALSE(AF
.getError()) << Message
;
3088 auto AFName
= (*AF
)->getName();
3089 ASSERT_FALSE(AFName
.getError()) << Message
;
3090 EXPECT_EQ(Expected
.str(), AFName
.get()) << Message
;
3092 auto AS
= FS
.status("//root/a/f");
3093 ASSERT_FALSE(AS
.getError()) << Message
;
3094 EXPECT_EQ(Expected
.str(), AS
->getName()) << Message
;
3097 auto ExpectFailure
= [&](vfs::FileSystem
&FS
, StringRef Message
) {
3098 EXPECT_TRUE(FS
.openFileForRead("//root/a/f").getError()) << Message
;
3099 EXPECT_TRUE(FS
.status("//root/a/f").getError()) << Message
;
3103 // `f` in both `a` and `b`
3105 // `fallthrough` tries `external-name` first, so should be `b`
3106 IntrusiveRefCntPtr
<vfs::FileSystem
> Fallthrough
=
3107 getFromYAMLString(FallthroughStr
, Both
);
3108 ASSERT_TRUE(Fallthrough
.get() != nullptr);
3109 ExpectPath(*Fallthrough
, "//root/b/f", "fallthrough, both exist");
3111 // `fallback` tries the original name first, so should be `a`
3112 IntrusiveRefCntPtr
<vfs::FileSystem
> Fallback
=
3113 getFromYAMLString(FallbackStr
, Both
);
3114 ASSERT_TRUE(Fallback
.get() != nullptr);
3115 ExpectPath(*Fallback
, "//root/a/f", "fallback, both exist");
3117 // `redirect-only` is the same as `fallthrough` but doesn't try the
3118 // original on failure, so no change here (ie. `b`)
3119 IntrusiveRefCntPtr
<vfs::FileSystem
> Redirect
=
3120 getFromYAMLString(RedirectOnlyStr
, Both
);
3121 ASSERT_TRUE(Redirect
.get() != nullptr);
3122 ExpectPath(*Redirect
, "//root/b/f", "redirect-only, both exist");
3128 // Fallthrough to the original path, `a`
3129 IntrusiveRefCntPtr
<vfs::FileSystem
> Fallthrough
=
3130 getFromYAMLString(FallthroughStr
, AOnly
);
3131 ASSERT_TRUE(Fallthrough
.get() != nullptr);
3132 ExpectPath(*Fallthrough
, "//root/a/f", "fallthrough, a only");
3134 // Original first, so still `a`
3135 IntrusiveRefCntPtr
<vfs::FileSystem
> Fallback
=
3136 getFromYAMLString(FallbackStr
, AOnly
);
3137 ASSERT_TRUE(Fallback
.get() != nullptr);
3138 ExpectPath(*Fallback
, "//root/a/f", "fallback, a only");
3140 // Fails since no fallthrough
3141 IntrusiveRefCntPtr
<vfs::FileSystem
> Redirect
=
3142 getFromYAMLString(RedirectOnlyStr
, AOnly
);
3143 ASSERT_TRUE(Redirect
.get() != nullptr);
3144 ExpectFailure(*Redirect
, "redirect-only, a only");
3150 // Tries `b` first (no fallthrough)
3151 IntrusiveRefCntPtr
<vfs::FileSystem
> Fallthrough
=
3152 getFromYAMLString(FallthroughStr
, BOnly
);
3153 ASSERT_TRUE(Fallthrough
.get() != nullptr);
3154 ExpectPath(*Fallthrough
, "//root/b/f", "fallthrough, b only");
3156 // Tries original first but then fallsback to `b`
3157 IntrusiveRefCntPtr
<vfs::FileSystem
> Fallback
=
3158 getFromYAMLString(FallbackStr
, BOnly
);
3159 ASSERT_TRUE(Fallback
.get() != nullptr);
3160 ExpectPath(*Fallback
, "//root/b/f", "fallback, b only");
3162 // Redirect exists, so uses it (`b`)
3163 IntrusiveRefCntPtr
<vfs::FileSystem
> Redirect
=
3164 getFromYAMLString(RedirectOnlyStr
, BOnly
);
3165 ASSERT_TRUE(Redirect
.get() != nullptr);
3166 ExpectPath(*Redirect
, "//root/b/f", "redirect-only, b only");
3169 EXPECT_EQ(0, NumDiagnostics
);
3172 TEST(VFSFromRemappedFilesTest
, Basic
) {
3173 IntrusiveRefCntPtr
<vfs::InMemoryFileSystem
> BaseFS
=
3174 new vfs::InMemoryFileSystem
;
3175 BaseFS
->addFile("//root/b", 0, MemoryBuffer::getMemBuffer("contents of b"));
3176 BaseFS
->addFile("//root/c", 0, MemoryBuffer::getMemBuffer("contents of c"));
3178 std::vector
<std::pair
<std::string
, std::string
>> RemappedFiles
= {
3179 {"//root/a/a", "//root/b"},
3180 {"//root/a/b/c", "//root/c"},
3182 auto RemappedFS
= vfs::RedirectingFileSystem::create(
3183 RemappedFiles
, /*UseExternalNames=*/false, *BaseFS
);
3185 auto StatA
= RemappedFS
->status("//root/a/a");
3186 auto StatB
= RemappedFS
->status("//root/a/b/c");
3189 EXPECT_EQ("//root/a/a", StatA
->getName());
3190 EXPECT_EQ("//root/a/b/c", StatB
->getName());
3192 auto BufferA
= RemappedFS
->getBufferForFile("//root/a/a");
3193 auto BufferB
= RemappedFS
->getBufferForFile("//root/a/b/c");
3194 ASSERT_TRUE(BufferA
);
3195 ASSERT_TRUE(BufferB
);
3196 EXPECT_EQ("contents of b", (*BufferA
)->getBuffer());
3197 EXPECT_EQ("contents of c", (*BufferB
)->getBuffer());
3200 TEST(VFSFromRemappedFilesTest
, UseExternalNames
) {
3201 IntrusiveRefCntPtr
<vfs::InMemoryFileSystem
> BaseFS
=
3202 new vfs::InMemoryFileSystem
;
3203 BaseFS
->addFile("//root/b", 0, MemoryBuffer::getMemBuffer("contents of b"));
3204 BaseFS
->addFile("//root/c", 0, MemoryBuffer::getMemBuffer("contents of c"));
3206 std::vector
<std::pair
<std::string
, std::string
>> RemappedFiles
= {
3207 {"//root/a/a", "//root/b"},
3208 {"//root/a/b/c", "//root/c"},
3210 auto RemappedFS
= vfs::RedirectingFileSystem::create(
3211 RemappedFiles
, /*UseExternalNames=*/true, *BaseFS
);
3213 auto StatA
= RemappedFS
->status("//root/a/a");
3214 auto StatB
= RemappedFS
->status("//root/a/b/c");
3217 EXPECT_EQ("//root/b", StatA
->getName());
3218 EXPECT_EQ("//root/c", StatB
->getName());
3220 auto BufferA
= RemappedFS
->getBufferForFile("//root/a/a");
3221 auto BufferB
= RemappedFS
->getBufferForFile("//root/a/b/c");
3222 ASSERT_TRUE(BufferA
);
3223 ASSERT_TRUE(BufferB
);
3224 EXPECT_EQ("contents of b", (*BufferA
)->getBuffer());
3225 EXPECT_EQ("contents of c", (*BufferB
)->getBuffer());
3228 TEST(VFSFromRemappedFilesTest
, LastMappingWins
) {
3229 IntrusiveRefCntPtr
<vfs::InMemoryFileSystem
> BaseFS
=
3230 new vfs::InMemoryFileSystem
;
3231 BaseFS
->addFile("//root/b", 0, MemoryBuffer::getMemBuffer("contents of b"));
3232 BaseFS
->addFile("//root/c", 0, MemoryBuffer::getMemBuffer("contents of c"));
3234 std::vector
<std::pair
<std::string
, std::string
>> RemappedFiles
= {
3235 {"//root/a", "//root/b"},
3236 {"//root/a", "//root/c"},
3238 auto RemappedFSKeepName
= vfs::RedirectingFileSystem::create(
3239 RemappedFiles
, /*UseExternalNames=*/false, *BaseFS
);
3240 auto RemappedFSExternalName
= vfs::RedirectingFileSystem::create(
3241 RemappedFiles
, /*UseExternalNames=*/true, *BaseFS
);
3243 auto StatKeepA
= RemappedFSKeepName
->status("//root/a");
3244 auto StatExternalA
= RemappedFSExternalName
->status("//root/a");
3245 ASSERT_TRUE(StatKeepA
);
3246 ASSERT_TRUE(StatExternalA
);
3247 EXPECT_EQ("//root/a", StatKeepA
->getName());
3248 EXPECT_EQ("//root/c", StatExternalA
->getName());
3250 auto BufferKeepA
= RemappedFSKeepName
->getBufferForFile("//root/a");
3251 auto BufferExternalA
= RemappedFSExternalName
->getBufferForFile("//root/a");
3252 ASSERT_TRUE(BufferKeepA
);
3253 ASSERT_TRUE(BufferExternalA
);
3254 EXPECT_EQ("contents of c", (*BufferKeepA
)->getBuffer());
3255 EXPECT_EQ("contents of c", (*BufferExternalA
)->getBuffer());
3258 TEST(RedirectingFileSystemTest
, PrintOutput
) {
3260 MemoryBuffer::getMemBuffer("{\n"
3264 " 'type': 'directory-remap',\n"
3265 " 'name': '/dremap',\n"
3266 " 'external-contents': '/a',\n"
3269 " 'type': 'directory',\n"
3270 " 'name': '/vdir',\n"
3273 " 'type': 'directory-remap',\n"
3274 " 'name': 'dremap',\n"
3275 " 'external-contents': '/b'\n"
3276 " 'use-external-name': 'true'\n"
3279 " 'type': 'file',\n"
3280 " 'name': 'vfile',\n"
3281 " 'external-contents': '/c'\n"
3282 " 'use-external-name': 'false'\n"
3287 auto Dummy
= makeIntrusiveRefCnt
<DummyFileSystem
>();
3288 auto Redirecting
= vfs::RedirectingFileSystem::create(
3289 std::move(Buffer
), nullptr, "", nullptr, Dummy
);
3291 SmallString
<0> Output
;
3292 raw_svector_ostream OuputStream
{Output
};
3294 Redirecting
->print(OuputStream
, vfs::FileSystem::PrintType::Summary
);
3295 ASSERT_EQ("RedirectingFileSystem (UseExternalNames: true)\n", Output
);
3298 Redirecting
->print(OuputStream
, vfs::FileSystem::PrintType::Contents
);
3299 ASSERT_EQ("RedirectingFileSystem (UseExternalNames: true)\n"
3301 " 'dremap' -> '/a'\n"
3303 " 'dremap' -> '/b' (UseExternalName: true)\n"
3304 " 'vfile' -> '/c' (UseExternalName: false)\n"
3306 " DummyFileSystem (Summary)\n",
3310 Redirecting
->print(OuputStream
, vfs::FileSystem::PrintType::Contents
, 1);
3311 ASSERT_EQ(" RedirectingFileSystem (UseExternalNames: true)\n"
3313 " 'dremap' -> '/a'\n"
3315 " 'dremap' -> '/b' (UseExternalName: true)\n"
3316 " 'vfile' -> '/c' (UseExternalName: false)\n"
3318 " DummyFileSystem (Summary)\n",
3322 Redirecting
->print(OuputStream
,
3323 vfs::FileSystem::PrintType::RecursiveContents
);
3324 ASSERT_EQ("RedirectingFileSystem (UseExternalNames: true)\n"
3326 " 'dremap' -> '/a'\n"
3328 " 'dremap' -> '/b' (UseExternalName: true)\n"
3329 " 'vfile' -> '/c' (UseExternalName: false)\n"
3331 " DummyFileSystem (RecursiveContents)\n",
3335 TEST(RedirectingFileSystemTest
, Used
) {
3336 auto Dummy
= makeIntrusiveRefCnt
<DummyFileSystem
>();
3338 MemoryBuffer::getMemBuffer("{\n"
3340 " 'redirecting-with': 'fallthrough',\n"
3343 " 'type': 'file',\n"
3344 " 'name': '/vfile1',\n"
3345 " 'external-contents': '/a',\n"
3350 MemoryBuffer::getMemBuffer("{\n"
3352 " 'redirecting-with': 'fallthrough',\n"
3355 " 'type': 'file',\n"
3356 " 'name': '/vfile2',\n"
3357 " 'external-contents': '/b',\n"
3362 Dummy
->addRegularFile("/a");
3363 Dummy
->addRegularFile("/b");
3365 IntrusiveRefCntPtr
<vfs::RedirectingFileSystem
> Redirecting1
=
3366 vfs::RedirectingFileSystem::create(std::move(YAML1
), nullptr, "", nullptr,
3369 auto Redirecting2
= vfs::RedirectingFileSystem::create(
3370 std::move(YAML2
), nullptr, "", nullptr, Redirecting1
);
3372 Redirecting1
->setUsageTrackingActive(true);
3373 Redirecting2
->setUsageTrackingActive(true);
3374 EXPECT_TRUE(Redirecting2
->exists("/vfile1"));
3375 EXPECT_TRUE(Redirecting2
->exists("/b"));
3376 EXPECT_TRUE(Redirecting1
->hasBeenUsed());
3377 EXPECT_FALSE(Redirecting2
->hasBeenUsed());
3380 // Check that paths looked up in the external filesystem are unmodified, except
3381 // potentially to add the working directory. We cannot canonicalize away ..
3382 // in the presence of symlinks in the external filesystem.
3383 TEST(RedirectingFileSystemTest
, ExternalPaths
) {
3384 struct InterceptorFS
: llvm::vfs::ProxyFileSystem
{
3385 std::vector
<std::string
> SeenPaths
;
3387 InterceptorFS(IntrusiveRefCntPtr
<FileSystem
> UnderlyingFS
)
3388 : ProxyFileSystem(UnderlyingFS
) {}
3390 llvm::ErrorOr
<llvm::vfs::Status
> status(const Twine
&Path
) override
{
3391 SeenPaths
.push_back(Path
.str());
3392 return ProxyFileSystem::status(Path
);
3395 llvm::ErrorOr
<std::unique_ptr
<llvm::vfs::File
>>
3396 openFileForRead(const Twine
&Path
) override
{
3397 SeenPaths
.push_back(Path
.str());
3398 return ProxyFileSystem::openFileForRead(Path
);
3401 std::error_code
isLocal(const Twine
&Path
, bool &Result
) override
{
3402 SeenPaths
.push_back(Path
.str());
3403 return ProxyFileSystem::isLocal(Path
, Result
);
3406 vfs::directory_iterator
dir_begin(const Twine
&Dir
,
3407 std::error_code
&EC
) override
{
3408 SeenPaths
.push_back(Dir
.str());
3409 return ProxyFileSystem::dir_begin(Dir
, EC
);
3412 bool exists(const Twine
&Path
) override
{
3413 SeenPaths
.push_back(Path
.str());
3414 return ProxyFileSystem::exists(Path
);
3419 auto BaseFS
= makeIntrusiveRefCnt
<DummyFileSystem
>();
3420 BaseFS
->setCurrentWorkingDirectory("/cwd");
3421 auto CheckFS
= makeIntrusiveRefCnt
<InterceptorFS
>(BaseFS
);
3422 auto FS
= vfs::RedirectingFileSystem::create({}, /*UseExternalNames=*/false,
3425 FS
->status("/a/../b");
3426 FS
->openFileForRead("c");
3428 bool IsLocal
= false;
3429 FS
->isLocal("/e/./../f", IsLocal
);
3430 FS
->dir_begin(".././g", EC
);
3432 std::vector
<std::string
> Expected
{"/a/../b", "/cwd/c", "/cwd/./d",
3433 "/e/./../f", "/cwd/.././g"};
3435 EXPECT_EQ(CheckFS
->SeenPaths
, Expected
);
3437 CheckFS
->SeenPaths
.clear();
3438 FS
->setRedirection(vfs::RedirectingFileSystem::RedirectKind::Fallback
);
3439 FS
->status("/a/../b");
3440 FS
->openFileForRead("c");
3442 FS
->isLocal("/e/./../f", IsLocal
);
3443 FS
->dir_begin(".././g", EC
);
3445 EXPECT_EQ(CheckFS
->SeenPaths
, Expected
);
3448 TEST(RedirectingFileSystemTest
, Exists
) {
3449 IntrusiveRefCntPtr
<DummyFileSystem
> Dummy(new NoStatusDummyFileSystem());
3451 MemoryBuffer::getMemBuffer("{\n"
3455 " 'type': 'directory-remap',\n"
3456 " 'name': '/dremap',\n"
3457 " 'external-contents': '/a',\n"
3460 " 'type': 'directory-remap',\n"
3461 " 'name': '/dmissing',\n"
3462 " 'external-contents': '/dmissing',\n"
3465 " 'type': 'directory',\n"
3466 " 'name': '/both',\n"
3469 " 'type': 'file',\n"
3470 " 'name': 'vfile',\n"
3471 " 'external-contents': '/c'\n"
3476 " 'type': 'directory',\n"
3477 " 'name': '/vdir',\n"
3480 " 'type': 'directory-remap',\n"
3481 " 'name': 'dremap',\n"
3482 " 'external-contents': '/b'\n"
3485 " 'type': 'file',\n"
3486 " 'name': 'missing',\n"
3487 " 'external-contents': '/missing'\n"
3490 " 'type': 'file',\n"
3491 " 'name': 'vfile',\n"
3492 " 'external-contents': '/c'\n"
3497 Dummy
->addDirectory("/a");
3498 Dummy
->addRegularFile("/a/foo");
3499 Dummy
->addDirectory("/b");
3500 Dummy
->addRegularFile("/c");
3501 Dummy
->addRegularFile("/both/foo");
3503 auto Redirecting
= vfs::RedirectingFileSystem::create(
3504 std::move(YAML
), nullptr, "", nullptr, Dummy
);
3506 EXPECT_TRUE(Redirecting
->exists("/dremap"));
3507 EXPECT_FALSE(Redirecting
->exists("/dmissing"));
3508 EXPECT_FALSE(Redirecting
->exists("/unknown"));
3509 EXPECT_TRUE(Redirecting
->exists("/both"));
3510 EXPECT_TRUE(Redirecting
->exists("/both/foo"));
3511 EXPECT_TRUE(Redirecting
->exists("/both/vfile"));
3512 EXPECT_TRUE(Redirecting
->exists("/vdir"));
3513 EXPECT_TRUE(Redirecting
->exists("/vdir/dremap"));
3514 EXPECT_FALSE(Redirecting
->exists("/vdir/missing"));
3515 EXPECT_TRUE(Redirecting
->exists("/vdir/vfile"));
3516 EXPECT_FALSE(Redirecting
->exists("/vdir/unknown"));
3519 TEST(RedirectingFileSystemTest
, ExistsFallback
) {
3520 IntrusiveRefCntPtr
<DummyFileSystem
> Dummy(new NoStatusDummyFileSystem());
3522 MemoryBuffer::getMemBuffer("{\n"
3524 " 'redirecting-with': 'fallback',\n"
3527 " 'type': 'file',\n"
3528 " 'name': '/fallback',\n"
3529 " 'external-contents': '/missing',\n"
3534 Dummy
->addRegularFile("/fallback");
3536 auto Redirecting
= vfs::RedirectingFileSystem::create(
3537 std::move(YAML
), nullptr, "", nullptr, Dummy
);
3539 EXPECT_TRUE(Redirecting
->exists("/fallback"));
3540 EXPECT_FALSE(Redirecting
->exists("/missing"));
3543 TEST(RedirectingFileSystemTest
, ExistsRedirectOnly
) {
3544 IntrusiveRefCntPtr
<DummyFileSystem
> Dummy(new NoStatusDummyFileSystem());
3546 MemoryBuffer::getMemBuffer("{\n"
3548 " 'redirecting-with': 'redirect-only',\n"
3551 " 'type': 'file',\n"
3552 " 'name': '/vfile',\n"
3553 " 'external-contents': '/a',\n"
3558 Dummy
->addRegularFile("/a");
3559 Dummy
->addRegularFile("/b");
3561 auto Redirecting
= vfs::RedirectingFileSystem::create(
3562 std::move(YAML
), nullptr, "", nullptr, Dummy
);
3564 EXPECT_FALSE(Redirecting
->exists("/a"));
3565 EXPECT_FALSE(Redirecting
->exists("/b"));
3566 EXPECT_TRUE(Redirecting
->exists("/vfile"));