1 //===- unittests/Support/VirtualFileSystem.cpp -------------- VFS tests ---===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 #include "llvm/Support/VirtualFileSystem.h"
10 #include "llvm/ADT/Triple.h"
11 #include "llvm/Config/llvm-config.h"
12 #include "llvm/Support/Errc.h"
13 #include "llvm/Support/Host.h"
14 #include "llvm/Support/MemoryBuffer.h"
15 #include "llvm/Support/Path.h"
16 #include "llvm/Support/SourceMgr.h"
17 #include "gmock/gmock.h"
18 #include "gtest/gtest.h"
23 using llvm::sys::fs::UniqueID
;
24 using testing::ElementsAre
;
26 using testing::UnorderedElementsAre
;
29 struct DummyFile
: public vfs::File
{
31 explicit DummyFile(vfs::Status S
) : S(S
) {}
32 llvm::ErrorOr
<vfs::Status
> status() override
{ return S
; }
33 llvm::ErrorOr
<std::unique_ptr
<llvm::MemoryBuffer
>>
34 getBuffer(const Twine
&Name
, int64_t FileSize
, bool RequiresNullTerminator
,
35 bool IsVolatile
) override
{
36 llvm_unreachable("unimplemented");
38 std::error_code
close() override
{ return std::error_code(); }
41 class DummyFileSystem
: public vfs::FileSystem
{
42 int FSID
; // used to produce UniqueIDs
43 int FileID
; // used to produce UniqueIDs
44 std::string WorkingDirectory
;
45 std::map
<std::string
, vfs::Status
> FilesAndDirs
;
46 typedef std::map
<std::string
, vfs::Status
>::const_iterator const_iterator
;
48 static int getNextFSID() {
54 DummyFileSystem() : FSID(getNextFSID()), FileID(0) {}
56 ErrorOr
<vfs::Status
> status(const Twine
&Path
) override
{
57 auto I
= findEntry(Path
);
58 if (I
== FilesAndDirs
.end())
59 return make_error_code(llvm::errc::no_such_file_or_directory
);
62 ErrorOr
<std::unique_ptr
<vfs::File
>>
63 openFileForRead(const Twine
&Path
) override
{
64 auto S
= status(Path
);
66 return std::unique_ptr
<vfs::File
>(new DummyFile
{*S
});
69 llvm::ErrorOr
<std::string
> getCurrentWorkingDirectory() const override
{
70 return WorkingDirectory
;
72 std::error_code
setCurrentWorkingDirectory(const Twine
&Path
) override
{
73 WorkingDirectory
= Path
.str();
74 return std::error_code();
76 // Map any symlink to "/symlink".
77 std::error_code
getRealPath(const Twine
&Path
,
78 SmallVectorImpl
<char> &Output
) const override
{
79 auto I
= findEntry(Path
);
80 if (I
== FilesAndDirs
.end())
81 return make_error_code(llvm::errc::no_such_file_or_directory
);
82 if (I
->second
.isSymlink()) {
84 Twine("/symlink").toVector(Output
);
85 return std::error_code();
88 Path
.toVector(Output
);
89 return std::error_code();
92 struct DirIterImpl
: public llvm::vfs::detail::DirIterImpl
{
93 std::map
<std::string
, vfs::Status
> &FilesAndDirs
;
94 std::map
<std::string
, vfs::Status
>::iterator I
;
96 bool isInPath(StringRef S
) {
97 if (Path
.size() < S
.size() && S
.find(Path
) == 0) {
98 auto LastSep
= S
.find_last_of('/');
99 if (LastSep
== Path
.size() || LastSep
== Path
.size() - 1)
104 DirIterImpl(std::map
<std::string
, vfs::Status
> &FilesAndDirs
,
106 : FilesAndDirs(FilesAndDirs
), I(FilesAndDirs
.begin()),
108 for (; I
!= FilesAndDirs
.end(); ++I
) {
109 if (isInPath(I
->first
)) {
111 vfs::directory_entry(I
->second
.getName(), I
->second
.getType());
116 std::error_code
increment() override
{
118 for (; I
!= FilesAndDirs
.end(); ++I
) {
119 if (isInPath(I
->first
)) {
121 vfs::directory_entry(I
->second
.getName(), I
->second
.getType());
125 if (I
== FilesAndDirs
.end())
126 CurrentEntry
= vfs::directory_entry();
127 return std::error_code();
131 vfs::directory_iterator
dir_begin(const Twine
&Dir
,
132 std::error_code
&EC
) override
{
133 return vfs::directory_iterator(
134 std::make_shared
<DirIterImpl
>(FilesAndDirs
, Dir
));
137 void addEntry(StringRef Path
, const vfs::Status
&Status
) {
138 FilesAndDirs
[Path
] = Status
;
141 const_iterator
findEntry(const Twine
&Path
) const {
144 std::error_code EC
= makeAbsolute(P
);
147 return FilesAndDirs
.find(P
.str());
150 void addRegularFile(StringRef Path
, sys::fs::perms Perms
= sys::fs::all_all
) {
151 vfs::Status
S(Path
, UniqueID(FSID
, FileID
++),
152 std::chrono::system_clock::now(), 0, 0, 1024,
153 sys::fs::file_type::regular_file
, Perms
);
157 void addDirectory(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, 0,
160 sys::fs::file_type::directory_file
, Perms
);
164 void addSymlink(StringRef Path
) {
165 vfs::Status
S(Path
, UniqueID(FSID
, FileID
++),
166 std::chrono::system_clock::now(), 0, 0, 0,
167 sys::fs::file_type::symlink_file
, sys::fs::all_all
);
172 class ErrorDummyFileSystem
: public DummyFileSystem
{
173 std::error_code
setCurrentWorkingDirectory(const Twine
&Path
) override
{
174 return llvm::errc::no_such_file_or_directory
;
178 /// Replace back-slashes by front-slashes.
179 std::string
getPosixPath(std::string S
) {
180 SmallString
<128> Result
;
181 llvm::sys::path::native(S
, Result
, llvm::sys::path::Style::posix
);
184 } // end anonymous namespace
186 TEST(VirtualFileSystemTest
, StatusQueries
) {
187 IntrusiveRefCntPtr
<DummyFileSystem
> D(new DummyFileSystem());
188 ErrorOr
<vfs::Status
> Status((std::error_code()));
190 D
->addRegularFile("/foo");
191 Status
= D
->status("/foo");
192 ASSERT_FALSE(Status
.getError());
193 EXPECT_TRUE(Status
->isStatusKnown());
194 EXPECT_FALSE(Status
->isDirectory());
195 EXPECT_TRUE(Status
->isRegularFile());
196 EXPECT_FALSE(Status
->isSymlink());
197 EXPECT_FALSE(Status
->isOther());
198 EXPECT_TRUE(Status
->exists());
200 D
->addDirectory("/bar");
201 Status
= D
->status("/bar");
202 ASSERT_FALSE(Status
.getError());
203 EXPECT_TRUE(Status
->isStatusKnown());
204 EXPECT_TRUE(Status
->isDirectory());
205 EXPECT_FALSE(Status
->isRegularFile());
206 EXPECT_FALSE(Status
->isSymlink());
207 EXPECT_FALSE(Status
->isOther());
208 EXPECT_TRUE(Status
->exists());
210 D
->addSymlink("/baz");
211 Status
= D
->status("/baz");
212 ASSERT_FALSE(Status
.getError());
213 EXPECT_TRUE(Status
->isStatusKnown());
214 EXPECT_FALSE(Status
->isDirectory());
215 EXPECT_FALSE(Status
->isRegularFile());
216 EXPECT_TRUE(Status
->isSymlink());
217 EXPECT_FALSE(Status
->isOther());
218 EXPECT_TRUE(Status
->exists());
220 EXPECT_TRUE(Status
->equivalent(*Status
));
221 ErrorOr
<vfs::Status
> Status2
= D
->status("/foo");
222 ASSERT_FALSE(Status2
.getError());
223 EXPECT_FALSE(Status
->equivalent(*Status2
));
226 TEST(VirtualFileSystemTest
, BaseOnlyOverlay
) {
227 IntrusiveRefCntPtr
<DummyFileSystem
> D(new DummyFileSystem());
228 ErrorOr
<vfs::Status
> Status((std::error_code()));
229 EXPECT_FALSE(Status
= D
->status("/foo"));
231 IntrusiveRefCntPtr
<vfs::OverlayFileSystem
> O(new vfs::OverlayFileSystem(D
));
232 EXPECT_FALSE(Status
= O
->status("/foo"));
234 D
->addRegularFile("/foo");
235 Status
= D
->status("/foo");
236 EXPECT_FALSE(Status
.getError());
238 ErrorOr
<vfs::Status
> Status2((std::error_code()));
239 Status2
= O
->status("/foo");
240 EXPECT_FALSE(Status2
.getError());
241 EXPECT_TRUE(Status
->equivalent(*Status2
));
244 TEST(VirtualFileSystemTest
, GetRealPathInOverlay
) {
245 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
246 Lower
->addRegularFile("/foo");
247 Lower
->addSymlink("/lower_link");
248 IntrusiveRefCntPtr
<DummyFileSystem
> Upper(new DummyFileSystem());
250 IntrusiveRefCntPtr
<vfs::OverlayFileSystem
> O(
251 new vfs::OverlayFileSystem(Lower
));
252 O
->pushOverlay(Upper
);
255 SmallString
<16> RealPath
;
256 EXPECT_FALSE(O
->getRealPath("/foo", RealPath
));
257 EXPECT_EQ(RealPath
.str(), "/foo");
259 // Expect no error getting real path for symlink in lower overlay.
260 EXPECT_FALSE(O
->getRealPath("/lower_link", RealPath
));
261 EXPECT_EQ(RealPath
.str(), "/symlink");
263 // Try a non-existing link.
264 EXPECT_EQ(O
->getRealPath("/upper_link", RealPath
),
265 errc::no_such_file_or_directory
);
267 // Add a new symlink in upper.
268 Upper
->addSymlink("/upper_link");
269 EXPECT_FALSE(O
->getRealPath("/upper_link", RealPath
));
270 EXPECT_EQ(RealPath
.str(), "/symlink");
273 TEST(VirtualFileSystemTest
, OverlayFiles
) {
274 IntrusiveRefCntPtr
<DummyFileSystem
> Base(new DummyFileSystem());
275 IntrusiveRefCntPtr
<DummyFileSystem
> Middle(new DummyFileSystem());
276 IntrusiveRefCntPtr
<DummyFileSystem
> Top(new DummyFileSystem());
277 IntrusiveRefCntPtr
<vfs::OverlayFileSystem
> O(
278 new vfs::OverlayFileSystem(Base
));
279 O
->pushOverlay(Middle
);
282 ErrorOr
<vfs::Status
> Status1((std::error_code())),
283 Status2((std::error_code())), Status3((std::error_code())),
284 StatusB((std::error_code())), StatusM((std::error_code())),
285 StatusT((std::error_code()));
287 Base
->addRegularFile("/foo");
288 StatusB
= Base
->status("/foo");
289 ASSERT_FALSE(StatusB
.getError());
290 Status1
= O
->status("/foo");
291 ASSERT_FALSE(Status1
.getError());
292 Middle
->addRegularFile("/foo");
293 StatusM
= Middle
->status("/foo");
294 ASSERT_FALSE(StatusM
.getError());
295 Status2
= O
->status("/foo");
296 ASSERT_FALSE(Status2
.getError());
297 Top
->addRegularFile("/foo");
298 StatusT
= Top
->status("/foo");
299 ASSERT_FALSE(StatusT
.getError());
300 Status3
= O
->status("/foo");
301 ASSERT_FALSE(Status3
.getError());
303 EXPECT_TRUE(Status1
->equivalent(*StatusB
));
304 EXPECT_TRUE(Status2
->equivalent(*StatusM
));
305 EXPECT_TRUE(Status3
->equivalent(*StatusT
));
307 EXPECT_FALSE(Status1
->equivalent(*Status2
));
308 EXPECT_FALSE(Status2
->equivalent(*Status3
));
309 EXPECT_FALSE(Status1
->equivalent(*Status3
));
312 TEST(VirtualFileSystemTest
, OverlayDirsNonMerged
) {
313 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
314 IntrusiveRefCntPtr
<DummyFileSystem
> Upper(new DummyFileSystem());
315 IntrusiveRefCntPtr
<vfs::OverlayFileSystem
> O(
316 new vfs::OverlayFileSystem(Lower
));
317 O
->pushOverlay(Upper
);
319 Lower
->addDirectory("/lower-only");
320 Upper
->addDirectory("/upper-only");
322 // non-merged paths should be the same
323 ErrorOr
<vfs::Status
> Status1
= Lower
->status("/lower-only");
324 ASSERT_FALSE(Status1
.getError());
325 ErrorOr
<vfs::Status
> Status2
= O
->status("/lower-only");
326 ASSERT_FALSE(Status2
.getError());
327 EXPECT_TRUE(Status1
->equivalent(*Status2
));
329 Status1
= Upper
->status("/upper-only");
330 ASSERT_FALSE(Status1
.getError());
331 Status2
= O
->status("/upper-only");
332 ASSERT_FALSE(Status2
.getError());
333 EXPECT_TRUE(Status1
->equivalent(*Status2
));
336 TEST(VirtualFileSystemTest
, MergedDirPermissions
) {
337 // merged directories get the permissions of the upper dir
338 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
339 IntrusiveRefCntPtr
<DummyFileSystem
> Upper(new DummyFileSystem());
340 IntrusiveRefCntPtr
<vfs::OverlayFileSystem
> O(
341 new vfs::OverlayFileSystem(Lower
));
342 O
->pushOverlay(Upper
);
344 ErrorOr
<vfs::Status
> Status((std::error_code()));
345 Lower
->addDirectory("/both", sys::fs::owner_read
);
346 Upper
->addDirectory("/both", sys::fs::owner_all
| sys::fs::group_read
);
347 Status
= O
->status("/both");
348 ASSERT_FALSE(Status
.getError());
349 EXPECT_EQ(0740, Status
->getPermissions());
351 // permissions (as usual) are not recursively applied
352 Lower
->addRegularFile("/both/foo", sys::fs::owner_read
);
353 Upper
->addRegularFile("/both/bar", sys::fs::owner_write
);
354 Status
= O
->status("/both/foo");
355 ASSERT_FALSE(Status
.getError());
356 EXPECT_EQ(0400, Status
->getPermissions());
357 Status
= O
->status("/both/bar");
358 ASSERT_FALSE(Status
.getError());
359 EXPECT_EQ(0200, Status
->getPermissions());
362 TEST(VirtualFileSystemTest
, OverlayIterator
) {
363 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
364 Lower
->addRegularFile("/foo");
365 IntrusiveRefCntPtr
<DummyFileSystem
> Upper(new DummyFileSystem());
367 IntrusiveRefCntPtr
<vfs::OverlayFileSystem
> O(
368 new vfs::OverlayFileSystem(Lower
));
369 O
->pushOverlay(Upper
);
371 ErrorOr
<vfs::Status
> Status((std::error_code()));
373 auto it
= O
->overlays_begin();
374 auto end
= O
->overlays_end();
378 Status
= (*it
)->status("/foo");
379 ASSERT_TRUE(Status
.getError());
384 Status
= (*it
)->status("/foo");
385 ASSERT_FALSE(Status
.getError());
386 EXPECT_TRUE(Status
->exists());
393 auto it
= O
->overlays_rbegin();
394 auto end
= O
->overlays_rend();
398 Status
= (*it
)->status("/foo");
399 ASSERT_FALSE(Status
.getError());
400 EXPECT_TRUE(Status
->exists());
405 Status
= (*it
)->status("/foo");
406 ASSERT_TRUE(Status
.getError());
415 SmallString
<128> Path
;
416 ScopedDir(const Twine
&Name
, bool Unique
= false) {
419 EC
= llvm::sys::fs::createUniqueDirectory(Name
, Path
);
421 // Resolve any symlinks in the new directory.
422 std::string UnresolvedPath
= Path
.str();
423 EC
= llvm::sys::fs::real_path(UnresolvedPath
, Path
);
427 EC
= llvm::sys::fs::create_directory(Twine(Path
));
431 EXPECT_FALSE(EC
) << EC
.message();
435 EXPECT_FALSE(llvm::sys::fs::remove(Path
.str()));
438 operator StringRef() { return Path
.str(); }
442 SmallString
<128> Path
;
443 ScopedLink(const Twine
&To
, const Twine
&From
) {
445 std::error_code EC
= sys::fs::create_link(To
, From
);
452 EXPECT_FALSE(llvm::sys::fs::remove(Path
.str()));
455 operator StringRef() { return Path
.str(); }
459 SmallString
<128> Path
;
460 ScopedFile(const Twine
&Path
, StringRef Contents
) {
461 Path
.toVector(this->Path
);
463 raw_fd_ostream
OS(this->Path
, EC
);
467 EXPECT_FALSE(OS
.error());
468 if (EC
|| OS
.error())
473 EXPECT_FALSE(llvm::sys::fs::remove(Path
.str()));
477 } // end anonymous namespace
479 TEST(VirtualFileSystemTest
, BasicRealFSIteration
) {
480 ScopedDir
TestDirectory("virtual-file-system-test", /*Unique*/ true);
481 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= vfs::getRealFileSystem();
484 vfs::directory_iterator I
= FS
->dir_begin(Twine(TestDirectory
), EC
);
486 EXPECT_EQ(vfs::directory_iterator(), I
); // empty directory is empty
488 ScopedDir
_a(TestDirectory
+ "/a");
489 ScopedDir
_ab(TestDirectory
+ "/a/b");
490 ScopedDir
_c(TestDirectory
+ "/c");
491 ScopedDir
_cd(TestDirectory
+ "/c/d");
493 I
= FS
->dir_begin(Twine(TestDirectory
), EC
);
495 ASSERT_NE(vfs::directory_iterator(), I
);
496 // Check either a or c, since we can't rely on the iteration order.
497 EXPECT_TRUE(I
->path().endswith("a") || I
->path().endswith("c"));
500 ASSERT_NE(vfs::directory_iterator(), I
);
501 EXPECT_TRUE(I
->path().endswith("a") || I
->path().endswith("c"));
503 EXPECT_EQ(vfs::directory_iterator(), I
);
507 TEST(VirtualFileSystemTest
, MultipleWorkingDirs
) {
508 // Our root contains a/aa, b/bb, c, where c is a link to a/.
509 // Run tests both in root/b/ and root/c/ (to test "normal" and symlink dirs).
510 // Interleave operations to show the working directories are independent.
511 ScopedDir
Root("r", true), ADir(Root
.Path
+ "/a"), BDir(Root
.Path
+ "/b");
512 ScopedLink
C(ADir
.Path
, Root
.Path
+ "/c");
513 ScopedFile
AA(ADir
.Path
+ "/aa", "aaaa"), BB(BDir
.Path
+ "/bb", "bbbb");
514 std::unique_ptr
<vfs::FileSystem
> BFS
= vfs::createPhysicalFileSystem(),
515 CFS
= vfs::createPhysicalFileSystem();
517 ASSERT_FALSE(BFS
->setCurrentWorkingDirectory(BDir
.Path
));
518 ASSERT_FALSE(CFS
->setCurrentWorkingDirectory(C
.Path
));
519 EXPECT_EQ(BDir
.Path
, *BFS
->getCurrentWorkingDirectory());
520 EXPECT_EQ(C
.Path
, *CFS
->getCurrentWorkingDirectory());
522 // openFileForRead(), indirectly.
523 auto BBuf
= BFS
->getBufferForFile("bb");
525 EXPECT_EQ("bbbb", (*BBuf
)->getBuffer());
527 auto ABuf
= CFS
->getBufferForFile("aa");
529 EXPECT_EQ("aaaa", (*ABuf
)->getBuffer());
532 auto BStat
= BFS
->status("bb");
534 EXPECT_EQ("bb", BStat
->getName());
536 auto AStat
= CFS
->status("aa");
538 EXPECT_EQ("aa", AStat
->getName()); // unresolved name
541 SmallString
<128> BPath
;
542 ASSERT_FALSE(BFS
->getRealPath("bb", BPath
));
543 EXPECT_EQ(BB
.Path
, BPath
);
545 SmallString
<128> APath
;
546 ASSERT_FALSE(CFS
->getRealPath("aa", APath
));
547 EXPECT_EQ(AA
.Path
, APath
); // Reports resolved name.
551 auto BIt
= BFS
->dir_begin(".", EC
);
553 ASSERT_NE(BIt
, vfs::directory_iterator());
554 EXPECT_EQ((BDir
.Path
+ "/./bb").str(), BIt
->path());
557 ASSERT_EQ(BIt
, vfs::directory_iterator());
559 auto CIt
= CFS
->dir_begin(".", EC
);
561 ASSERT_NE(CIt
, vfs::directory_iterator());
562 EXPECT_EQ((ADir
.Path
+ "/./aa").str(), CIt
->path()); // Partly resolved name!
563 CIt
.increment(EC
); // Because likely to read through this path.
565 ASSERT_EQ(CIt
, vfs::directory_iterator());
568 TEST(VirtualFileSystemTest
, BrokenSymlinkRealFSIteration
) {
569 ScopedDir
TestDirectory("virtual-file-system-test", /*Unique*/ true);
570 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= vfs::getRealFileSystem();
572 ScopedLink
_a("no_such_file", TestDirectory
+ "/a");
573 ScopedDir
_b(TestDirectory
+ "/b");
574 ScopedLink
_c("no_such_file", TestDirectory
+ "/c");
576 // Should get no iteration error, but a stat error for the broken symlinks.
577 std::map
<std::string
, std::error_code
> StatResults
;
579 for (vfs::directory_iterator I
= FS
->dir_begin(Twine(TestDirectory
), EC
), E
;
580 I
!= E
; I
.increment(EC
)) {
582 StatResults
[sys::path::filename(I
->path())] =
583 FS
->status(I
->path()).getError();
588 Pair("a", std::make_error_code(std::errc::no_such_file_or_directory
)),
589 Pair("b", std::error_code()),
591 std::make_error_code(std::errc::no_such_file_or_directory
))));
595 TEST(VirtualFileSystemTest
, BasicRealFSRecursiveIteration
) {
596 ScopedDir
TestDirectory("virtual-file-system-test", /*Unique*/ true);
597 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= vfs::getRealFileSystem();
600 auto I
= vfs::recursive_directory_iterator(*FS
, Twine(TestDirectory
), EC
);
602 EXPECT_EQ(vfs::recursive_directory_iterator(), I
); // empty directory is empty
604 ScopedDir
_a(TestDirectory
+ "/a");
605 ScopedDir
_ab(TestDirectory
+ "/a/b");
606 ScopedDir
_c(TestDirectory
+ "/c");
607 ScopedDir
_cd(TestDirectory
+ "/c/d");
609 I
= vfs::recursive_directory_iterator(*FS
, Twine(TestDirectory
), EC
);
611 ASSERT_NE(vfs::recursive_directory_iterator(), I
);
613 std::vector
<std::string
> Contents
;
614 for (auto E
= vfs::recursive_directory_iterator(); !EC
&& I
!= E
;
616 Contents
.push_back(I
->path());
619 // Check contents, which may be in any order
620 EXPECT_EQ(4U, Contents
.size());
621 int Counts
[4] = {0, 0, 0, 0};
622 for (const std::string
&Name
: Contents
) {
623 ASSERT_FALSE(Name
.empty());
624 int Index
= Name
[Name
.size() - 1] - 'a';
625 ASSERT_TRUE(Index
>= 0 && Index
< 4);
628 EXPECT_EQ(1, Counts
[0]); // a
629 EXPECT_EQ(1, Counts
[1]); // b
630 EXPECT_EQ(1, Counts
[2]); // c
631 EXPECT_EQ(1, Counts
[3]); // d
634 TEST(VirtualFileSystemTest
, BasicRealFSRecursiveIterationNoPush
) {
635 ScopedDir
TestDirectory("virtual-file-system-test", /*Unique*/ true);
637 ScopedDir
_a(TestDirectory
+ "/a");
638 ScopedDir
_ab(TestDirectory
+ "/a/b");
639 ScopedDir
_c(TestDirectory
+ "/c");
640 ScopedDir
_cd(TestDirectory
+ "/c/d");
641 ScopedDir
_e(TestDirectory
+ "/e");
642 ScopedDir
_ef(TestDirectory
+ "/e/f");
643 ScopedDir
_g(TestDirectory
+ "/g");
645 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= vfs::getRealFileSystem();
647 // Test that calling no_push on entries without subdirectories has no effect.
650 auto I
= vfs::recursive_directory_iterator(*FS
, Twine(TestDirectory
), EC
);
653 std::vector
<std::string
> Contents
;
654 for (auto E
= vfs::recursive_directory_iterator(); !EC
&& I
!= E
;
656 Contents
.push_back(I
->path());
657 char last
= I
->path().back();
669 EXPECT_EQ(7U, Contents
.size());
672 // Test that calling no_push skips subdirectories.
675 auto I
= vfs::recursive_directory_iterator(*FS
, Twine(TestDirectory
), EC
);
678 std::vector
<std::string
> Contents
;
679 for (auto E
= vfs::recursive_directory_iterator(); !EC
&& I
!= E
;
681 Contents
.push_back(I
->path());
682 char last
= I
->path().back();
694 // Check contents, which may be in any order
695 EXPECT_EQ(4U, Contents
.size());
696 int Counts
[7] = {0, 0, 0, 0, 0, 0, 0};
697 for (const std::string
&Name
: Contents
) {
698 ASSERT_FALSE(Name
.empty());
699 int Index
= Name
[Name
.size() - 1] - 'a';
700 ASSERT_TRUE(Index
>= 0 && Index
< 7);
703 EXPECT_EQ(1, Counts
[0]); // a
704 EXPECT_EQ(0, Counts
[1]); // b
705 EXPECT_EQ(1, Counts
[2]); // c
706 EXPECT_EQ(0, Counts
[3]); // d
707 EXPECT_EQ(1, Counts
[4]); // e
708 EXPECT_EQ(0, Counts
[5]); // f
709 EXPECT_EQ(1, Counts
[6]); // g
714 TEST(VirtualFileSystemTest
, BrokenSymlinkRealFSRecursiveIteration
) {
715 ScopedDir
TestDirectory("virtual-file-system-test", /*Unique*/ true);
716 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= vfs::getRealFileSystem();
718 ScopedLink
_a("no_such_file", TestDirectory
+ "/a");
719 ScopedDir
_b(TestDirectory
+ "/b");
720 ScopedLink
_ba("no_such_file", TestDirectory
+ "/b/a");
721 ScopedDir
_bb(TestDirectory
+ "/b/b");
722 ScopedLink
_bc("no_such_file", TestDirectory
+ "/b/c");
723 ScopedLink
_c("no_such_file", TestDirectory
+ "/c");
724 ScopedDir
_d(TestDirectory
+ "/d");
725 ScopedDir
_dd(TestDirectory
+ "/d/d");
726 ScopedDir
_ddd(TestDirectory
+ "/d/d/d");
727 ScopedLink
_e("no_such_file", TestDirectory
+ "/e");
729 std::vector
<std::string
> VisitedBrokenSymlinks
;
730 std::vector
<std::string
> VisitedNonBrokenSymlinks
;
732 for (vfs::recursive_directory_iterator
I(*FS
, Twine(TestDirectory
), EC
), E
;
733 I
!= E
; I
.increment(EC
)) {
735 (FS
->status(I
->path()) ? VisitedNonBrokenSymlinks
: VisitedBrokenSymlinks
)
736 .push_back(I
->path());
739 // Check visited file names.
740 EXPECT_THAT(VisitedBrokenSymlinks
,
741 UnorderedElementsAre(StringRef(_a
), StringRef(_ba
),
742 StringRef(_bc
), StringRef(_c
),
744 EXPECT_THAT(VisitedNonBrokenSymlinks
,
745 UnorderedElementsAre(StringRef(_b
), StringRef(_bb
), StringRef(_d
),
746 StringRef(_dd
), StringRef(_ddd
)));
750 template <typename DirIter
>
751 static void checkContents(DirIter I
, ArrayRef
<StringRef
> ExpectedOut
) {
753 SmallVector
<StringRef
, 4> Expected(ExpectedOut
.begin(), ExpectedOut
.end());
754 SmallVector
<std::string
, 4> InputToCheck
;
756 // Do not rely on iteration order to check for contents, sort both
757 // content vectors before comparison.
758 for (DirIter E
; !EC
&& I
!= E
; I
.increment(EC
))
759 InputToCheck
.push_back(I
->path());
761 llvm::sort(InputToCheck
);
762 llvm::sort(Expected
);
763 EXPECT_EQ(InputToCheck
.size(), Expected
.size());
765 unsigned LastElt
= std::min(InputToCheck
.size(), Expected
.size());
766 for (unsigned Idx
= 0; Idx
!= LastElt
; ++Idx
)
767 EXPECT_EQ(StringRef(InputToCheck
[Idx
]), Expected
[Idx
]);
770 TEST(VirtualFileSystemTest
, OverlayIteration
) {
771 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
772 IntrusiveRefCntPtr
<DummyFileSystem
> Upper(new DummyFileSystem());
773 IntrusiveRefCntPtr
<vfs::OverlayFileSystem
> O(
774 new vfs::OverlayFileSystem(Lower
));
775 O
->pushOverlay(Upper
);
778 checkContents(O
->dir_begin("/", EC
), ArrayRef
<StringRef
>());
780 Lower
->addRegularFile("/file1");
781 checkContents(O
->dir_begin("/", EC
), ArrayRef
<StringRef
>("/file1"));
783 Upper
->addRegularFile("/file2");
784 checkContents(O
->dir_begin("/", EC
), {"/file2", "/file1"});
786 Lower
->addDirectory("/dir1");
787 Lower
->addRegularFile("/dir1/foo");
788 Upper
->addDirectory("/dir2");
789 Upper
->addRegularFile("/dir2/foo");
790 checkContents(O
->dir_begin("/dir2", EC
), ArrayRef
<StringRef
>("/dir2/foo"));
791 checkContents(O
->dir_begin("/", EC
), {"/dir2", "/file2", "/dir1", "/file1"});
794 TEST(VirtualFileSystemTest
, OverlayRecursiveIteration
) {
795 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
796 IntrusiveRefCntPtr
<DummyFileSystem
> Middle(new DummyFileSystem());
797 IntrusiveRefCntPtr
<DummyFileSystem
> Upper(new DummyFileSystem());
798 IntrusiveRefCntPtr
<vfs::OverlayFileSystem
> O(
799 new vfs::OverlayFileSystem(Lower
));
800 O
->pushOverlay(Middle
);
801 O
->pushOverlay(Upper
);
804 checkContents(vfs::recursive_directory_iterator(*O
, "/", EC
),
805 ArrayRef
<StringRef
>());
807 Lower
->addRegularFile("/file1");
808 checkContents(vfs::recursive_directory_iterator(*O
, "/", EC
),
809 ArrayRef
<StringRef
>("/file1"));
811 Upper
->addDirectory("/dir");
812 Upper
->addRegularFile("/dir/file2");
813 checkContents(vfs::recursive_directory_iterator(*O
, "/", EC
),
814 {"/dir", "/dir/file2", "/file1"});
816 Lower
->addDirectory("/dir1");
817 Lower
->addRegularFile("/dir1/foo");
818 Lower
->addDirectory("/dir1/a");
819 Lower
->addRegularFile("/dir1/a/b");
820 Middle
->addDirectory("/a");
821 Middle
->addDirectory("/a/b");
822 Middle
->addDirectory("/a/b/c");
823 Middle
->addRegularFile("/a/b/c/d");
824 Middle
->addRegularFile("/hiddenByUp");
825 Upper
->addDirectory("/dir2");
826 Upper
->addRegularFile("/dir2/foo");
827 Upper
->addRegularFile("/hiddenByUp");
828 checkContents(vfs::recursive_directory_iterator(*O
, "/dir2", EC
),
829 ArrayRef
<StringRef
>("/dir2/foo"));
830 checkContents(vfs::recursive_directory_iterator(*O
, "/", EC
),
831 {"/dir", "/dir/file2", "/dir2", "/dir2/foo", "/hiddenByUp",
832 "/a", "/a/b", "/a/b/c", "/a/b/c/d", "/dir1", "/dir1/a",
833 "/dir1/a/b", "/dir1/foo", "/file1"});
836 TEST(VirtualFileSystemTest
, ThreeLevelIteration
) {
837 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
838 IntrusiveRefCntPtr
<DummyFileSystem
> Middle(new DummyFileSystem());
839 IntrusiveRefCntPtr
<DummyFileSystem
> Upper(new DummyFileSystem());
840 IntrusiveRefCntPtr
<vfs::OverlayFileSystem
> O(
841 new vfs::OverlayFileSystem(Lower
));
842 O
->pushOverlay(Middle
);
843 O
->pushOverlay(Upper
);
846 checkContents(O
->dir_begin("/", EC
), ArrayRef
<StringRef
>());
848 Middle
->addRegularFile("/file2");
849 checkContents(O
->dir_begin("/", EC
), ArrayRef
<StringRef
>("/file2"));
851 Lower
->addRegularFile("/file1");
852 Upper
->addRegularFile("/file3");
853 checkContents(O
->dir_begin("/", EC
), {"/file3", "/file2", "/file1"});
856 TEST(VirtualFileSystemTest
, HiddenInIteration
) {
857 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
858 IntrusiveRefCntPtr
<DummyFileSystem
> Middle(new DummyFileSystem());
859 IntrusiveRefCntPtr
<DummyFileSystem
> Upper(new DummyFileSystem());
860 IntrusiveRefCntPtr
<vfs::OverlayFileSystem
> O(
861 new vfs::OverlayFileSystem(Lower
));
862 O
->pushOverlay(Middle
);
863 O
->pushOverlay(Upper
);
866 Lower
->addRegularFile("/onlyInLow");
867 Lower
->addDirectory("/hiddenByMid");
868 Lower
->addDirectory("/hiddenByUp");
869 Middle
->addRegularFile("/onlyInMid");
870 Middle
->addRegularFile("/hiddenByMid");
871 Middle
->addDirectory("/hiddenByUp");
872 Upper
->addRegularFile("/onlyInUp");
873 Upper
->addRegularFile("/hiddenByUp");
875 O
->dir_begin("/", EC
),
876 {"/hiddenByUp", "/onlyInUp", "/hiddenByMid", "/onlyInMid", "/onlyInLow"});
878 // Make sure we get the top-most entry
881 vfs::directory_iterator I
= O
->dir_begin("/", EC
), E
;
882 for (; !EC
&& I
!= E
; I
.increment(EC
))
883 if (I
->path() == "/hiddenByUp")
886 EXPECT_EQ(sys::fs::file_type::regular_file
, I
->type());
890 vfs::directory_iterator I
= O
->dir_begin("/", EC
), E
;
891 for (; !EC
&& I
!= E
; I
.increment(EC
))
892 if (I
->path() == "/hiddenByMid")
895 EXPECT_EQ(sys::fs::file_type::regular_file
, I
->type());
899 TEST(ProxyFileSystemTest
, Basic
) {
900 IntrusiveRefCntPtr
<vfs::InMemoryFileSystem
> Base(
901 new vfs::InMemoryFileSystem());
902 vfs::ProxyFileSystem
PFS(Base
);
904 Base
->addFile("/a", 0, MemoryBuffer::getMemBuffer("test"));
906 auto Stat
= PFS
.status("/a");
907 ASSERT_FALSE(Stat
.getError());
909 auto File
= PFS
.openFileForRead("/a");
910 ASSERT_FALSE(File
.getError());
911 EXPECT_EQ("test", (*(*File
)->getBuffer("ignored"))->getBuffer());
914 vfs::directory_iterator I
= PFS
.dir_begin("/", EC
);
916 ASSERT_EQ("/a", I
->path());
919 ASSERT_EQ(vfs::directory_iterator(), I
);
921 ASSERT_FALSE(PFS
.setCurrentWorkingDirectory("/"));
923 auto PWD
= PFS
.getCurrentWorkingDirectory();
924 ASSERT_FALSE(PWD
.getError());
925 ASSERT_EQ("/", *PWD
);
927 SmallString
<16> Path
;
928 ASSERT_FALSE(PFS
.getRealPath("a", Path
));
929 ASSERT_EQ("/a", Path
);
932 ASSERT_FALSE(PFS
.isLocal("/a", Local
));
936 class InMemoryFileSystemTest
: public ::testing::Test
{
938 llvm::vfs::InMemoryFileSystem FS
;
939 llvm::vfs::InMemoryFileSystem NormalizedFS
;
941 InMemoryFileSystemTest()
942 : FS(/*UseNormalizedPaths=*/false),
943 NormalizedFS(/*UseNormalizedPaths=*/true) {}
946 MATCHER_P2(IsHardLinkTo
, FS
, Target
, "") {
947 StringRef From
= arg
;
948 StringRef To
= Target
;
949 auto OpenedFrom
= FS
->openFileForRead(From
);
950 auto OpenedTo
= FS
->openFileForRead(To
);
951 return !OpenedFrom
.getError() && !OpenedTo
.getError() &&
952 (*OpenedFrom
)->status()->getUniqueID() ==
953 (*OpenedTo
)->status()->getUniqueID();
956 TEST_F(InMemoryFileSystemTest
, IsEmpty
) {
957 auto Stat
= FS
.status("/a");
958 ASSERT_EQ(Stat
.getError(), errc::no_such_file_or_directory
) << FS
.toString();
959 Stat
= FS
.status("/");
960 ASSERT_EQ(Stat
.getError(), errc::no_such_file_or_directory
) << FS
.toString();
963 TEST_F(InMemoryFileSystemTest
, WindowsPath
) {
964 FS
.addFile("c:/windows/system128/foo.cpp", 0, MemoryBuffer::getMemBuffer(""));
965 auto Stat
= FS
.status("c:");
967 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << FS
.toString();
969 Stat
= FS
.status("c:/windows/system128/foo.cpp");
970 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << FS
.toString();
971 FS
.addFile("d:/windows/foo.cpp", 0, MemoryBuffer::getMemBuffer(""));
972 Stat
= FS
.status("d:/windows/foo.cpp");
973 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << FS
.toString();
976 TEST_F(InMemoryFileSystemTest
, OverlayFile
) {
977 FS
.addFile("/a", 0, MemoryBuffer::getMemBuffer("a"));
978 NormalizedFS
.addFile("/a", 0, MemoryBuffer::getMemBuffer("a"));
979 auto Stat
= FS
.status("/");
980 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << FS
.toString();
981 Stat
= FS
.status("/.");
983 Stat
= NormalizedFS
.status("/.");
984 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << FS
.toString();
985 Stat
= FS
.status("/a");
986 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n" << FS
.toString();
987 ASSERT_EQ("/a", Stat
->getName());
990 TEST_F(InMemoryFileSystemTest
, OverlayFileNoOwn
) {
991 auto Buf
= MemoryBuffer::getMemBuffer("a");
992 FS
.addFileNoOwn("/a", 0, Buf
.get());
993 auto Stat
= FS
.status("/a");
994 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n" << FS
.toString();
995 ASSERT_EQ("/a", Stat
->getName());
998 TEST_F(InMemoryFileSystemTest
, OpenFileForRead
) {
999 FS
.addFile("/a", 0, MemoryBuffer::getMemBuffer("a"));
1000 FS
.addFile("././c", 0, MemoryBuffer::getMemBuffer("c"));
1001 FS
.addFile("./d/../d", 0, MemoryBuffer::getMemBuffer("d"));
1002 NormalizedFS
.addFile("/a", 0, MemoryBuffer::getMemBuffer("a"));
1003 NormalizedFS
.addFile("././c", 0, MemoryBuffer::getMemBuffer("c"));
1004 NormalizedFS
.addFile("./d/../d", 0, MemoryBuffer::getMemBuffer("d"));
1005 auto File
= FS
.openFileForRead("/a");
1006 ASSERT_EQ("a", (*(*File
)->getBuffer("ignored"))->getBuffer());
1007 File
= FS
.openFileForRead("/a"); // Open again.
1008 ASSERT_EQ("a", (*(*File
)->getBuffer("ignored"))->getBuffer());
1009 File
= NormalizedFS
.openFileForRead("/././a"); // Open again.
1010 ASSERT_EQ("a", (*(*File
)->getBuffer("ignored"))->getBuffer());
1011 File
= FS
.openFileForRead("/");
1012 ASSERT_EQ(File
.getError(), errc::invalid_argument
) << FS
.toString();
1013 File
= FS
.openFileForRead("/b");
1014 ASSERT_EQ(File
.getError(), errc::no_such_file_or_directory
) << FS
.toString();
1015 File
= FS
.openFileForRead("./c");
1017 File
= FS
.openFileForRead("e/../d");
1019 File
= NormalizedFS
.openFileForRead("./c");
1020 ASSERT_EQ("c", (*(*File
)->getBuffer("ignored"))->getBuffer());
1021 File
= NormalizedFS
.openFileForRead("e/../d");
1022 ASSERT_EQ("d", (*(*File
)->getBuffer("ignored"))->getBuffer());
1025 TEST_F(InMemoryFileSystemTest
, DuplicatedFile
) {
1026 ASSERT_TRUE(FS
.addFile("/a", 0, MemoryBuffer::getMemBuffer("a")));
1027 ASSERT_FALSE(FS
.addFile("/a/b", 0, MemoryBuffer::getMemBuffer("a")));
1028 ASSERT_TRUE(FS
.addFile("/a", 0, MemoryBuffer::getMemBuffer("a")));
1029 ASSERT_FALSE(FS
.addFile("/a", 0, MemoryBuffer::getMemBuffer("b")));
1032 TEST_F(InMemoryFileSystemTest
, DirectoryIteration
) {
1033 FS
.addFile("/a", 0, MemoryBuffer::getMemBuffer(""));
1034 FS
.addFile("/b/c", 0, MemoryBuffer::getMemBuffer(""));
1037 vfs::directory_iterator I
= FS
.dir_begin("/", EC
);
1039 ASSERT_EQ("/a", I
->path());
1042 ASSERT_EQ("/b", I
->path());
1045 ASSERT_EQ(vfs::directory_iterator(), I
);
1047 I
= FS
.dir_begin("/b", EC
);
1049 // When on Windows, we end up with "/b\\c" as the name. Convert to Posix
1050 // path for the sake of the comparison.
1051 ASSERT_EQ("/b/c", getPosixPath(I
->path()));
1054 ASSERT_EQ(vfs::directory_iterator(), I
);
1057 TEST_F(InMemoryFileSystemTest
, WorkingDirectory
) {
1058 FS
.setCurrentWorkingDirectory("/b");
1059 FS
.addFile("c", 0, MemoryBuffer::getMemBuffer(""));
1061 auto Stat
= FS
.status("/b/c");
1062 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n" << FS
.toString();
1063 ASSERT_EQ("/b/c", Stat
->getName());
1064 ASSERT_EQ("/b", *FS
.getCurrentWorkingDirectory());
1066 Stat
= FS
.status("c");
1067 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n" << FS
.toString();
1069 NormalizedFS
.setCurrentWorkingDirectory("/b/c");
1070 NormalizedFS
.setCurrentWorkingDirectory(".");
1072 getPosixPath(NormalizedFS
.getCurrentWorkingDirectory().get()));
1073 NormalizedFS
.setCurrentWorkingDirectory("..");
1075 getPosixPath(NormalizedFS
.getCurrentWorkingDirectory().get()));
1078 TEST_F(InMemoryFileSystemTest
, IsLocal
) {
1079 FS
.setCurrentWorkingDirectory("/b");
1080 FS
.addFile("c", 0, MemoryBuffer::getMemBuffer(""));
1083 bool IsLocal
= true;
1084 EC
= FS
.isLocal("c", IsLocal
);
1086 ASSERT_FALSE(IsLocal
);
1089 #if !defined(_WIN32)
1090 TEST_F(InMemoryFileSystemTest
, GetRealPath
) {
1091 SmallString
<16> Path
;
1092 EXPECT_EQ(FS
.getRealPath("b", Path
), errc::operation_not_permitted
);
1094 auto GetRealPath
= [this](StringRef P
) {
1095 SmallString
<16> Output
;
1096 auto EC
= FS
.getRealPath(P
, Output
);
1098 return Output
.str().str();
1101 FS
.setCurrentWorkingDirectory("a");
1102 EXPECT_EQ(GetRealPath("b"), "a/b");
1103 EXPECT_EQ(GetRealPath("../b"), "b");
1104 EXPECT_EQ(GetRealPath("b/./c"), "a/b/c");
1106 FS
.setCurrentWorkingDirectory("/a");
1107 EXPECT_EQ(GetRealPath("b"), "/a/b");
1108 EXPECT_EQ(GetRealPath("../b"), "/b");
1109 EXPECT_EQ(GetRealPath("b/./c"), "/a/b/c");
1113 TEST_F(InMemoryFileSystemTest
, AddFileWithUser
) {
1114 FS
.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"), 0xFEEDFACE);
1115 auto Stat
= FS
.status("/a");
1116 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n" << FS
.toString();
1117 ASSERT_TRUE(Stat
->isDirectory());
1118 ASSERT_EQ(0xFEEDFACE, Stat
->getUser());
1119 Stat
= FS
.status("/a/b");
1120 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n" << FS
.toString();
1121 ASSERT_TRUE(Stat
->isDirectory());
1122 ASSERT_EQ(0xFEEDFACE, Stat
->getUser());
1123 Stat
= FS
.status("/a/b/c");
1124 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n" << FS
.toString();
1125 ASSERT_TRUE(Stat
->isRegularFile());
1126 ASSERT_EQ(sys::fs::perms::all_all
, Stat
->getPermissions());
1127 ASSERT_EQ(0xFEEDFACE, Stat
->getUser());
1130 TEST_F(InMemoryFileSystemTest
, AddFileWithGroup
) {
1131 FS
.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"), None
, 0xDABBAD00);
1132 auto Stat
= FS
.status("/a");
1133 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n" << FS
.toString();
1134 ASSERT_TRUE(Stat
->isDirectory());
1135 ASSERT_EQ(0xDABBAD00, Stat
->getGroup());
1136 Stat
= FS
.status("/a/b");
1137 ASSERT_TRUE(Stat
->isDirectory());
1138 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n" << FS
.toString();
1139 ASSERT_EQ(0xDABBAD00, Stat
->getGroup());
1140 Stat
= FS
.status("/a/b/c");
1141 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n" << FS
.toString();
1142 ASSERT_TRUE(Stat
->isRegularFile());
1143 ASSERT_EQ(sys::fs::perms::all_all
, Stat
->getPermissions());
1144 ASSERT_EQ(0xDABBAD00, Stat
->getGroup());
1147 TEST_F(InMemoryFileSystemTest
, AddFileWithFileType
) {
1148 FS
.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"), None
, None
,
1149 sys::fs::file_type::socket_file
);
1150 auto Stat
= FS
.status("/a");
1151 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n" << FS
.toString();
1152 ASSERT_TRUE(Stat
->isDirectory());
1153 Stat
= FS
.status("/a/b");
1154 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n" << FS
.toString();
1155 ASSERT_TRUE(Stat
->isDirectory());
1156 Stat
= FS
.status("/a/b/c");
1157 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n" << FS
.toString();
1158 ASSERT_EQ(sys::fs::file_type::socket_file
, Stat
->getType());
1159 ASSERT_EQ(sys::fs::perms::all_all
, Stat
->getPermissions());
1162 TEST_F(InMemoryFileSystemTest
, AddFileWithPerms
) {
1163 FS
.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"), None
, None
, None
,
1164 sys::fs::perms::owner_read
| sys::fs::perms::owner_write
);
1165 auto Stat
= FS
.status("/a");
1166 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n" << FS
.toString();
1167 ASSERT_TRUE(Stat
->isDirectory());
1168 ASSERT_EQ(sys::fs::perms::owner_read
| sys::fs::perms::owner_write
|
1169 sys::fs::perms::owner_exe
,
1170 Stat
->getPermissions());
1171 Stat
= FS
.status("/a/b");
1172 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n" << FS
.toString();
1173 ASSERT_TRUE(Stat
->isDirectory());
1174 ASSERT_EQ(sys::fs::perms::owner_read
| sys::fs::perms::owner_write
|
1175 sys::fs::perms::owner_exe
,
1176 Stat
->getPermissions());
1177 Stat
= FS
.status("/a/b/c");
1178 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n" << FS
.toString();
1179 ASSERT_TRUE(Stat
->isRegularFile());
1180 ASSERT_EQ(sys::fs::perms::owner_read
| sys::fs::perms::owner_write
,
1181 Stat
->getPermissions());
1184 TEST_F(InMemoryFileSystemTest
, AddDirectoryThenAddChild
) {
1185 FS
.addFile("/a", 0, MemoryBuffer::getMemBuffer(""), /*User=*/None
,
1186 /*Group=*/None
, sys::fs::file_type::directory_file
);
1187 FS
.addFile("/a/b", 0, MemoryBuffer::getMemBuffer("abc"), /*User=*/None
,
1188 /*Group=*/None
, sys::fs::file_type::regular_file
);
1189 auto Stat
= FS
.status("/a");
1190 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n" << FS
.toString();
1191 ASSERT_TRUE(Stat
->isDirectory());
1192 Stat
= FS
.status("/a/b");
1193 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n" << FS
.toString();
1194 ASSERT_TRUE(Stat
->isRegularFile());
1197 // Test that the name returned by status() is in the same form as the path that
1198 // was requested (to match the behavior of RealFileSystem).
1199 TEST_F(InMemoryFileSystemTest
, StatusName
) {
1200 NormalizedFS
.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"),
1202 /*Group=*/None
, sys::fs::file_type::regular_file
);
1203 NormalizedFS
.setCurrentWorkingDirectory("/a/b");
1205 // Access using InMemoryFileSystem::status.
1206 auto Stat
= NormalizedFS
.status("../b/c");
1207 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n"
1208 << NormalizedFS
.toString();
1209 ASSERT_TRUE(Stat
->isRegularFile());
1210 ASSERT_EQ("../b/c", Stat
->getName());
1212 // Access using InMemoryFileAdaptor::status.
1213 auto File
= NormalizedFS
.openFileForRead("../b/c");
1214 ASSERT_FALSE(File
.getError()) << File
.getError() << "\n"
1215 << NormalizedFS
.toString();
1216 Stat
= (*File
)->status();
1217 ASSERT_FALSE(Stat
.getError()) << Stat
.getError() << "\n"
1218 << NormalizedFS
.toString();
1219 ASSERT_TRUE(Stat
->isRegularFile());
1220 ASSERT_EQ("../b/c", Stat
->getName());
1222 // Access using a directory iterator.
1224 llvm::vfs::directory_iterator It
= NormalizedFS
.dir_begin("../b", EC
);
1225 // When on Windows, we end up with "../b\\c" as the name. Convert to Posix
1226 // path for the sake of the comparison.
1227 ASSERT_EQ("../b/c", getPosixPath(It
->path()));
1230 TEST_F(InMemoryFileSystemTest
, AddHardLinkToFile
) {
1231 StringRef FromLink
= "/path/to/FROM/link";
1232 StringRef Target
= "/path/to/TO/file";
1233 FS
.addFile(Target
, 0, MemoryBuffer::getMemBuffer("content of target"));
1234 EXPECT_TRUE(FS
.addHardLink(FromLink
, Target
));
1235 EXPECT_THAT(FromLink
, IsHardLinkTo(&FS
, Target
));
1236 EXPECT_TRUE(FS
.status(FromLink
)->getSize() == FS
.status(Target
)->getSize());
1237 EXPECT_TRUE(FS
.getBufferForFile(FromLink
)->get()->getBuffer() ==
1238 FS
.getBufferForFile(Target
)->get()->getBuffer());
1241 TEST_F(InMemoryFileSystemTest
, AddHardLinkInChainPattern
) {
1242 StringRef Link0
= "/path/to/0/link";
1243 StringRef Link1
= "/path/to/1/link";
1244 StringRef Link2
= "/path/to/2/link";
1245 StringRef Target
= "/path/to/target";
1246 FS
.addFile(Target
, 0, MemoryBuffer::getMemBuffer("content of target file"));
1247 EXPECT_TRUE(FS
.addHardLink(Link2
, Target
));
1248 EXPECT_TRUE(FS
.addHardLink(Link1
, Link2
));
1249 EXPECT_TRUE(FS
.addHardLink(Link0
, Link1
));
1250 EXPECT_THAT(Link0
, IsHardLinkTo(&FS
, Target
));
1251 EXPECT_THAT(Link1
, IsHardLinkTo(&FS
, Target
));
1252 EXPECT_THAT(Link2
, IsHardLinkTo(&FS
, Target
));
1255 TEST_F(InMemoryFileSystemTest
, AddHardLinkToAFileThatWasNotAddedBefore
) {
1256 EXPECT_FALSE(FS
.addHardLink("/path/to/link", "/path/to/target"));
1259 TEST_F(InMemoryFileSystemTest
, AddHardLinkFromAFileThatWasAddedBefore
) {
1260 StringRef Link
= "/path/to/link";
1261 StringRef Target
= "/path/to/target";
1262 FS
.addFile(Target
, 0, MemoryBuffer::getMemBuffer("content of target"));
1263 FS
.addFile(Link
, 0, MemoryBuffer::getMemBuffer("content of link"));
1264 EXPECT_FALSE(FS
.addHardLink(Link
, Target
));
1267 TEST_F(InMemoryFileSystemTest
, AddSameHardLinkMoreThanOnce
) {
1268 StringRef Link
= "/path/to/link";
1269 StringRef Target
= "/path/to/target";
1270 FS
.addFile(Target
, 0, MemoryBuffer::getMemBuffer("content of target"));
1271 EXPECT_TRUE(FS
.addHardLink(Link
, Target
));
1272 EXPECT_FALSE(FS
.addHardLink(Link
, Target
));
1275 TEST_F(InMemoryFileSystemTest
, AddFileInPlaceOfAHardLinkWithSameContent
) {
1276 StringRef Link
= "/path/to/link";
1277 StringRef Target
= "/path/to/target";
1278 StringRef Content
= "content of target";
1279 EXPECT_TRUE(FS
.addFile(Target
, 0, MemoryBuffer::getMemBuffer(Content
)));
1280 EXPECT_TRUE(FS
.addHardLink(Link
, Target
));
1281 EXPECT_TRUE(FS
.addFile(Link
, 0, MemoryBuffer::getMemBuffer(Content
)));
1284 TEST_F(InMemoryFileSystemTest
, AddFileInPlaceOfAHardLinkWithDifferentContent
) {
1285 StringRef Link
= "/path/to/link";
1286 StringRef Target
= "/path/to/target";
1287 StringRef Content
= "content of target";
1288 StringRef LinkContent
= "different content of link";
1289 EXPECT_TRUE(FS
.addFile(Target
, 0, MemoryBuffer::getMemBuffer(Content
)));
1290 EXPECT_TRUE(FS
.addHardLink(Link
, Target
));
1291 EXPECT_FALSE(FS
.addFile(Link
, 0, MemoryBuffer::getMemBuffer(LinkContent
)));
1294 TEST_F(InMemoryFileSystemTest
, AddHardLinkToADirectory
) {
1295 StringRef Dir
= "path/to/dummy/dir";
1296 StringRef Link
= "/path/to/link";
1297 StringRef File
= "path/to/dummy/dir/target";
1298 StringRef Content
= "content of target";
1299 EXPECT_TRUE(FS
.addFile(File
, 0, MemoryBuffer::getMemBuffer(Content
)));
1300 EXPECT_FALSE(FS
.addHardLink(Link
, Dir
));
1303 TEST_F(InMemoryFileSystemTest
, AddHardLinkFromADirectory
) {
1304 StringRef Dir
= "path/to/dummy/dir";
1305 StringRef Target
= "path/to/dummy/dir/target";
1306 StringRef Content
= "content of target";
1307 EXPECT_TRUE(FS
.addFile(Target
, 0, MemoryBuffer::getMemBuffer(Content
)));
1308 EXPECT_FALSE(FS
.addHardLink(Dir
, Target
));
1311 TEST_F(InMemoryFileSystemTest
, AddHardLinkUnderAFile
) {
1312 StringRef CommonContent
= "content string";
1313 FS
.addFile("/a/b", 0, MemoryBuffer::getMemBuffer(CommonContent
));
1314 FS
.addFile("/c/d", 0, MemoryBuffer::getMemBuffer(CommonContent
));
1315 EXPECT_FALSE(FS
.addHardLink("/c/d/e", "/a/b"));
1318 TEST_F(InMemoryFileSystemTest
, RecursiveIterationWithHardLink
) {
1320 FS
.addFile("/a/b", 0, MemoryBuffer::getMemBuffer("content string"));
1321 EXPECT_TRUE(FS
.addHardLink("/c/d", "/a/b"));
1322 auto I
= vfs::recursive_directory_iterator(FS
, "/", EC
);
1324 std::vector
<std::string
> Nodes
;
1325 for (auto E
= vfs::recursive_directory_iterator(); !EC
&& I
!= E
;
1327 Nodes
.push_back(getPosixPath(I
->path()));
1329 EXPECT_THAT(Nodes
, testing::UnorderedElementsAre("/a", "/a/b", "/c", "/c/d"));
1332 // NOTE: in the tests below, we use '//root/' as our root directory, since it is
1333 // a legal *absolute* path on Windows as well as *nix.
1334 class VFSFromYAMLTest
: public ::testing::Test
{
1338 void SetUp() override
{ NumDiagnostics
= 0; }
1340 static void CountingDiagHandler(const SMDiagnostic
&, void *Context
) {
1341 VFSFromYAMLTest
*Test
= static_cast<VFSFromYAMLTest
*>(Context
);
1342 ++Test
->NumDiagnostics
;
1345 IntrusiveRefCntPtr
<vfs::FileSystem
>
1346 getFromYAMLRawString(StringRef Content
,
1347 IntrusiveRefCntPtr
<vfs::FileSystem
> ExternalFS
) {
1348 std::unique_ptr
<MemoryBuffer
> Buffer
= MemoryBuffer::getMemBuffer(Content
);
1349 return getVFSFromYAML(std::move(Buffer
), CountingDiagHandler
, "", this,
1353 IntrusiveRefCntPtr
<vfs::FileSystem
> getFromYAMLString(
1355 IntrusiveRefCntPtr
<vfs::FileSystem
> ExternalFS
= new DummyFileSystem()) {
1356 std::string
VersionPlusContent("{\n 'version':0,\n");
1357 VersionPlusContent
+= Content
.slice(Content
.find('{') + 1, StringRef::npos
);
1358 return getFromYAMLRawString(VersionPlusContent
, ExternalFS
);
1361 // This is intended as a "XFAIL" for windows hosts.
1362 bool supportsSameDirMultipleYAMLEntries() {
1363 Triple
Host(Triple::normalize(sys::getProcessTriple()));
1364 return !Host
.isOSWindows();
1368 TEST_F(VFSFromYAMLTest
, BasicVFSFromYAML
) {
1369 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
;
1370 FS
= getFromYAMLString("");
1371 EXPECT_EQ(nullptr, FS
.get());
1372 FS
= getFromYAMLString("[]");
1373 EXPECT_EQ(nullptr, FS
.get());
1374 FS
= getFromYAMLString("'string'");
1375 EXPECT_EQ(nullptr, FS
.get());
1376 EXPECT_EQ(3, NumDiagnostics
);
1379 TEST_F(VFSFromYAMLTest
, MappedFiles
) {
1380 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
1381 Lower
->addRegularFile("//root/foo/bar/a");
1382 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= getFromYAMLString(
1385 " 'type': 'directory',\n"
1386 " 'name': '//root/',\n"
1387 " 'contents': [ {\n"
1388 " 'type': 'file',\n"
1389 " 'name': 'file1',\n"
1390 " 'external-contents': '//root/foo/bar/a'\n"
1393 " 'type': 'file',\n"
1394 " 'name': 'file2',\n"
1395 " 'external-contents': '//root/foo/b'\n"
1402 ASSERT_TRUE(FS
.get() != nullptr);
1404 IntrusiveRefCntPtr
<vfs::OverlayFileSystem
> O(
1405 new vfs::OverlayFileSystem(Lower
));
1409 ErrorOr
<vfs::Status
> S
= O
->status("//root/file1");
1410 ASSERT_FALSE(S
.getError());
1411 EXPECT_EQ("//root/foo/bar/a", S
->getName());
1412 EXPECT_TRUE(S
->IsVFSMapped
);
1414 ErrorOr
<vfs::Status
> SLower
= O
->status("//root/foo/bar/a");
1415 EXPECT_EQ("//root/foo/bar/a", SLower
->getName());
1416 EXPECT_TRUE(S
->equivalent(*SLower
));
1417 EXPECT_FALSE(SLower
->IsVFSMapped
);
1419 // file after opening
1420 auto OpenedF
= O
->openFileForRead("//root/file1");
1421 ASSERT_FALSE(OpenedF
.getError());
1422 auto OpenedS
= (*OpenedF
)->status();
1423 ASSERT_FALSE(OpenedS
.getError());
1424 EXPECT_EQ("//root/foo/bar/a", OpenedS
->getName());
1425 EXPECT_TRUE(OpenedS
->IsVFSMapped
);
1428 S
= O
->status("//root/");
1429 ASSERT_FALSE(S
.getError());
1430 EXPECT_TRUE(S
->isDirectory());
1431 EXPECT_TRUE(S
->equivalent(*O
->status("//root/"))); // non-volatile UniqueID
1434 EXPECT_EQ(O
->status("//root/file2").getError(),
1435 llvm::errc::no_such_file_or_directory
);
1436 EXPECT_EQ(0, NumDiagnostics
);
1439 TEST_F(VFSFromYAMLTest
, CaseInsensitive
) {
1440 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
1441 Lower
->addRegularFile("//root/foo/bar/a");
1442 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= getFromYAMLString(
1443 "{ 'case-sensitive': 'false',\n"
1446 " 'type': 'directory',\n"
1447 " 'name': '//root/',\n"
1448 " 'contents': [ {\n"
1449 " 'type': 'file',\n"
1451 " 'external-contents': '//root/foo/bar/a'\n"
1456 ASSERT_TRUE(FS
.get() != nullptr);
1458 IntrusiveRefCntPtr
<vfs::OverlayFileSystem
> O(
1459 new vfs::OverlayFileSystem(Lower
));
1462 ErrorOr
<vfs::Status
> S
= O
->status("//root/XX");
1463 ASSERT_FALSE(S
.getError());
1465 ErrorOr
<vfs::Status
> SS
= O
->status("//root/xx");
1466 ASSERT_FALSE(SS
.getError());
1467 EXPECT_TRUE(S
->equivalent(*SS
));
1468 SS
= O
->status("//root/xX");
1469 EXPECT_TRUE(S
->equivalent(*SS
));
1470 SS
= O
->status("//root/Xx");
1471 EXPECT_TRUE(S
->equivalent(*SS
));
1472 EXPECT_EQ(0, NumDiagnostics
);
1475 TEST_F(VFSFromYAMLTest
, CaseSensitive
) {
1476 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
1477 Lower
->addRegularFile("//root/foo/bar/a");
1478 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= getFromYAMLString(
1479 "{ 'case-sensitive': 'true',\n"
1482 " 'type': 'directory',\n"
1483 " 'name': '//root/',\n"
1484 " 'contents': [ {\n"
1485 " 'type': 'file',\n"
1487 " 'external-contents': '//root/foo/bar/a'\n"
1492 ASSERT_TRUE(FS
.get() != nullptr);
1494 IntrusiveRefCntPtr
<vfs::OverlayFileSystem
> O(
1495 new vfs::OverlayFileSystem(Lower
));
1498 ErrorOr
<vfs::Status
> SS
= O
->status("//root/xx");
1499 EXPECT_EQ(SS
.getError(), llvm::errc::no_such_file_or_directory
);
1500 SS
= O
->status("//root/xX");
1501 EXPECT_EQ(SS
.getError(), llvm::errc::no_such_file_or_directory
);
1502 SS
= O
->status("//root/Xx");
1503 EXPECT_EQ(SS
.getError(), llvm::errc::no_such_file_or_directory
);
1504 EXPECT_EQ(0, NumDiagnostics
);
1507 TEST_F(VFSFromYAMLTest
, IllegalVFSFile
) {
1508 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
1510 // invalid YAML at top-level
1511 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= getFromYAMLString("{]", Lower
);
1512 EXPECT_EQ(nullptr, FS
.get());
1513 // invalid YAML in roots
1514 FS
= getFromYAMLString("{ 'roots':[}", Lower
);
1515 // invalid YAML in directory
1516 FS
= getFromYAMLString(
1517 "{ 'roots':[ { 'name': 'foo', 'type': 'directory', 'contents': [}",
1519 EXPECT_EQ(nullptr, FS
.get());
1521 // invalid configuration
1522 FS
= getFromYAMLString("{ 'knobular': 'true', 'roots':[] }", Lower
);
1523 EXPECT_EQ(nullptr, FS
.get());
1524 FS
= getFromYAMLString("{ 'case-sensitive': 'maybe', 'roots':[] }", Lower
);
1525 EXPECT_EQ(nullptr, FS
.get());
1528 FS
= getFromYAMLString("{ 'roots':'' }", Lower
);
1529 EXPECT_EQ(nullptr, FS
.get());
1530 FS
= getFromYAMLString("{ 'roots':{} }", Lower
);
1531 EXPECT_EQ(nullptr, FS
.get());
1534 FS
= getFromYAMLString(
1535 "{ 'roots':[ { 'type': 'other', 'name': 'me', 'contents': '' }", Lower
);
1536 EXPECT_EQ(nullptr, FS
.get());
1537 FS
= getFromYAMLString("{ 'roots':[ { 'type': 'file', 'name': [], "
1538 "'external-contents': 'other' }",
1540 EXPECT_EQ(nullptr, FS
.get());
1541 FS
= getFromYAMLString(
1542 "{ 'roots':[ { 'type': 'file', 'name': 'me', 'external-contents': [] }",
1544 EXPECT_EQ(nullptr, FS
.get());
1545 FS
= getFromYAMLString(
1546 "{ 'roots':[ { 'type': 'file', 'name': 'me', 'external-contents': {} }",
1548 EXPECT_EQ(nullptr, FS
.get());
1549 FS
= getFromYAMLString(
1550 "{ 'roots':[ { 'type': 'directory', 'name': 'me', 'contents': {} }",
1552 EXPECT_EQ(nullptr, FS
.get());
1553 FS
= getFromYAMLString(
1554 "{ 'roots':[ { 'type': 'directory', 'name': 'me', 'contents': '' }",
1556 EXPECT_EQ(nullptr, FS
.get());
1557 FS
= getFromYAMLString(
1558 "{ 'roots':[ { 'thingy': 'directory', 'name': 'me', 'contents': [] }",
1560 EXPECT_EQ(nullptr, FS
.get());
1562 // missing mandatory fields
1563 FS
= getFromYAMLString("{ 'roots':[ { 'type': 'file', 'name': 'me' }", Lower
);
1564 EXPECT_EQ(nullptr, FS
.get());
1565 FS
= getFromYAMLString(
1566 "{ 'roots':[ { 'type': 'file', 'external-contents': 'other' }", Lower
);
1567 EXPECT_EQ(nullptr, FS
.get());
1568 FS
= getFromYAMLString("{ 'roots':[ { 'name': 'me', 'contents': [] }", Lower
);
1569 EXPECT_EQ(nullptr, FS
.get());
1572 FS
= getFromYAMLString("{ 'roots':[], 'roots':[] }", Lower
);
1573 EXPECT_EQ(nullptr, FS
.get());
1574 FS
= getFromYAMLString(
1575 "{ 'case-sensitive':'true', 'case-sensitive':'true', 'roots':[] }",
1577 EXPECT_EQ(nullptr, FS
.get());
1579 getFromYAMLString("{ 'roots':[{'name':'me', 'name':'you', 'type':'file', "
1580 "'external-contents':'blah' } ] }",
1582 EXPECT_EQ(nullptr, FS
.get());
1585 FS
= getFromYAMLRawString("{ 'roots':[] }", Lower
);
1586 EXPECT_EQ(nullptr, FS
.get());
1588 // bad version number
1589 FS
= getFromYAMLRawString("{ 'version':'foo', 'roots':[] }", Lower
);
1590 EXPECT_EQ(nullptr, FS
.get());
1591 FS
= getFromYAMLRawString("{ 'version':-1, 'roots':[] }", Lower
);
1592 EXPECT_EQ(nullptr, FS
.get());
1593 FS
= getFromYAMLRawString("{ 'version':100000, 'roots':[] }", Lower
);
1594 EXPECT_EQ(nullptr, FS
.get());
1595 EXPECT_EQ(24, NumDiagnostics
);
1598 TEST_F(VFSFromYAMLTest
, UseExternalName
) {
1599 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
1600 Lower
->addRegularFile("//root/external/file");
1602 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
=
1603 getFromYAMLString("{ 'roots': [\n"
1604 " { 'type': 'file', 'name': '//root/A',\n"
1605 " 'external-contents': '//root/external/file'\n"
1607 " { 'type': 'file', 'name': '//root/B',\n"
1608 " 'use-external-name': true,\n"
1609 " 'external-contents': '//root/external/file'\n"
1611 " { 'type': 'file', 'name': '//root/C',\n"
1612 " 'use-external-name': false,\n"
1613 " 'external-contents': '//root/external/file'\n"
1617 ASSERT_TRUE(nullptr != FS
.get());
1620 EXPECT_EQ("//root/external/file", FS
->status("//root/A")->getName());
1622 EXPECT_EQ("//root/external/file", FS
->status("//root/B")->getName());
1623 EXPECT_EQ("//root/C", FS
->status("//root/C")->getName());
1625 // global configuration
1626 FS
= getFromYAMLString("{ 'use-external-names': false,\n"
1628 " { 'type': 'file', 'name': '//root/A',\n"
1629 " 'external-contents': '//root/external/file'\n"
1631 " { 'type': 'file', 'name': '//root/B',\n"
1632 " 'use-external-name': true,\n"
1633 " 'external-contents': '//root/external/file'\n"
1635 " { 'type': 'file', 'name': '//root/C',\n"
1636 " 'use-external-name': false,\n"
1637 " 'external-contents': '//root/external/file'\n"
1641 ASSERT_TRUE(nullptr != FS
.get());
1644 EXPECT_EQ("//root/A", FS
->status("//root/A")->getName());
1646 EXPECT_EQ("//root/external/file", FS
->status("//root/B")->getName());
1647 EXPECT_EQ("//root/C", FS
->status("//root/C")->getName());
1650 TEST_F(VFSFromYAMLTest
, MultiComponentPath
) {
1651 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
1652 Lower
->addRegularFile("//root/other");
1655 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
=
1656 getFromYAMLString("{ 'roots': [\n"
1657 " { 'type': 'file', 'name': '//root/path/to/file',\n"
1658 " 'external-contents': '//root/other' }]\n"
1661 ASSERT_TRUE(nullptr != FS
.get());
1662 EXPECT_FALSE(FS
->status("//root/path/to/file").getError());
1663 EXPECT_FALSE(FS
->status("//root/path/to").getError());
1664 EXPECT_FALSE(FS
->status("//root/path").getError());
1665 EXPECT_FALSE(FS
->status("//root/").getError());
1668 FS
= getFromYAMLString(
1670 " { 'type': 'directory', 'name': '//root/path/to',\n"
1671 " 'contents': [ { 'type': 'file', 'name': 'file',\n"
1672 " 'external-contents': '//root/other' }]}]\n"
1675 ASSERT_TRUE(nullptr != FS
.get());
1676 EXPECT_FALSE(FS
->status("//root/path/to/file").getError());
1677 EXPECT_FALSE(FS
->status("//root/path/to").getError());
1678 EXPECT_FALSE(FS
->status("//root/path").getError());
1679 EXPECT_FALSE(FS
->status("//root/").getError());
1682 FS
= getFromYAMLString(
1684 " { 'type': 'directory', 'name': '//root/',\n"
1685 " 'contents': [ { 'type': 'file', 'name': 'path/to/file',\n"
1686 " 'external-contents': '//root/other' }]}]\n"
1689 ASSERT_TRUE(nullptr != FS
.get());
1690 EXPECT_FALSE(FS
->status("//root/path/to/file").getError());
1691 EXPECT_FALSE(FS
->status("//root/path/to").getError());
1692 EXPECT_FALSE(FS
->status("//root/path").getError());
1693 EXPECT_FALSE(FS
->status("//root/").getError());
1696 TEST_F(VFSFromYAMLTest
, TrailingSlashes
) {
1697 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
1698 Lower
->addRegularFile("//root/other");
1701 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= getFromYAMLString(
1703 " { 'type': 'directory', 'name': '//root/path/to////',\n"
1704 " 'contents': [ { 'type': 'file', 'name': 'file',\n"
1705 " 'external-contents': '//root/other' }]}]\n"
1708 ASSERT_TRUE(nullptr != FS
.get());
1709 EXPECT_FALSE(FS
->status("//root/path/to/file").getError());
1710 EXPECT_FALSE(FS
->status("//root/path/to").getError());
1711 EXPECT_FALSE(FS
->status("//root/path").getError());
1712 EXPECT_FALSE(FS
->status("//root/").getError());
1715 TEST_F(VFSFromYAMLTest
, DirectoryIteration
) {
1716 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
1717 Lower
->addDirectory("//root/");
1718 Lower
->addDirectory("//root/foo");
1719 Lower
->addDirectory("//root/foo/bar");
1720 Lower
->addRegularFile("//root/foo/bar/a");
1721 Lower
->addRegularFile("//root/foo/bar/b");
1722 Lower
->addRegularFile("//root/file3");
1723 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= getFromYAMLString(
1724 "{ 'use-external-names': false,\n"
1727 " 'type': 'directory',\n"
1728 " 'name': '//root/',\n"
1729 " 'contents': [ {\n"
1730 " 'type': 'file',\n"
1731 " 'name': 'file1',\n"
1732 " 'external-contents': '//root/foo/bar/a'\n"
1735 " 'type': 'file',\n"
1736 " 'name': 'file2',\n"
1737 " 'external-contents': '//root/foo/bar/b'\n"
1744 ASSERT_TRUE(FS
.get() != nullptr);
1746 IntrusiveRefCntPtr
<vfs::OverlayFileSystem
> O(
1747 new vfs::OverlayFileSystem(Lower
));
1751 checkContents(O
->dir_begin("//root/", EC
),
1752 {"//root/file1", "//root/file2", "//root/file3", "//root/foo"});
1754 checkContents(O
->dir_begin("//root/foo/bar", EC
),
1755 {"//root/foo/bar/a", "//root/foo/bar/b"});
1758 TEST_F(VFSFromYAMLTest
, DirectoryIterationSameDirMultipleEntries
) {
1759 // https://llvm.org/bugs/show_bug.cgi?id=27725
1760 if (!supportsSameDirMultipleYAMLEntries())
1763 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
1764 Lower
->addDirectory("//root/zab");
1765 Lower
->addDirectory("//root/baz");
1766 Lower
->addRegularFile("//root/zab/a");
1767 Lower
->addRegularFile("//root/zab/b");
1768 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= getFromYAMLString(
1769 "{ 'use-external-names': false,\n"
1772 " 'type': 'directory',\n"
1773 " 'name': '//root/baz/',\n"
1774 " 'contents': [ {\n"
1775 " 'type': 'file',\n"
1777 " 'external-contents': '//root/zab/a'\n"
1782 " 'type': 'directory',\n"
1783 " 'name': '//root/baz/',\n"
1784 " 'contents': [ {\n"
1785 " 'type': 'file',\n"
1787 " 'external-contents': '//root/zab/b'\n"
1794 ASSERT_TRUE(FS
.get() != nullptr);
1796 IntrusiveRefCntPtr
<vfs::OverlayFileSystem
> O(
1797 new vfs::OverlayFileSystem(Lower
));
1802 checkContents(O
->dir_begin("//root/baz/", EC
),
1803 {"//root/baz/x", "//root/baz/y"});
1806 TEST_F(VFSFromYAMLTest
, RecursiveDirectoryIterationLevel
) {
1808 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
1809 Lower
->addDirectory("//root/a");
1810 Lower
->addDirectory("//root/a/b");
1811 Lower
->addDirectory("//root/a/b/c");
1812 Lower
->addRegularFile("//root/a/b/c/file");
1813 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= getFromYAMLString(
1814 "{ 'use-external-names': false,\n"
1817 " 'type': 'directory',\n"
1818 " 'name': '//root/a/b/c/',\n"
1819 " 'contents': [ {\n"
1820 " 'type': 'file',\n"
1821 " 'name': 'file',\n"
1822 " 'external-contents': '//root/a/b/c/file'\n"
1829 ASSERT_TRUE(FS
.get() != nullptr);
1831 IntrusiveRefCntPtr
<vfs::OverlayFileSystem
> O(
1832 new vfs::OverlayFileSystem(Lower
));
1837 // Test recursive_directory_iterator level()
1838 vfs::recursive_directory_iterator I
= vfs::recursive_directory_iterator(
1842 for (int l
= 0; I
!= E
; I
.increment(EC
), ++l
) {
1844 EXPECT_EQ(I
.level(), l
);
1849 TEST_F(VFSFromYAMLTest
, RelativePaths
) {
1850 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
1851 // Filename at root level without a parent directory.
1852 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= getFromYAMLString(
1854 " { 'type': 'file', 'name': 'file-not-in-directory.h',\n"
1855 " 'external-contents': '//root/external/file'\n"
1859 EXPECT_EQ(nullptr, FS
.get());
1861 // Relative file path.
1862 FS
= getFromYAMLString("{ 'roots': [\n"
1863 " { 'type': 'file', 'name': 'relative/file/path.h',\n"
1864 " 'external-contents': '//root/external/file'\n"
1868 EXPECT_EQ(nullptr, FS
.get());
1870 // Relative directory path.
1871 FS
= getFromYAMLString(
1873 " { 'type': 'directory', 'name': 'relative/directory/path.h',\n"
1878 EXPECT_EQ(nullptr, FS
.get());
1880 EXPECT_EQ(3, NumDiagnostics
);
1883 TEST_F(VFSFromYAMLTest
, NonFallthroughDirectoryIteration
) {
1884 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
1885 Lower
->addDirectory("//root/");
1886 Lower
->addRegularFile("//root/a");
1887 Lower
->addRegularFile("//root/b");
1888 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= getFromYAMLString(
1889 "{ 'use-external-names': false,\n"
1890 " 'fallthrough': false,\n"
1893 " 'type': 'directory',\n"
1894 " 'name': '//root/',\n"
1895 " 'contents': [ {\n"
1896 " 'type': 'file',\n"
1898 " 'external-contents': '//root/a'\n"
1905 ASSERT_TRUE(FS
.get() != nullptr);
1908 checkContents(FS
->dir_begin("//root/", EC
),
1912 TEST_F(VFSFromYAMLTest
, DirectoryIterationWithDuplicates
) {
1913 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
1914 Lower
->addDirectory("//root/");
1915 Lower
->addRegularFile("//root/a");
1916 Lower
->addRegularFile("//root/b");
1917 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= getFromYAMLString(
1918 "{ 'use-external-names': false,\n"
1921 " 'type': 'directory',\n"
1922 " 'name': '//root/',\n"
1923 " 'contents': [ {\n"
1924 " 'type': 'file',\n"
1926 " 'external-contents': '//root/a'\n"
1933 ASSERT_TRUE(FS
.get() != nullptr);
1936 checkContents(FS
->dir_begin("//root/", EC
),
1937 {"//root/a", "//root/b"});
1940 TEST_F(VFSFromYAMLTest
, DirectoryIterationErrorInVFSLayer
) {
1941 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
1942 Lower
->addDirectory("//root/");
1943 Lower
->addDirectory("//root/foo");
1944 Lower
->addRegularFile("//root/foo/a");
1945 Lower
->addRegularFile("//root/foo/b");
1946 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= getFromYAMLString(
1947 "{ 'use-external-names': false,\n"
1950 " 'type': 'directory',\n"
1951 " 'name': '//root/',\n"
1952 " 'contents': [ {\n"
1953 " 'type': 'file',\n"
1954 " 'name': 'bar/a',\n"
1955 " 'external-contents': '//root/foo/a'\n"
1962 ASSERT_TRUE(FS
.get() != nullptr);
1965 checkContents(FS
->dir_begin("//root/foo", EC
),
1966 {"//root/foo/a", "//root/foo/b"});
1969 TEST_F(VFSFromYAMLTest
, GetRealPath
) {
1970 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
1971 Lower
->addDirectory("//dir/");
1972 Lower
->addRegularFile("/foo");
1973 Lower
->addSymlink("/link");
1974 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= getFromYAMLString(
1975 "{ 'use-external-names': false,\n"
1978 " 'type': 'directory',\n"
1979 " 'name': '//root/',\n"
1980 " 'contents': [ {\n"
1981 " 'type': 'file',\n"
1983 " 'external-contents': '/link'\n"
1988 " 'type': 'directory',\n"
1989 " 'name': '//dir/',\n"
1995 ASSERT_TRUE(FS
.get() != nullptr);
1997 // Regular file present in underlying file system.
1998 SmallString
<16> RealPath
;
1999 EXPECT_FALSE(FS
->getRealPath("/foo", RealPath
));
2000 EXPECT_EQ(RealPath
.str(), "/foo");
2002 // File present in YAML pointing to symlink in underlying file system.
2003 EXPECT_FALSE(FS
->getRealPath("//root/bar", RealPath
));
2004 EXPECT_EQ(RealPath
.str(), "/symlink");
2006 // Directories should fall back to the underlying file system is possible.
2007 EXPECT_FALSE(FS
->getRealPath("//dir/", RealPath
));
2008 EXPECT_EQ(RealPath
.str(), "//dir/");
2010 // Try a non-existing file.
2011 EXPECT_EQ(FS
->getRealPath("/non_existing", RealPath
),
2012 errc::no_such_file_or_directory
);
2015 TEST_F(VFSFromYAMLTest
, WorkingDirectory
) {
2016 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
2017 Lower
->addDirectory("//root/");
2018 Lower
->addDirectory("//root/foo");
2019 Lower
->addRegularFile("//root/foo/a");
2020 Lower
->addRegularFile("//root/foo/b");
2021 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= getFromYAMLString(
2022 "{ 'use-external-names': false,\n"
2025 " 'type': 'directory',\n"
2026 " 'name': '//root/bar',\n"
2027 " 'contents': [ {\n"
2028 " 'type': 'file',\n"
2030 " 'external-contents': '//root/foo/a'\n"
2037 ASSERT_TRUE(FS
.get() != nullptr);
2038 std::error_code EC
= FS
->setCurrentWorkingDirectory("//root/bar");
2041 llvm::ErrorOr
<std::string
> WorkingDir
= FS
->getCurrentWorkingDirectory();
2042 ASSERT_TRUE(WorkingDir
);
2043 EXPECT_EQ(*WorkingDir
, "//root/bar");
2045 llvm::ErrorOr
<vfs::Status
> Status
= FS
->status("./a");
2046 ASSERT_FALSE(Status
.getError());
2047 EXPECT_TRUE(Status
->isStatusKnown());
2048 EXPECT_FALSE(Status
->isDirectory());
2049 EXPECT_TRUE(Status
->isRegularFile());
2050 EXPECT_FALSE(Status
->isSymlink());
2051 EXPECT_FALSE(Status
->isOther());
2052 EXPECT_TRUE(Status
->exists());
2054 EC
= FS
->setCurrentWorkingDirectory("bogus");
2056 WorkingDir
= FS
->getCurrentWorkingDirectory();
2057 ASSERT_TRUE(WorkingDir
);
2058 EXPECT_EQ(*WorkingDir
, "//root/bar");
2060 EC
= FS
->setCurrentWorkingDirectory("//root/");
2062 WorkingDir
= FS
->getCurrentWorkingDirectory();
2063 ASSERT_TRUE(WorkingDir
);
2064 EXPECT_EQ(*WorkingDir
, "//root/");
2066 EC
= FS
->setCurrentWorkingDirectory("bar");
2068 WorkingDir
= FS
->getCurrentWorkingDirectory();
2069 ASSERT_TRUE(WorkingDir
);
2070 EXPECT_EQ(*WorkingDir
, "//root/bar");
2073 TEST_F(VFSFromYAMLTest
, WorkingDirectoryFallthrough
) {
2074 IntrusiveRefCntPtr
<DummyFileSystem
> Lower(new DummyFileSystem());
2075 Lower
->addDirectory("//root/");
2076 Lower
->addDirectory("//root/foo");
2077 Lower
->addRegularFile("//root/foo/a");
2078 Lower
->addRegularFile("//root/foo/b");
2079 Lower
->addRegularFile("//root/c");
2080 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= getFromYAMLString(
2081 "{ 'use-external-names': false,\n"
2084 " 'type': 'directory',\n"
2085 " 'name': '//root/bar',\n"
2086 " 'contents': [ {\n"
2087 " 'type': 'file',\n"
2089 " 'external-contents': '//root/foo/a'\n"
2096 ASSERT_TRUE(FS
.get() != nullptr);
2097 std::error_code EC
= FS
->setCurrentWorkingDirectory("//root/");
2099 ASSERT_TRUE(FS
.get() != nullptr);
2101 llvm::ErrorOr
<vfs::Status
> Status
= FS
->status("bar/a");
2102 ASSERT_FALSE(Status
.getError());
2103 EXPECT_TRUE(Status
->exists());
2105 Status
= FS
->status("foo/a");
2106 ASSERT_FALSE(Status
.getError());
2107 EXPECT_TRUE(Status
->exists());
2109 EC
= FS
->setCurrentWorkingDirectory("//root/bar");
2112 Status
= FS
->status("./a");
2113 ASSERT_FALSE(Status
.getError());
2114 EXPECT_TRUE(Status
->exists());
2116 Status
= FS
->status("./b");
2117 ASSERT_TRUE(Status
.getError());
2119 Status
= FS
->status("./c");
2120 ASSERT_TRUE(Status
.getError());
2122 EC
= FS
->setCurrentWorkingDirectory("//root/");
2125 Status
= FS
->status("c");
2126 ASSERT_FALSE(Status
.getError());
2127 EXPECT_TRUE(Status
->exists());
2130 TEST_F(VFSFromYAMLTest
, WorkingDirectoryFallthroughInvalid
) {
2131 IntrusiveRefCntPtr
<ErrorDummyFileSystem
> Lower(new ErrorDummyFileSystem());
2132 Lower
->addDirectory("//root/");
2133 Lower
->addDirectory("//root/foo");
2134 Lower
->addRegularFile("//root/foo/a");
2135 Lower
->addRegularFile("//root/foo/b");
2136 Lower
->addRegularFile("//root/c");
2137 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
= getFromYAMLString(
2138 "{ 'use-external-names': false,\n"
2141 " 'type': 'directory',\n"
2142 " 'name': '//root/bar',\n"
2143 " 'contents': [ {\n"
2144 " 'type': 'file',\n"
2146 " 'external-contents': '//root/foo/a'\n"
2153 ASSERT_TRUE(FS
.get() != nullptr);
2154 std::error_code EC
= FS
->setCurrentWorkingDirectory("//root/");
2156 ASSERT_TRUE(FS
.get() != nullptr);
2158 llvm::ErrorOr
<vfs::Status
> Status
= FS
->status("bar/a");
2159 ASSERT_FALSE(Status
.getError());
2160 EXPECT_TRUE(Status
->exists());
2162 Status
= FS
->status("foo/a");
2163 ASSERT_TRUE(Status
.getError());