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