xref: /llvm-project/llvm/unittests/Support/VirtualFileSystemTest.cpp (revision 5437a4c5e90886c6688da90eaf70617d50e64895)
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 //===----------------------------------------------------------------------===//
8 
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>
23 
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;
32 
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");
42   }
43   std::error_code close() override { return std::error_code(); }
44 };
45 
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;
52 
53   static int getNextFSID() {
54     static int Count = 0;
55     return Count++;
56   }
57 
58 public:
59   DummyFileSystem() : FSID(getNextFSID()), FileID(0) {}
60 
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;
66   }
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();
73   }
74   llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const override {
75     return WorkingDirectory;
76   }
77   std::error_code setCurrentWorkingDirectory(const Twine &Path) override {
78     WorkingDirectory = Path.str();
79     return std::error_code();
80   }
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();
91     }
92     Output.clear();
93     Path.toVector(Output);
94     return std::error_code();
95   }
96 
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;
106       }
107       return false;
108     }
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;
118         }
119       }
120     }
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;
128         }
129       }
130       if (I == FilesAndDirs.end())
131         CurrentEntry = vfs::directory_entry();
132       return std::error_code();
133     }
134   };
135 
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));
140   }
141 
142   void addEntry(StringRef Path, const vfs::Status &Status) {
143     FilesAndDirs[std::string(Path)] = Status;
144   }
145 
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()));
153   }
154 
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);
160   }
161 
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);
167   }
168 
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);
174   }
175 
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;
191     }
192     OS << ")\n";
193   }
194 };
195 
196 class ErrorDummyFileSystem : public DummyFileSystem {
197   std::error_code setCurrentWorkingDirectory(const Twine &Path) override {
198     return llvm::errc::no_such_file_or_directory;
199   }
200 };
201 
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());
207 }
208 } // end anonymous namespace
209 
210 TEST(VirtualFileSystemTest, StatusQueries) {
211   IntrusiveRefCntPtr<DummyFileSystem> D(new DummyFileSystem());
212   ErrorOr<vfs::Status> Status((std::error_code()));
213 
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());
223 
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());
233 
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());
243 
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));
248 }
249 
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"));
254 
255   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(new vfs::OverlayFileSystem(D));
256   EXPECT_FALSE(Status = O->status("/foo"));
257 
258   D->addRegularFile("/foo");
259   Status = D->status("/foo");
260   EXPECT_FALSE(Status.getError());
261 
262   ErrorOr<vfs::Status> Status2((std::error_code()));
263   Status2 = O->status("/foo");
264   EXPECT_FALSE(Status2.getError());
265   EXPECT_TRUE(Status->equivalent(*Status2));
266 }
267 
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());
273 
274   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
275       new vfs::OverlayFileSystem(Lower));
276   O->pushOverlay(Upper);
277 
278   // Regular file.
279   SmallString<16> RealPath;
280   EXPECT_FALSE(O->getRealPath("/foo", RealPath));
281   EXPECT_EQ(RealPath.str(), "/foo");
282 
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");
286 
287   // Try a non-existing link.
288   EXPECT_EQ(O->getRealPath("/upper_link", RealPath),
289             errc::no_such_file_or_directory);
290 
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");
295 }
296 
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);
305 
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()));
310 
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());
326 
327   EXPECT_TRUE(Status1->equivalent(*StatusB));
328   EXPECT_TRUE(Status2->equivalent(*StatusM));
329   EXPECT_TRUE(Status3->equivalent(*StatusT));
330 
331   EXPECT_FALSE(Status1->equivalent(*Status2));
332   EXPECT_FALSE(Status2->equivalent(*Status3));
333   EXPECT_FALSE(Status1->equivalent(*Status3));
334 }
335 
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);
342 
343   Lower->addDirectory("/lower-only");
344   Upper->addDirectory("/upper-only");
345 
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));
352 
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));
358 }
359 
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);
367 
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());
374 
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());
384 }
385 
386 TEST(VirtualFileSystemTest, OverlayIterator) {
387   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
388   Lower->addRegularFile("/foo");
389   IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem());
390 
391   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
392       new vfs::OverlayFileSystem(Lower));
393   O->pushOverlay(Upper);
394 
395   ErrorOr<vfs::Status> Status((std::error_code()));
396   {
397     auto it = O->overlays_begin();
398     auto end = O->overlays_end();
399 
400     EXPECT_NE(it, end);
401 
402     Status = (*it)->status("/foo");
403     ASSERT_TRUE(Status.getError());
404 
405     it++;
406     EXPECT_NE(it, end);
407 
408     Status = (*it)->status("/foo");
409     ASSERT_FALSE(Status.getError());
410     EXPECT_TRUE(Status->exists());
411 
412     it++;
413     EXPECT_EQ(it, end);
414   }
415 
416   {
417     auto it = O->overlays_rbegin();
418     auto end = O->overlays_rend();
419 
420     EXPECT_NE(it, end);
421 
422     Status = (*it)->status("/foo");
423     ASSERT_FALSE(Status.getError());
424     EXPECT_TRUE(Status->exists());
425 
426     it++;
427     EXPECT_NE(it, end);
428 
429     Status = (*it)->status("/foo");
430     ASSERT_TRUE(Status.getError());
431 
432     it++;
433     EXPECT_EQ(it, end);
434   }
435 }
436 
437 TEST(VirtualFileSystemTest, BasicRealFSIteration) {
438   TempDir TestDirectory("virtual-file-system-test", /*Unique*/ true);
439   IntrusiveRefCntPtr<vfs::FileSystem> FS = vfs::getRealFileSystem();
440 
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
445 
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"));
450 
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);
462 }
463 
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();
477 
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());
482 
483   // openFileForRead(), indirectly.
484   auto BBuf = BFS->getBufferForFile("bb");
485   ASSERT_TRUE(BBuf);
486   EXPECT_EQ("bbbb", (*BBuf)->getBuffer());
487 
488   auto ABuf = CFS->getBufferForFile("aa");
489   ASSERT_TRUE(ABuf);
490   EXPECT_EQ("aaaa", (*ABuf)->getBuffer());
491 
492   // status()
493   auto BStat = BFS->status("bb");
494   ASSERT_TRUE(BStat);
495   EXPECT_EQ("bb", BStat->getName());
496 
497   auto AStat = CFS->status("aa");
498   ASSERT_TRUE(AStat);
499   EXPECT_EQ("aa", AStat->getName()); // unresolved name
500 
501   // getRealPath()
502   SmallString<128> BPath;
503   ASSERT_FALSE(BFS->getRealPath("bb", BPath));
504   EXPECT_EQ(BB.path(), BPath);
505 
506   SmallString<128> APath;
507   ASSERT_FALSE(CFS->getRealPath("aa", APath));
508   EXPECT_EQ(AA.path(), APath); // Reports resolved name.
509 
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());
519 
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());
528 }
529 
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); });
538 
539   // Delete the working directory to create an error.
540   ASSERT_EQ(sys::fs::remove_directories(WD), std::error_code());
541 
542   // Verify that we still get two separate working directories.
543   auto FS1 = vfs::createPhysicalFileSystem();
544   auto FS2 = vfs::createPhysicalFileSystem();
545   ASSERT_EQ(FS1->getCurrentWorkingDirectory().getError(),
546             errc::no_such_file_or_directory);
547   ASSERT_EQ(FS1->setCurrentWorkingDirectory(D2.path()), std::error_code());
548   ASSERT_EQ(FS1->getCurrentWorkingDirectory().get(), D2.path());
549   EXPECT_EQ(FS2->getCurrentWorkingDirectory().getError(),
550             errc::no_such_file_or_directory);
551   SmallString<128> WD2;
552   EXPECT_EQ(sys::fs::current_path(WD2), errc::no_such_file_or_directory);
553 }
554 
555 TEST(VirtualFileSystemTest, BrokenSymlinkRealFSIteration) {
556   TempDir TestDirectory("virtual-file-system-test", /*Unique*/ true);
557   IntrusiveRefCntPtr<vfs::FileSystem> FS = vfs::getRealFileSystem();
558 
559   TempLink _a("no_such_file", TestDirectory.path("a"));
560   TempDir _b(TestDirectory.path("b"));
561   TempLink _c("no_such_file", TestDirectory.path("c"));
562 
563   // Should get no iteration error, but a stat error for the broken symlinks.
564   std::map<std::string, std::error_code> StatResults;
565   std::error_code EC;
566   for (vfs::directory_iterator
567            I = FS->dir_begin(Twine(TestDirectory.path()), EC),
568            E;
569        I != E; I.increment(EC)) {
570     EXPECT_FALSE(EC);
571     StatResults[std::string(sys::path::filename(I->path()))] =
572         FS->status(I->path()).getError();
573   }
574   EXPECT_THAT(
575       StatResults,
576       ElementsAre(
577           Pair("a", std::make_error_code(std::errc::no_such_file_or_directory)),
578           Pair("b", std::error_code()),
579           Pair("c",
580                std::make_error_code(std::errc::no_such_file_or_directory))));
581 }
582 #endif
583 
584 TEST(VirtualFileSystemTest, BasicRealFSRecursiveIteration) {
585   TempDir TestDirectory("virtual-file-system-test", /*Unique*/ true);
586   IntrusiveRefCntPtr<vfs::FileSystem> FS = vfs::getRealFileSystem();
587 
588   std::error_code EC;
589   auto I =
590       vfs::recursive_directory_iterator(*FS, Twine(TestDirectory.path()), EC);
591   ASSERT_FALSE(EC);
592   EXPECT_EQ(vfs::recursive_directory_iterator(), I); // empty directory is empty
593 
594   TempDir _a(TestDirectory.path("a"));
595   TempDir _ab(TestDirectory.path("a/b"));
596   TempDir _c(TestDirectory.path("c"));
597   TempDir _cd(TestDirectory.path("c/d"));
598 
599   I = vfs::recursive_directory_iterator(*FS, Twine(TestDirectory.path()), EC);
600   ASSERT_FALSE(EC);
601   ASSERT_NE(vfs::recursive_directory_iterator(), I);
602 
603   std::vector<std::string> Contents;
604   for (auto E = vfs::recursive_directory_iterator(); !EC && I != E;
605        I.increment(EC)) {
606     Contents.push_back(std::string(I->path()));
607   }
608 
609   // Check contents, which may be in any order
610   EXPECT_EQ(4U, Contents.size());
611   int Counts[4] = {0, 0, 0, 0};
612   for (const std::string &Name : Contents) {
613     ASSERT_FALSE(Name.empty());
614     int Index = Name[Name.size() - 1] - 'a';
615     ASSERT_GE(Index, 0);
616     ASSERT_LT(Index, 4);
617     Counts[Index]++;
618   }
619   EXPECT_EQ(1, Counts[0]); // a
620   EXPECT_EQ(1, Counts[1]); // b
621   EXPECT_EQ(1, Counts[2]); // c
622   EXPECT_EQ(1, Counts[3]); // d
623 }
624 
625 TEST(VirtualFileSystemTest, BasicRealFSRecursiveIterationNoPush) {
626   TempDir TestDirectory("virtual-file-system-test", /*Unique*/ true);
627 
628   TempDir _a(TestDirectory.path("a"));
629   TempDir _ab(TestDirectory.path("a/b"));
630   TempDir _c(TestDirectory.path("c"));
631   TempDir _cd(TestDirectory.path("c/d"));
632   TempDir _e(TestDirectory.path("e"));
633   TempDir _ef(TestDirectory.path("e/f"));
634   TempDir _g(TestDirectory.path("g"));
635 
636   IntrusiveRefCntPtr<vfs::FileSystem> FS = vfs::getRealFileSystem();
637 
638   // Test that calling no_push on entries without subdirectories has no effect.
639   {
640     std::error_code EC;
641     auto I =
642         vfs::recursive_directory_iterator(*FS, Twine(TestDirectory.path()), EC);
643     ASSERT_FALSE(EC);
644 
645     std::vector<std::string> Contents;
646     for (auto E = vfs::recursive_directory_iterator(); !EC && I != E;
647          I.increment(EC)) {
648       Contents.push_back(std::string(I->path()));
649       char last = I->path().back();
650       switch (last) {
651       case 'b':
652       case 'd':
653       case 'f':
654       case 'g':
655         I.no_push();
656         break;
657       default:
658         break;
659       }
660     }
661     EXPECT_EQ(7U, Contents.size());
662   }
663 
664   // Test that calling no_push skips subdirectories.
665   {
666     std::error_code EC;
667     auto I =
668         vfs::recursive_directory_iterator(*FS, Twine(TestDirectory.path()), EC);
669     ASSERT_FALSE(EC);
670 
671     std::vector<std::string> Contents;
672     for (auto E = vfs::recursive_directory_iterator(); !EC && I != E;
673          I.increment(EC)) {
674       Contents.push_back(std::string(I->path()));
675       char last = I->path().back();
676       switch (last) {
677       case 'a':
678       case 'c':
679       case 'e':
680         I.no_push();
681         break;
682       default:
683         break;
684       }
685     }
686 
687     // Check contents, which may be in any order
688     EXPECT_EQ(4U, Contents.size());
689     int Counts[7] = {0, 0, 0, 0, 0, 0, 0};
690     for (const std::string &Name : Contents) {
691       ASSERT_FALSE(Name.empty());
692       int Index = Name[Name.size() - 1] - 'a';
693       ASSERT_GE(Index, 0);
694       ASSERT_LT(Index, 7);
695       Counts[Index]++;
696     }
697     EXPECT_EQ(1, Counts[0]); // a
698     EXPECT_EQ(0, Counts[1]); // b
699     EXPECT_EQ(1, Counts[2]); // c
700     EXPECT_EQ(0, Counts[3]); // d
701     EXPECT_EQ(1, Counts[4]); // e
702     EXPECT_EQ(0, Counts[5]); // f
703     EXPECT_EQ(1, Counts[6]); // g
704   }
705 }
706 
707 #ifdef LLVM_ON_UNIX
708 TEST(VirtualFileSystemTest, BrokenSymlinkRealFSRecursiveIteration) {
709   TempDir TestDirectory("virtual-file-system-test", /*Unique*/ true);
710   IntrusiveRefCntPtr<vfs::FileSystem> FS = vfs::getRealFileSystem();
711 
712   TempLink _a("no_such_file", TestDirectory.path("a"));
713   TempDir _b(TestDirectory.path("b"));
714   TempLink _ba("no_such_file", TestDirectory.path("b/a"));
715   TempDir _bb(TestDirectory.path("b/b"));
716   TempLink _bc("no_such_file", TestDirectory.path("b/c"));
717   TempLink _c("no_such_file", TestDirectory.path("c"));
718   TempDir _d(TestDirectory.path("d"));
719   TempDir _dd(TestDirectory.path("d/d"));
720   TempDir _ddd(TestDirectory.path("d/d/d"));
721   TempLink _e("no_such_file", TestDirectory.path("e"));
722 
723   std::vector<std::string> VisitedBrokenSymlinks;
724   std::vector<std::string> VisitedNonBrokenSymlinks;
725   std::error_code EC;
726   for (vfs::recursive_directory_iterator
727            I(*FS, Twine(TestDirectory.path()), EC),
728        E;
729        I != E; I.increment(EC)) {
730     EXPECT_FALSE(EC);
731     (FS->status(I->path()) ? VisitedNonBrokenSymlinks : VisitedBrokenSymlinks)
732         .push_back(std::string(I->path()));
733   }
734 
735   // Check visited file names.
736   EXPECT_THAT(VisitedBrokenSymlinks,
737               UnorderedElementsAre(_a.path().str(), _ba.path().str(),
738                                    _bc.path().str(), _c.path().str(),
739                                    _e.path().str()));
740   EXPECT_THAT(VisitedNonBrokenSymlinks,
741               UnorderedElementsAre(_b.path().str(), _bb.path().str(),
742                                    _d.path().str(), _dd.path().str(),
743                                    _ddd.path().str()));
744 }
745 #endif
746 
747 template <typename DirIter>
748 static void checkContents(DirIter I, ArrayRef<StringRef> ExpectedOut) {
749   std::error_code EC;
750   SmallVector<StringRef, 4> Expected(ExpectedOut.begin(), ExpectedOut.end());
751   SmallVector<std::string, 4> InputToCheck;
752 
753   // Do not rely on iteration order to check for contents, sort both
754   // content vectors before comparison.
755   for (DirIter E; !EC && I != E; I.increment(EC))
756     InputToCheck.push_back(std::string(I->path()));
757 
758   llvm::sort(InputToCheck);
759   llvm::sort(Expected);
760   EXPECT_EQ(InputToCheck.size(), Expected.size());
761 
762   unsigned LastElt = std::min(InputToCheck.size(), Expected.size());
763   for (unsigned Idx = 0; Idx != LastElt; ++Idx)
764     EXPECT_EQ(StringRef(InputToCheck[Idx]), Expected[Idx]);
765 }
766 
767 TEST(VirtualFileSystemTest, OverlayIteration) {
768   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
769   IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem());
770   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
771       new vfs::OverlayFileSystem(Lower));
772   O->pushOverlay(Upper);
773 
774   std::error_code EC;
775   checkContents(O->dir_begin("/", EC), ArrayRef<StringRef>());
776 
777   Lower->addRegularFile("/file1");
778   checkContents(O->dir_begin("/", EC), ArrayRef<StringRef>("/file1"));
779 
780   Upper->addRegularFile("/file2");
781   checkContents(O->dir_begin("/", EC), {"/file2", "/file1"});
782 
783   Lower->addDirectory("/dir1");
784   Lower->addRegularFile("/dir1/foo");
785   Upper->addDirectory("/dir2");
786   Upper->addRegularFile("/dir2/foo");
787   checkContents(O->dir_begin("/dir2", EC), ArrayRef<StringRef>("/dir2/foo"));
788   checkContents(O->dir_begin("/", EC), {"/dir2", "/file2", "/dir1", "/file1"});
789 }
790 
791 TEST(VirtualFileSystemTest, OverlayRecursiveIteration) {
792   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
793   IntrusiveRefCntPtr<DummyFileSystem> Middle(new DummyFileSystem());
794   IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem());
795   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
796       new vfs::OverlayFileSystem(Lower));
797   O->pushOverlay(Middle);
798   O->pushOverlay(Upper);
799 
800   std::error_code EC;
801   checkContents(vfs::recursive_directory_iterator(*O, "/", EC),
802                 ArrayRef<StringRef>());
803 
804   Lower->addRegularFile("/file1");
805   checkContents(vfs::recursive_directory_iterator(*O, "/", EC),
806                 ArrayRef<StringRef>("/file1"));
807 
808   Upper->addDirectory("/dir");
809   Upper->addRegularFile("/dir/file2");
810   checkContents(vfs::recursive_directory_iterator(*O, "/", EC),
811                 {"/dir", "/dir/file2", "/file1"});
812 
813   Lower->addDirectory("/dir1");
814   Lower->addRegularFile("/dir1/foo");
815   Lower->addDirectory("/dir1/a");
816   Lower->addRegularFile("/dir1/a/b");
817   Middle->addDirectory("/a");
818   Middle->addDirectory("/a/b");
819   Middle->addDirectory("/a/b/c");
820   Middle->addRegularFile("/a/b/c/d");
821   Middle->addRegularFile("/hiddenByUp");
822   Upper->addDirectory("/dir2");
823   Upper->addRegularFile("/dir2/foo");
824   Upper->addRegularFile("/hiddenByUp");
825   checkContents(vfs::recursive_directory_iterator(*O, "/dir2", EC),
826                 ArrayRef<StringRef>("/dir2/foo"));
827   checkContents(vfs::recursive_directory_iterator(*O, "/", EC),
828                 {"/dir", "/dir/file2", "/dir2", "/dir2/foo", "/hiddenByUp",
829                  "/a", "/a/b", "/a/b/c", "/a/b/c/d", "/dir1", "/dir1/a",
830                  "/dir1/a/b", "/dir1/foo", "/file1"});
831 }
832 
833 TEST(VirtualFileSystemTest, ThreeLevelIteration) {
834   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
835   IntrusiveRefCntPtr<DummyFileSystem> Middle(new DummyFileSystem());
836   IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem());
837   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
838       new vfs::OverlayFileSystem(Lower));
839   O->pushOverlay(Middle);
840   O->pushOverlay(Upper);
841 
842   std::error_code EC;
843   checkContents(O->dir_begin("/", EC), ArrayRef<StringRef>());
844 
845   Middle->addRegularFile("/file2");
846   checkContents(O->dir_begin("/", EC), ArrayRef<StringRef>("/file2"));
847 
848   Lower->addRegularFile("/file1");
849   Upper->addRegularFile("/file3");
850   checkContents(O->dir_begin("/", EC), {"/file3", "/file2", "/file1"});
851 }
852 
853 TEST(VirtualFileSystemTest, HiddenInIteration) {
854   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
855   IntrusiveRefCntPtr<DummyFileSystem> Middle(new DummyFileSystem());
856   IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem());
857   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
858       new vfs::OverlayFileSystem(Lower));
859   O->pushOverlay(Middle);
860   O->pushOverlay(Upper);
861 
862   std::error_code EC;
863   Lower->addRegularFile("/onlyInLow");
864   Lower->addDirectory("/hiddenByMid");
865   Lower->addDirectory("/hiddenByUp");
866   Middle->addRegularFile("/onlyInMid");
867   Middle->addRegularFile("/hiddenByMid");
868   Middle->addDirectory("/hiddenByUp");
869   Upper->addRegularFile("/onlyInUp");
870   Upper->addRegularFile("/hiddenByUp");
871   checkContents(
872       O->dir_begin("/", EC),
873       {"/hiddenByUp", "/onlyInUp", "/hiddenByMid", "/onlyInMid", "/onlyInLow"});
874 
875   // Make sure we get the top-most entry
876   {
877     std::error_code EC;
878     vfs::directory_iterator I = O->dir_begin("/", EC), E;
879     for (; !EC && I != E; I.increment(EC))
880       if (I->path() == "/hiddenByUp")
881         break;
882     ASSERT_NE(E, I);
883     EXPECT_EQ(sys::fs::file_type::regular_file, I->type());
884   }
885   {
886     std::error_code EC;
887     vfs::directory_iterator I = O->dir_begin("/", EC), E;
888     for (; !EC && I != E; I.increment(EC))
889       if (I->path() == "/hiddenByMid")
890         break;
891     ASSERT_NE(E, I);
892     EXPECT_EQ(sys::fs::file_type::regular_file, I->type());
893   }
894 }
895 
896 TEST(OverlayFileSystemTest, PrintOutput) {
897   auto Dummy = makeIntrusiveRefCnt<DummyFileSystem>();
898   auto Overlay1 = makeIntrusiveRefCnt<vfs::OverlayFileSystem>(Dummy);
899   Overlay1->pushOverlay(Dummy);
900   auto Overlay2 = makeIntrusiveRefCnt<vfs::OverlayFileSystem>(Overlay1);
901   Overlay2->pushOverlay(Dummy);
902 
903   SmallString<0> Output;
904   raw_svector_ostream OuputStream{Output};
905 
906   Overlay2->print(OuputStream, vfs::FileSystem::PrintType::Summary);
907   ASSERT_EQ("OverlayFileSystem\n", Output);
908 
909   Output.clear();
910   Overlay2->print(OuputStream, vfs::FileSystem::PrintType::Contents);
911   ASSERT_EQ("OverlayFileSystem\n"
912             "  DummyFileSystem (Summary)\n"
913             "  OverlayFileSystem\n",
914             Output);
915 
916   Output.clear();
917   Overlay2->print(OuputStream, vfs::FileSystem::PrintType::RecursiveContents);
918   ASSERT_EQ("OverlayFileSystem\n"
919             "  DummyFileSystem (RecursiveContents)\n"
920             "  OverlayFileSystem\n"
921             "    DummyFileSystem (RecursiveContents)\n"
922             "    DummyFileSystem (RecursiveContents)\n",
923             Output);
924 }
925 
926 TEST(ProxyFileSystemTest, Basic) {
927   IntrusiveRefCntPtr<vfs::InMemoryFileSystem> Base(
928       new vfs::InMemoryFileSystem());
929   vfs::ProxyFileSystem PFS(Base);
930 
931   Base->addFile("/a", 0, MemoryBuffer::getMemBuffer("test"));
932 
933   auto Stat = PFS.status("/a");
934   ASSERT_FALSE(Stat.getError());
935 
936   auto File = PFS.openFileForRead("/a");
937   ASSERT_FALSE(File.getError());
938   EXPECT_EQ("test", (*(*File)->getBuffer("ignored"))->getBuffer());
939 
940   std::error_code EC;
941   vfs::directory_iterator I = PFS.dir_begin("/", EC);
942   ASSERT_FALSE(EC);
943   ASSERT_EQ("/a", I->path());
944   I.increment(EC);
945   ASSERT_FALSE(EC);
946   ASSERT_EQ(vfs::directory_iterator(), I);
947 
948   ASSERT_FALSE(PFS.setCurrentWorkingDirectory("/"));
949 
950   auto PWD = PFS.getCurrentWorkingDirectory();
951   ASSERT_FALSE(PWD.getError());
952   ASSERT_EQ("/", getPosixPath(*PWD));
953 
954   SmallString<16> Path;
955   ASSERT_FALSE(PFS.getRealPath("a", Path));
956   ASSERT_EQ("/a", getPosixPath(Path));
957 
958   bool Local = true;
959   ASSERT_FALSE(PFS.isLocal("/a", Local));
960   EXPECT_FALSE(Local);
961 }
962 
963 class InMemoryFileSystemTest : public ::testing::Test {
964 protected:
965   llvm::vfs::InMemoryFileSystem FS;
966   llvm::vfs::InMemoryFileSystem NormalizedFS;
967 
968   InMemoryFileSystemTest()
969       : FS(/*UseNormalizedPaths=*/false),
970         NormalizedFS(/*UseNormalizedPaths=*/true) {}
971 };
972 
973 MATCHER_P2(IsHardLinkTo, FS, Target, "") {
974   StringRef From = arg;
975   StringRef To = Target;
976   auto OpenedFrom = FS->openFileForRead(From);
977   auto OpenedTo = FS->openFileForRead(To);
978   return !OpenedFrom.getError() && !OpenedTo.getError() &&
979          (*OpenedFrom)->status()->getUniqueID() ==
980              (*OpenedTo)->status()->getUniqueID();
981 }
982 
983 TEST_F(InMemoryFileSystemTest, IsEmpty) {
984   auto Stat = FS.status("/a");
985   ASSERT_EQ(Stat.getError(), errc::no_such_file_or_directory) << FS.toString();
986   Stat = FS.status("/");
987   ASSERT_EQ(Stat.getError(), errc::no_such_file_or_directory) << FS.toString();
988 }
989 
990 TEST_F(InMemoryFileSystemTest, WindowsPath) {
991   FS.addFile("c:/windows/system128/foo.cpp", 0, MemoryBuffer::getMemBuffer(""));
992   auto Stat = FS.status("c:");
993 #if !defined(_WIN32)
994   ASSERT_FALSE(Stat.getError()) << Stat.getError() << FS.toString();
995 #endif
996   Stat = FS.status("c:/windows/system128/foo.cpp");
997   ASSERT_FALSE(Stat.getError()) << Stat.getError() << FS.toString();
998   FS.addFile("d:/windows/foo.cpp", 0, MemoryBuffer::getMemBuffer(""));
999   Stat = FS.status("d:/windows/foo.cpp");
1000   ASSERT_FALSE(Stat.getError()) << Stat.getError() << FS.toString();
1001 }
1002 
1003 TEST_F(InMemoryFileSystemTest, OverlayFile) {
1004   FS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a"));
1005   NormalizedFS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a"));
1006   auto Stat = FS.status("/");
1007   ASSERT_FALSE(Stat.getError()) << Stat.getError() << FS.toString();
1008   Stat = FS.status("/.");
1009   ASSERT_FALSE(Stat);
1010   Stat = NormalizedFS.status("/.");
1011   ASSERT_FALSE(Stat.getError()) << Stat.getError() << FS.toString();
1012   Stat = FS.status("/a");
1013   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
1014   ASSERT_EQ("/a", Stat->getName());
1015 }
1016 
1017 TEST_F(InMemoryFileSystemTest, OverlayFileNoOwn) {
1018   auto Buf = MemoryBuffer::getMemBuffer("a");
1019   FS.addFileNoOwn("/a", 0, *Buf);
1020   auto Stat = FS.status("/a");
1021   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
1022   ASSERT_EQ("/a", Stat->getName());
1023 }
1024 
1025 TEST_F(InMemoryFileSystemTest, OpenFileForRead) {
1026   FS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a"));
1027   FS.addFile("././c", 0, MemoryBuffer::getMemBuffer("c"));
1028   FS.addFile("./d/../d", 0, MemoryBuffer::getMemBuffer("d"));
1029   NormalizedFS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a"));
1030   NormalizedFS.addFile("././c", 0, MemoryBuffer::getMemBuffer("c"));
1031   NormalizedFS.addFile("./d/../d", 0, MemoryBuffer::getMemBuffer("d"));
1032   auto File = FS.openFileForRead("/a");
1033   ASSERT_EQ("a", (*(*File)->getBuffer("ignored"))->getBuffer());
1034   File = FS.openFileForRead("/a"); // Open again.
1035   ASSERT_EQ("a", (*(*File)->getBuffer("ignored"))->getBuffer());
1036   File = NormalizedFS.openFileForRead("/././a"); // Open again.
1037   ASSERT_EQ("a", (*(*File)->getBuffer("ignored"))->getBuffer());
1038   File = FS.openFileForRead("/");
1039   ASSERT_EQ(File.getError(), errc::invalid_argument) << FS.toString();
1040   File = FS.openFileForRead("/b");
1041   ASSERT_EQ(File.getError(), errc::no_such_file_or_directory) << FS.toString();
1042   File = FS.openFileForRead("./c");
1043   ASSERT_FALSE(File);
1044   File = FS.openFileForRead("e/../d");
1045   ASSERT_FALSE(File);
1046   File = NormalizedFS.openFileForRead("./c");
1047   ASSERT_EQ("c", (*(*File)->getBuffer("ignored"))->getBuffer());
1048   File = NormalizedFS.openFileForRead("e/../d");
1049   ASSERT_EQ("d", (*(*File)->getBuffer("ignored"))->getBuffer());
1050 }
1051 
1052 TEST_F(InMemoryFileSystemTest, DuplicatedFile) {
1053   ASSERT_TRUE(FS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a")));
1054   ASSERT_FALSE(FS.addFile("/a/b", 0, MemoryBuffer::getMemBuffer("a")));
1055   ASSERT_TRUE(FS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a")));
1056   ASSERT_FALSE(FS.addFile("/a", 0, MemoryBuffer::getMemBuffer("b")));
1057 }
1058 
1059 TEST_F(InMemoryFileSystemTest, DirectoryIteration) {
1060   FS.addFile("/a", 0, MemoryBuffer::getMemBuffer(""));
1061   FS.addFile("/b/c", 0, MemoryBuffer::getMemBuffer(""));
1062 
1063   std::error_code EC;
1064   vfs::directory_iterator I = FS.dir_begin("/", EC);
1065   ASSERT_FALSE(EC);
1066   ASSERT_EQ("/a", I->path());
1067   I.increment(EC);
1068   ASSERT_FALSE(EC);
1069   ASSERT_EQ("/b", I->path());
1070   I.increment(EC);
1071   ASSERT_FALSE(EC);
1072   ASSERT_EQ(vfs::directory_iterator(), I);
1073 
1074   I = FS.dir_begin("/b", EC);
1075   ASSERT_FALSE(EC);
1076   // When on Windows, we end up with "/b\\c" as the name.  Convert to Posix
1077   // path for the sake of the comparison.
1078   ASSERT_EQ("/b/c", getPosixPath(std::string(I->path())));
1079   I.increment(EC);
1080   ASSERT_FALSE(EC);
1081   ASSERT_EQ(vfs::directory_iterator(), I);
1082 }
1083 
1084 TEST_F(InMemoryFileSystemTest, WorkingDirectory) {
1085   FS.setCurrentWorkingDirectory("/b");
1086   FS.addFile("c", 0, MemoryBuffer::getMemBuffer(""));
1087 
1088   auto Stat = FS.status("/b/c");
1089   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
1090   ASSERT_EQ("/b/c", Stat->getName());
1091   ASSERT_EQ("/b", *FS.getCurrentWorkingDirectory());
1092 
1093   Stat = FS.status("c");
1094   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
1095 
1096   NormalizedFS.setCurrentWorkingDirectory("/b/c");
1097   NormalizedFS.setCurrentWorkingDirectory(".");
1098   ASSERT_EQ("/b/c",
1099             getPosixPath(NormalizedFS.getCurrentWorkingDirectory().get()));
1100   NormalizedFS.setCurrentWorkingDirectory("..");
1101   ASSERT_EQ("/b",
1102             getPosixPath(NormalizedFS.getCurrentWorkingDirectory().get()));
1103 }
1104 
1105 TEST_F(InMemoryFileSystemTest, IsLocal) {
1106   FS.setCurrentWorkingDirectory("/b");
1107   FS.addFile("c", 0, MemoryBuffer::getMemBuffer(""));
1108 
1109   std::error_code EC;
1110   bool IsLocal = true;
1111   EC = FS.isLocal("c", IsLocal);
1112   ASSERT_FALSE(EC);
1113   ASSERT_FALSE(IsLocal);
1114 }
1115 
1116 #if !defined(_WIN32)
1117 TEST_F(InMemoryFileSystemTest, GetRealPath) {
1118   SmallString<16> Path;
1119   EXPECT_EQ(FS.getRealPath("b", Path), errc::operation_not_permitted);
1120 
1121   auto GetRealPath = [this](StringRef P) {
1122     SmallString<16> Output;
1123     auto EC = FS.getRealPath(P, Output);
1124     EXPECT_FALSE(EC);
1125     return std::string(Output);
1126   };
1127 
1128   FS.setCurrentWorkingDirectory("a");
1129   EXPECT_EQ(GetRealPath("b"), "a/b");
1130   EXPECT_EQ(GetRealPath("../b"), "b");
1131   EXPECT_EQ(GetRealPath("b/./c"), "a/b/c");
1132 
1133   FS.setCurrentWorkingDirectory("/a");
1134   EXPECT_EQ(GetRealPath("b"), "/a/b");
1135   EXPECT_EQ(GetRealPath("../b"), "/b");
1136   EXPECT_EQ(GetRealPath("b/./c"), "/a/b/c");
1137 }
1138 #endif // _WIN32
1139 
1140 TEST_F(InMemoryFileSystemTest, AddFileWithUser) {
1141   FS.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"), 0xFEEDFACE);
1142   auto Stat = FS.status("/a");
1143   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
1144   ASSERT_TRUE(Stat->isDirectory());
1145   ASSERT_EQ(0xFEEDFACE, Stat->getUser());
1146   Stat = FS.status("/a/b");
1147   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
1148   ASSERT_TRUE(Stat->isDirectory());
1149   ASSERT_EQ(0xFEEDFACE, Stat->getUser());
1150   Stat = FS.status("/a/b/c");
1151   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
1152   ASSERT_TRUE(Stat->isRegularFile());
1153   ASSERT_EQ(sys::fs::perms::all_all, Stat->getPermissions());
1154   ASSERT_EQ(0xFEEDFACE, Stat->getUser());
1155 }
1156 
1157 TEST_F(InMemoryFileSystemTest, AddFileWithGroup) {
1158   FS.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"), std::nullopt,
1159              0xDABBAD00);
1160   auto Stat = FS.status("/a");
1161   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
1162   ASSERT_TRUE(Stat->isDirectory());
1163   ASSERT_EQ(0xDABBAD00, Stat->getGroup());
1164   Stat = FS.status("/a/b");
1165   ASSERT_TRUE(Stat->isDirectory());
1166   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
1167   ASSERT_EQ(0xDABBAD00, Stat->getGroup());
1168   Stat = FS.status("/a/b/c");
1169   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
1170   ASSERT_TRUE(Stat->isRegularFile());
1171   ASSERT_EQ(sys::fs::perms::all_all, Stat->getPermissions());
1172   ASSERT_EQ(0xDABBAD00, Stat->getGroup());
1173 }
1174 
1175 TEST_F(InMemoryFileSystemTest, AddFileWithFileType) {
1176   FS.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"), std::nullopt,
1177              std::nullopt, sys::fs::file_type::socket_file);
1178   auto Stat = FS.status("/a");
1179   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
1180   ASSERT_TRUE(Stat->isDirectory());
1181   Stat = FS.status("/a/b");
1182   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
1183   ASSERT_TRUE(Stat->isDirectory());
1184   Stat = FS.status("/a/b/c");
1185   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
1186   ASSERT_EQ(sys::fs::file_type::socket_file, Stat->getType());
1187   ASSERT_EQ(sys::fs::perms::all_all, Stat->getPermissions());
1188 }
1189 
1190 TEST_F(InMemoryFileSystemTest, AddFileWithPerms) {
1191   FS.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"), std::nullopt,
1192              std::nullopt, std::nullopt,
1193              sys::fs::perms::owner_read | sys::fs::perms::owner_write);
1194   auto Stat = FS.status("/a");
1195   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
1196   ASSERT_TRUE(Stat->isDirectory());
1197   ASSERT_EQ(sys::fs::perms::owner_read | sys::fs::perms::owner_write |
1198                 sys::fs::perms::owner_exe,
1199             Stat->getPermissions());
1200   Stat = FS.status("/a/b");
1201   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
1202   ASSERT_TRUE(Stat->isDirectory());
1203   ASSERT_EQ(sys::fs::perms::owner_read | sys::fs::perms::owner_write |
1204                 sys::fs::perms::owner_exe,
1205             Stat->getPermissions());
1206   Stat = FS.status("/a/b/c");
1207   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
1208   ASSERT_TRUE(Stat->isRegularFile());
1209   ASSERT_EQ(sys::fs::perms::owner_read | sys::fs::perms::owner_write,
1210             Stat->getPermissions());
1211 }
1212 
1213 TEST_F(InMemoryFileSystemTest, AddDirectoryThenAddChild) {
1214   FS.addFile("/a", 0, MemoryBuffer::getMemBuffer(""), /*User=*/std::nullopt,
1215              /*Group=*/std::nullopt, sys::fs::file_type::directory_file);
1216   FS.addFile("/a/b", 0, MemoryBuffer::getMemBuffer("abc"),
1217              /*User=*/std::nullopt,
1218              /*Group=*/std::nullopt, sys::fs::file_type::regular_file);
1219   auto Stat = FS.status("/a");
1220   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
1221   ASSERT_TRUE(Stat->isDirectory());
1222   Stat = FS.status("/a/b");
1223   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
1224   ASSERT_TRUE(Stat->isRegularFile());
1225 }
1226 
1227 // Test that the name returned by status() is in the same form as the path that
1228 // was requested (to match the behavior of RealFileSystem).
1229 TEST_F(InMemoryFileSystemTest, StatusName) {
1230   NormalizedFS.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"),
1231                        /*User=*/std::nullopt,
1232                        /*Group=*/std::nullopt,
1233                        sys::fs::file_type::regular_file);
1234   NormalizedFS.setCurrentWorkingDirectory("/a/b");
1235 
1236   // Access using InMemoryFileSystem::status.
1237   auto Stat = NormalizedFS.status("../b/c");
1238   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n"
1239                                 << NormalizedFS.toString();
1240   ASSERT_TRUE(Stat->isRegularFile());
1241   ASSERT_EQ("../b/c", Stat->getName());
1242 
1243   // Access using InMemoryFileAdaptor::status.
1244   auto File = NormalizedFS.openFileForRead("../b/c");
1245   ASSERT_FALSE(File.getError()) << File.getError() << "\n"
1246                                 << NormalizedFS.toString();
1247   Stat = (*File)->status();
1248   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n"
1249                                 << NormalizedFS.toString();
1250   ASSERT_TRUE(Stat->isRegularFile());
1251   ASSERT_EQ("../b/c", Stat->getName());
1252 
1253   // Access using a directory iterator.
1254   std::error_code EC;
1255   llvm::vfs::directory_iterator It = NormalizedFS.dir_begin("../b", EC);
1256   // When on Windows, we end up with "../b\\c" as the name.  Convert to Posix
1257   // path for the sake of the comparison.
1258   ASSERT_EQ("../b/c", getPosixPath(std::string(It->path())));
1259 }
1260 
1261 TEST_F(InMemoryFileSystemTest, AddHardLinkToFile) {
1262   StringRef FromLink = "/path/to/FROM/link";
1263   StringRef Target = "/path/to/TO/file";
1264   FS.addFile(Target, 0, MemoryBuffer::getMemBuffer("content of target"));
1265   EXPECT_TRUE(FS.addHardLink(FromLink, Target));
1266   EXPECT_THAT(FromLink, IsHardLinkTo(&FS, Target));
1267   EXPECT_EQ(FS.status(FromLink)->getSize(), FS.status(Target)->getSize());
1268   EXPECT_EQ(FS.getBufferForFile(FromLink)->get()->getBuffer(),
1269             FS.getBufferForFile(Target)->get()->getBuffer());
1270 }
1271 
1272 TEST_F(InMemoryFileSystemTest, AddHardLinkInChainPattern) {
1273   StringRef Link0 = "/path/to/0/link";
1274   StringRef Link1 = "/path/to/1/link";
1275   StringRef Link2 = "/path/to/2/link";
1276   StringRef Target = "/path/to/target";
1277   FS.addFile(Target, 0, MemoryBuffer::getMemBuffer("content of target file"));
1278   EXPECT_TRUE(FS.addHardLink(Link2, Target));
1279   EXPECT_TRUE(FS.addHardLink(Link1, Link2));
1280   EXPECT_TRUE(FS.addHardLink(Link0, Link1));
1281   EXPECT_THAT(Link0, IsHardLinkTo(&FS, Target));
1282   EXPECT_THAT(Link1, IsHardLinkTo(&FS, Target));
1283   EXPECT_THAT(Link2, IsHardLinkTo(&FS, Target));
1284 }
1285 
1286 TEST_F(InMemoryFileSystemTest, AddHardLinkToAFileThatWasNotAddedBefore) {
1287   EXPECT_FALSE(FS.addHardLink("/path/to/link", "/path/to/target"));
1288 }
1289 
1290 TEST_F(InMemoryFileSystemTest, AddHardLinkFromAFileThatWasAddedBefore) {
1291   StringRef Link = "/path/to/link";
1292   StringRef Target = "/path/to/target";
1293   FS.addFile(Target, 0, MemoryBuffer::getMemBuffer("content of target"));
1294   FS.addFile(Link, 0, MemoryBuffer::getMemBuffer("content of link"));
1295   EXPECT_FALSE(FS.addHardLink(Link, Target));
1296 }
1297 
1298 TEST_F(InMemoryFileSystemTest, AddSameHardLinkMoreThanOnce) {
1299   StringRef Link = "/path/to/link";
1300   StringRef Target = "/path/to/target";
1301   FS.addFile(Target, 0, MemoryBuffer::getMemBuffer("content of target"));
1302   EXPECT_TRUE(FS.addHardLink(Link, Target));
1303   EXPECT_FALSE(FS.addHardLink(Link, Target));
1304 }
1305 
1306 TEST_F(InMemoryFileSystemTest, AddFileInPlaceOfAHardLinkWithSameContent) {
1307   StringRef Link = "/path/to/link";
1308   StringRef Target = "/path/to/target";
1309   StringRef Content = "content of target";
1310   EXPECT_TRUE(FS.addFile(Target, 0, MemoryBuffer::getMemBuffer(Content)));
1311   EXPECT_TRUE(FS.addHardLink(Link, Target));
1312   EXPECT_TRUE(FS.addFile(Link, 0, MemoryBuffer::getMemBuffer(Content)));
1313 }
1314 
1315 TEST_F(InMemoryFileSystemTest, AddFileInPlaceOfAHardLinkWithDifferentContent) {
1316   StringRef Link = "/path/to/link";
1317   StringRef Target = "/path/to/target";
1318   StringRef Content = "content of target";
1319   StringRef LinkContent = "different content of link";
1320   EXPECT_TRUE(FS.addFile(Target, 0, MemoryBuffer::getMemBuffer(Content)));
1321   EXPECT_TRUE(FS.addHardLink(Link, Target));
1322   EXPECT_FALSE(FS.addFile(Link, 0, MemoryBuffer::getMemBuffer(LinkContent)));
1323 }
1324 
1325 TEST_F(InMemoryFileSystemTest, AddHardLinkToADirectory) {
1326   StringRef Dir = "path/to/dummy/dir";
1327   StringRef Link = "/path/to/link";
1328   StringRef File = "path/to/dummy/dir/target";
1329   StringRef Content = "content of target";
1330   EXPECT_TRUE(FS.addFile(File, 0, MemoryBuffer::getMemBuffer(Content)));
1331   EXPECT_FALSE(FS.addHardLink(Link, Dir));
1332 }
1333 
1334 TEST_F(InMemoryFileSystemTest, AddHardLinkToASymlink) {
1335   EXPECT_TRUE(FS.addFile("/file", 0, MemoryBuffer::getMemBuffer("content")));
1336   EXPECT_TRUE(FS.addSymbolicLink("/symlink", "/file", 0));
1337   EXPECT_TRUE(FS.addHardLink("/hardlink", "/symlink"));
1338   EXPECT_EQ((*FS.getBufferForFile("/hardlink"))->getBuffer(), "content");
1339 }
1340 
1341 TEST_F(InMemoryFileSystemTest, AddHardLinkFromADirectory) {
1342   StringRef Dir = "path/to/dummy/dir";
1343   StringRef Target = "path/to/dummy/dir/target";
1344   StringRef Content = "content of target";
1345   EXPECT_TRUE(FS.addFile(Target, 0, MemoryBuffer::getMemBuffer(Content)));
1346   EXPECT_FALSE(FS.addHardLink(Dir, Target));
1347 }
1348 
1349 TEST_F(InMemoryFileSystemTest, AddHardLinkUnderAFile) {
1350   StringRef CommonContent = "content string";
1351   FS.addFile("/a/b", 0, MemoryBuffer::getMemBuffer(CommonContent));
1352   FS.addFile("/c/d", 0, MemoryBuffer::getMemBuffer(CommonContent));
1353   EXPECT_FALSE(FS.addHardLink("/c/d/e", "/a/b"));
1354 }
1355 
1356 TEST_F(InMemoryFileSystemTest, RecursiveIterationWithHardLink) {
1357   std::error_code EC;
1358   FS.addFile("/a/b", 0, MemoryBuffer::getMemBuffer("content string"));
1359   EXPECT_TRUE(FS.addHardLink("/c/d", "/a/b"));
1360   auto I = vfs::recursive_directory_iterator(FS, "/", EC);
1361   ASSERT_FALSE(EC);
1362   std::vector<std::string> Nodes;
1363   for (auto E = vfs::recursive_directory_iterator(); !EC && I != E;
1364        I.increment(EC)) {
1365     Nodes.push_back(getPosixPath(std::string(I->path())));
1366   }
1367   EXPECT_THAT(Nodes, testing::UnorderedElementsAre("/a", "/a/b", "/c", "/c/d"));
1368 }
1369 
1370 TEST_F(InMemoryFileSystemTest, UniqueID) {
1371   ASSERT_TRUE(FS.addFile("/a/b", 0, MemoryBuffer::getMemBuffer("text")));
1372   ASSERT_TRUE(FS.addFile("/c/d", 0, MemoryBuffer::getMemBuffer("text")));
1373   ASSERT_TRUE(FS.addHardLink("/e/f", "/a/b"));
1374 
1375   EXPECT_EQ(FS.status("/a/b")->getUniqueID(), FS.status("/a/b")->getUniqueID());
1376   EXPECT_NE(FS.status("/a/b")->getUniqueID(), FS.status("/c/d")->getUniqueID());
1377   EXPECT_EQ(FS.status("/a/b")->getUniqueID(), FS.status("/e/f")->getUniqueID());
1378   EXPECT_EQ(FS.status("/a")->getUniqueID(), FS.status("/a")->getUniqueID());
1379   EXPECT_NE(FS.status("/a")->getUniqueID(), FS.status("/c")->getUniqueID());
1380   EXPECT_NE(FS.status("/a")->getUniqueID(), FS.status("/e")->getUniqueID());
1381 
1382   // Recreating the "same" FS yields the same UniqueIDs.
1383   // Note: FS2 should match FS with respect to path normalization.
1384   vfs::InMemoryFileSystem FS2(/*UseNormalizedPath=*/false);
1385   ASSERT_TRUE(FS2.addFile("/a/b", 0, MemoryBuffer::getMemBuffer("text")));
1386   EXPECT_EQ(FS.status("/a/b")->getUniqueID(),
1387             FS2.status("/a/b")->getUniqueID());
1388   EXPECT_EQ(FS.status("/a")->getUniqueID(), FS2.status("/a")->getUniqueID());
1389 }
1390 
1391 TEST_F(InMemoryFileSystemTest, AddSymlinkToAFile) {
1392   EXPECT_TRUE(
1393       FS.addFile("/some/file", 0, MemoryBuffer::getMemBuffer("contents")));
1394   EXPECT_TRUE(FS.addSymbolicLink("/other/file/link", "/some/file", 0));
1395   ErrorOr<vfs::Status> Stat = FS.status("/some/file");
1396   EXPECT_TRUE(Stat->isRegularFile());
1397 }
1398 
1399 TEST_F(InMemoryFileSystemTest, AddSymlinkToADirectory) {
1400   EXPECT_TRUE(FS.addSymbolicLink("/link", "/target", 0));
1401   EXPECT_TRUE(
1402       FS.addFile("/target/foo.h", 0, MemoryBuffer::getMemBuffer("foo")));
1403   ErrorOr<vfs::Status> Stat = FS.status("/link/foo.h");
1404   EXPECT_TRUE(Stat);
1405   EXPECT_EQ((*Stat).getName(), "/link/foo.h");
1406   EXPECT_TRUE(Stat->isRegularFile());
1407 }
1408 
1409 TEST_F(InMemoryFileSystemTest, AddSymlinkToASymlink) {
1410   EXPECT_TRUE(FS.addSymbolicLink("/first", "/second", 0));
1411   EXPECT_TRUE(FS.addSymbolicLink("/second", "/third", 0));
1412   EXPECT_TRUE(FS.addFile("/third", 0, MemoryBuffer::getMemBuffer("")));
1413   ErrorOr<vfs::Status> Stat = FS.status("/first");
1414   EXPECT_TRUE(Stat);
1415   EXPECT_EQ((*Stat).getName(), "/first");
1416   // Follow-through symlinks by default. This matches RealFileSystem's
1417   // semantics.
1418   EXPECT_TRUE(Stat->isRegularFile());
1419   Stat = FS.status("/second");
1420   EXPECT_TRUE(Stat);
1421   EXPECT_EQ((*Stat).getName(), "/second");
1422   EXPECT_TRUE(Stat->isRegularFile());
1423   Stat = FS.status("/third");
1424   EXPECT_TRUE(Stat);
1425   EXPECT_EQ((*Stat).getName(), "/third");
1426   EXPECT_TRUE(Stat->isRegularFile());
1427 }
1428 
1429 TEST_F(InMemoryFileSystemTest, AddRecursiveSymlink) {
1430   EXPECT_TRUE(FS.addSymbolicLink("/link-a", "/link-b", 0));
1431   EXPECT_TRUE(FS.addSymbolicLink("/link-b", "/link-a", 0));
1432   ErrorOr<vfs::Status> Stat = FS.status("/link-a/foo");
1433   EXPECT_FALSE(Stat);
1434   EXPECT_EQ(Stat.getError(), errc::no_such_file_or_directory);
1435 }
1436 
1437 TEST_F(InMemoryFileSystemTest, DirectoryIteratorWithSymlinkToAFile) {
1438   std::error_code EC;
1439 
1440   EXPECT_TRUE(FS.addFile("/file", 0, MemoryBuffer::getMemBuffer("")));
1441   EXPECT_TRUE(FS.addSymbolicLink("/symlink", "/file", 0));
1442 
1443   vfs::directory_iterator I = FS.dir_begin("/", EC), E;
1444   ASSERT_FALSE(EC);
1445 
1446   std::vector<std::string> Nodes;
1447   for (; !EC && I != E; I.increment(EC))
1448     Nodes.push_back(getPosixPath(std::string(I->path())));
1449 
1450   EXPECT_THAT(Nodes, testing::UnorderedElementsAre("/file", "/file"));
1451 }
1452 
1453 TEST_F(InMemoryFileSystemTest, RecursiveDirectoryIteratorWithSymlinkToADir) {
1454   std::error_code EC;
1455 
1456   EXPECT_TRUE(FS.addFile("/dir/file", 0, MemoryBuffer::getMemBuffer("")));
1457   EXPECT_TRUE(FS.addSymbolicLink("/dir_symlink", "/dir", 0));
1458 
1459   vfs::recursive_directory_iterator I(FS, "/", EC), E;
1460   ASSERT_FALSE(EC);
1461 
1462   std::vector<std::string> Nodes;
1463   for (; !EC && I != E; I.increment(EC))
1464     Nodes.push_back(getPosixPath(std::string(I->path())));
1465 
1466   EXPECT_THAT(Nodes, testing::UnorderedElementsAre("/dir", "/dir/file", "/dir",
1467                                                    "/dir/file"));
1468 }
1469 
1470 // NOTE: in the tests below, we use '//root/' as our root directory, since it is
1471 // a legal *absolute* path on Windows as well as *nix.
1472 class VFSFromYAMLTest : public ::testing::Test {
1473 public:
1474   int NumDiagnostics;
1475 
1476   void SetUp() override { NumDiagnostics = 0; }
1477 
1478   static void CountingDiagHandler(const SMDiagnostic &, void *Context) {
1479     VFSFromYAMLTest *Test = static_cast<VFSFromYAMLTest *>(Context);
1480     ++Test->NumDiagnostics;
1481   }
1482 
1483   std::unique_ptr<vfs::FileSystem>
1484   getFromYAMLRawString(StringRef Content,
1485                        IntrusiveRefCntPtr<vfs::FileSystem> ExternalFS,
1486                        StringRef YAMLFilePath = "") {
1487     std::unique_ptr<MemoryBuffer> Buffer = MemoryBuffer::getMemBuffer(Content);
1488     return getVFSFromYAML(std::move(Buffer), CountingDiagHandler, YAMLFilePath,
1489                           this, ExternalFS);
1490   }
1491 
1492   std::unique_ptr<vfs::FileSystem> getFromYAMLString(
1493       StringRef Content,
1494       IntrusiveRefCntPtr<vfs::FileSystem> ExternalFS = new DummyFileSystem(),
1495       StringRef YAMLFilePath = "") {
1496     std::string VersionPlusContent("{\n  'version':0,\n");
1497     VersionPlusContent += Content.slice(Content.find('{') + 1, StringRef::npos);
1498     return getFromYAMLRawString(VersionPlusContent, ExternalFS, YAMLFilePath);
1499   }
1500 
1501   // This is intended as a "XFAIL" for windows hosts.
1502   bool supportsSameDirMultipleYAMLEntries() {
1503     Triple Host(Triple::normalize(sys::getProcessTriple()));
1504     return !Host.isOSWindows();
1505   }
1506 };
1507 
1508 TEST_F(VFSFromYAMLTest, BasicVFSFromYAML) {
1509   IntrusiveRefCntPtr<vfs::FileSystem> FS;
1510   FS = getFromYAMLString("");
1511   EXPECT_EQ(nullptr, FS.get());
1512   FS = getFromYAMLString("[]");
1513   EXPECT_EQ(nullptr, FS.get());
1514   FS = getFromYAMLString("'string'");
1515   EXPECT_EQ(nullptr, FS.get());
1516   EXPECT_EQ(3, NumDiagnostics);
1517 }
1518 
1519 TEST_F(VFSFromYAMLTest, MappedFiles) {
1520   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
1521   Lower->addDirectory("//root/foo/bar");
1522   Lower->addRegularFile("//root/foo/bar/a");
1523   IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
1524       "{ 'roots': [\n"
1525       "{\n"
1526       "  'type': 'directory',\n"
1527       "  'name': '//root/',\n"
1528       "  'contents': [ {\n"
1529       "                  'type': 'file',\n"
1530       "                  'name': 'file1',\n"
1531       "                  'external-contents': '//root/foo/bar/a'\n"
1532       "                },\n"
1533       "                {\n"
1534       "                  'type': 'file',\n"
1535       "                  'name': 'file2',\n"
1536       "                  'external-contents': '//root/foo/b'\n"
1537       "                },\n"
1538       "                {\n"
1539       "                  'type': 'directory-remap',\n"
1540       "                  'name': 'mappeddir',\n"
1541       "                  'external-contents': '//root/foo/bar'\n"
1542       "                },\n"
1543       "                {\n"
1544       "                  'type': 'directory-remap',\n"
1545       "                  'name': 'mappeddir2',\n"
1546       "                  'use-external-name': false,\n"
1547       "                  'external-contents': '//root/foo/bar'\n"
1548       "                }\n"
1549       "              ]\n"
1550       "}\n"
1551       "]\n"
1552       "}",
1553       Lower);
1554   ASSERT_NE(FS.get(), nullptr);
1555 
1556   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
1557       new vfs::OverlayFileSystem(Lower));
1558   O->pushOverlay(FS);
1559 
1560   // file
1561   ErrorOr<vfs::Status> S = O->status("//root/file1");
1562   ASSERT_FALSE(S.getError());
1563   EXPECT_EQ("//root/foo/bar/a", S->getName());
1564   EXPECT_TRUE(S->IsVFSMapped);
1565   EXPECT_TRUE(S->ExposesExternalVFSPath);
1566 
1567   ErrorOr<vfs::Status> SLower = O->status("//root/foo/bar/a");
1568   EXPECT_EQ("//root/foo/bar/a", SLower->getName());
1569   EXPECT_TRUE(S->equivalent(*SLower));
1570   EXPECT_FALSE(SLower->IsVFSMapped);
1571   EXPECT_FALSE(SLower->ExposesExternalVFSPath);
1572 
1573   // file after opening
1574   auto OpenedF = O->openFileForRead("//root/file1");
1575   ASSERT_FALSE(OpenedF.getError());
1576   auto OpenedS = (*OpenedF)->status();
1577   ASSERT_FALSE(OpenedS.getError());
1578   EXPECT_EQ("//root/foo/bar/a", OpenedS->getName());
1579   EXPECT_TRUE(OpenedS->IsVFSMapped);
1580   EXPECT_TRUE(OpenedS->ExposesExternalVFSPath);
1581 
1582   // directory
1583   S = O->status("//root/");
1584   ASSERT_FALSE(S.getError());
1585   EXPECT_TRUE(S->isDirectory());
1586   EXPECT_TRUE(S->equivalent(*O->status("//root/"))); // non-volatile UniqueID
1587 
1588   // remapped directory
1589   S = O->status("//root/mappeddir");
1590   ASSERT_FALSE(S.getError());
1591   EXPECT_TRUE(S->isDirectory());
1592   EXPECT_TRUE(S->IsVFSMapped);
1593   EXPECT_TRUE(S->ExposesExternalVFSPath);
1594   EXPECT_TRUE(S->equivalent(*O->status("//root/foo/bar")));
1595 
1596   SLower = O->status("//root/foo/bar");
1597   EXPECT_EQ("//root/foo/bar", SLower->getName());
1598   EXPECT_TRUE(S->equivalent(*SLower));
1599   EXPECT_FALSE(SLower->IsVFSMapped);
1600   EXPECT_FALSE(SLower->ExposesExternalVFSPath);
1601 
1602   // file in remapped directory
1603   S = O->status("//root/mappeddir/a");
1604   ASSERT_FALSE(S.getError());
1605   EXPECT_FALSE(S->isDirectory());
1606   EXPECT_TRUE(S->IsVFSMapped);
1607   EXPECT_TRUE(S->ExposesExternalVFSPath);
1608   EXPECT_EQ("//root/foo/bar/a", S->getName());
1609 
1610   // file in remapped directory, with use-external-name=false
1611   S = O->status("//root/mappeddir2/a");
1612   ASSERT_FALSE(S.getError());
1613   EXPECT_FALSE(S->isDirectory());
1614   EXPECT_TRUE(S->IsVFSMapped);
1615   EXPECT_FALSE(S->ExposesExternalVFSPath);
1616   EXPECT_EQ("//root/mappeddir2/a", S->getName());
1617 
1618   // file contents in remapped directory
1619   OpenedF = O->openFileForRead("//root/mappeddir/a");
1620   ASSERT_FALSE(OpenedF.getError());
1621   OpenedS = (*OpenedF)->status();
1622   ASSERT_FALSE(OpenedS.getError());
1623   EXPECT_EQ("//root/foo/bar/a", OpenedS->getName());
1624   EXPECT_TRUE(OpenedS->IsVFSMapped);
1625   EXPECT_TRUE(OpenedS->ExposesExternalVFSPath);
1626 
1627   // file contents in remapped directory, with use-external-name=false
1628   OpenedF = O->openFileForRead("//root/mappeddir2/a");
1629   ASSERT_FALSE(OpenedF.getError());
1630   OpenedS = (*OpenedF)->status();
1631   ASSERT_FALSE(OpenedS.getError());
1632   EXPECT_EQ("//root/mappeddir2/a", OpenedS->getName());
1633   EXPECT_TRUE(OpenedS->IsVFSMapped);
1634   EXPECT_FALSE(OpenedS->ExposesExternalVFSPath);
1635 
1636   // broken mapping
1637   EXPECT_EQ(O->status("//root/file2").getError(),
1638             llvm::errc::no_such_file_or_directory);
1639   EXPECT_EQ(0, NumDiagnostics);
1640 }
1641 
1642 TEST_F(VFSFromYAMLTest, MappedRoot) {
1643   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
1644   Lower->addDirectory("//root/foo/bar");
1645   Lower->addRegularFile("//root/foo/bar/a");
1646   IntrusiveRefCntPtr<vfs::FileSystem> FS =
1647       getFromYAMLString("{ 'roots': [\n"
1648                         "{\n"
1649                         "  'type': 'directory-remap',\n"
1650                         "  'name': '//mappedroot/',\n"
1651                         "  'external-contents': '//root/foo/bar'\n"
1652                         "}\n"
1653                         "]\n"
1654                         "}",
1655                         Lower);
1656   ASSERT_NE(FS.get(), nullptr);
1657 
1658   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
1659       new vfs::OverlayFileSystem(Lower));
1660   O->pushOverlay(FS);
1661 
1662   // file
1663   ErrorOr<vfs::Status> S = O->status("//mappedroot/a");
1664   ASSERT_FALSE(S.getError());
1665   EXPECT_EQ("//root/foo/bar/a", S->getName());
1666   EXPECT_TRUE(S->IsVFSMapped);
1667   EXPECT_TRUE(S->ExposesExternalVFSPath);
1668 
1669   ErrorOr<vfs::Status> SLower = O->status("//root/foo/bar/a");
1670   EXPECT_EQ("//root/foo/bar/a", SLower->getName());
1671   EXPECT_TRUE(S->equivalent(*SLower));
1672   EXPECT_FALSE(SLower->IsVFSMapped);
1673   EXPECT_FALSE(SLower->ExposesExternalVFSPath);
1674 
1675   // file after opening
1676   auto OpenedF = O->openFileForRead("//mappedroot/a");
1677   ASSERT_FALSE(OpenedF.getError());
1678   auto OpenedS = (*OpenedF)->status();
1679   ASSERT_FALSE(OpenedS.getError());
1680   EXPECT_EQ("//root/foo/bar/a", OpenedS->getName());
1681   EXPECT_TRUE(OpenedS->IsVFSMapped);
1682   EXPECT_TRUE(OpenedS->ExposesExternalVFSPath);
1683 
1684   EXPECT_EQ(0, NumDiagnostics);
1685 }
1686 
1687 TEST_F(VFSFromYAMLTest, RemappedDirectoryOverlay) {
1688   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
1689   Lower->addDirectory("//root/foo");
1690   Lower->addRegularFile("//root/foo/a");
1691   Lower->addDirectory("//root/bar");
1692   Lower->addRegularFile("//root/bar/b");
1693   Lower->addRegularFile("//root/bar/c");
1694   IntrusiveRefCntPtr<vfs::FileSystem> FS =
1695       getFromYAMLString("{ 'roots': [\n"
1696                         "{\n"
1697                         "  'type': 'directory',\n"
1698                         "  'name': '//root/',\n"
1699                         "  'contents': [ {\n"
1700                         "                  'type': 'directory-remap',\n"
1701                         "                  'name': 'bar',\n"
1702                         "                  'external-contents': '//root/foo'\n"
1703                         "                }\n"
1704                         "              ]\n"
1705                         "}]}",
1706                         Lower);
1707   ASSERT_NE(FS.get(), nullptr);
1708 
1709   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
1710       new vfs::OverlayFileSystem(Lower));
1711   O->pushOverlay(FS);
1712 
1713   ErrorOr<vfs::Status> S = O->status("//root/foo");
1714   ASSERT_FALSE(S.getError());
1715 
1716   ErrorOr<vfs::Status> SS = O->status("//root/bar");
1717   ASSERT_FALSE(SS.getError());
1718   EXPECT_TRUE(S->equivalent(*SS));
1719 
1720   std::error_code EC;
1721   checkContents(O->dir_begin("//root/bar", EC),
1722                 {"//root/foo/a", "//root/bar/b", "//root/bar/c"});
1723 
1724   Lower->addRegularFile("//root/foo/b");
1725   checkContents(O->dir_begin("//root/bar", EC),
1726                 {"//root/foo/a", "//root/foo/b", "//root/bar/c"});
1727 
1728   EXPECT_EQ(0, NumDiagnostics);
1729 }
1730 
1731 TEST_F(VFSFromYAMLTest, RemappedDirectoryOverlayNoExternalNames) {
1732   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
1733   Lower->addDirectory("//root/foo");
1734   Lower->addRegularFile("//root/foo/a");
1735   Lower->addDirectory("//root/bar");
1736   Lower->addRegularFile("//root/bar/b");
1737   Lower->addRegularFile("//root/bar/c");
1738   IntrusiveRefCntPtr<vfs::FileSystem> FS =
1739       getFromYAMLString("{ 'use-external-names': false,\n"
1740                         "  'roots': [\n"
1741                         "{\n"
1742                         "  'type': 'directory',\n"
1743                         "  'name': '//root/',\n"
1744                         "  'contents': [ {\n"
1745                         "                  'type': 'directory-remap',\n"
1746                         "                  'name': 'bar',\n"
1747                         "                  'external-contents': '//root/foo'\n"
1748                         "                }\n"
1749                         "              ]\n"
1750                         "}]}",
1751                         Lower);
1752   ASSERT_NE(FS.get(), nullptr);
1753 
1754   ErrorOr<vfs::Status> S = FS->status("//root/foo");
1755   ASSERT_FALSE(S.getError());
1756 
1757   ErrorOr<vfs::Status> SS = FS->status("//root/bar");
1758   ASSERT_FALSE(SS.getError());
1759   EXPECT_TRUE(S->equivalent(*SS));
1760 
1761   std::error_code EC;
1762   checkContents(FS->dir_begin("//root/bar", EC),
1763                 {"//root/bar/a", "//root/bar/b", "//root/bar/c"});
1764 
1765   Lower->addRegularFile("//root/foo/b");
1766   checkContents(FS->dir_begin("//root/bar", EC),
1767                 {"//root/bar/a", "//root/bar/b", "//root/bar/c"});
1768 
1769   EXPECT_EQ(0, NumDiagnostics);
1770 }
1771 
1772 TEST_F(VFSFromYAMLTest, RemappedDirectoryOverlayNoFallthrough) {
1773   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
1774   Lower->addDirectory("//root/foo");
1775   Lower->addRegularFile("//root/foo/a");
1776   Lower->addDirectory("//root/bar");
1777   Lower->addRegularFile("//root/bar/b");
1778   Lower->addRegularFile("//root/bar/c");
1779   IntrusiveRefCntPtr<vfs::FileSystem> FS =
1780       getFromYAMLString("{ 'fallthrough': false,\n"
1781                         "  'roots': [\n"
1782                         "{\n"
1783                         "  'type': 'directory',\n"
1784                         "  'name': '//root/',\n"
1785                         "  'contents': [ {\n"
1786                         "                  'type': 'directory-remap',\n"
1787                         "                  'name': 'bar',\n"
1788                         "                  'external-contents': '//root/foo'\n"
1789                         "                }\n"
1790                         "              ]\n"
1791                         "}]}",
1792                         Lower);
1793   ASSERT_NE(FS.get(), nullptr);
1794 
1795   ErrorOr<vfs::Status> S = Lower->status("//root/foo");
1796   ASSERT_FALSE(S.getError());
1797 
1798   ErrorOr<vfs::Status> SS = FS->status("//root/bar");
1799   ASSERT_FALSE(SS.getError());
1800   EXPECT_TRUE(S->equivalent(*SS));
1801 
1802   std::error_code EC;
1803   checkContents(FS->dir_begin("//root/bar", EC), {"//root/foo/a"});
1804 
1805   Lower->addRegularFile("//root/foo/b");
1806   checkContents(FS->dir_begin("//root/bar", EC),
1807                 {"//root/foo/a", "//root/foo/b"});
1808 
1809   EXPECT_EQ(0, NumDiagnostics);
1810 }
1811 
1812 TEST_F(VFSFromYAMLTest, ReturnsRequestedPathVFSMiss) {
1813   IntrusiveRefCntPtr<vfs::InMemoryFileSystem> BaseFS(
1814       new vfs::InMemoryFileSystem);
1815   BaseFS->addFile("//root/foo/a", 0,
1816                   MemoryBuffer::getMemBuffer("contents of a"));
1817   ASSERT_FALSE(BaseFS->setCurrentWorkingDirectory("//root/foo"));
1818   auto RemappedFS = vfs::RedirectingFileSystem::create(
1819       {}, /*UseExternalNames=*/false, *BaseFS);
1820 
1821   auto OpenedF = RemappedFS->openFileForRead("a");
1822   ASSERT_FALSE(OpenedF.getError());
1823   llvm::ErrorOr<std::string> Name = (*OpenedF)->getName();
1824   ASSERT_FALSE(Name.getError());
1825   EXPECT_EQ("a", Name.get());
1826 
1827   auto OpenedS = (*OpenedF)->status();
1828   ASSERT_FALSE(OpenedS.getError());
1829   EXPECT_EQ("a", OpenedS->getName());
1830   EXPECT_FALSE(OpenedS->IsVFSMapped);
1831   EXPECT_FALSE(OpenedS->ExposesExternalVFSPath);
1832 
1833   auto DirectS = RemappedFS->status("a");
1834   ASSERT_FALSE(DirectS.getError());
1835   EXPECT_EQ("a", DirectS->getName());
1836   EXPECT_FALSE(DirectS->IsVFSMapped);
1837   EXPECT_FALSE(DirectS->ExposesExternalVFSPath);
1838 
1839   EXPECT_EQ(0, NumDiagnostics);
1840 }
1841 
1842 TEST_F(VFSFromYAMLTest, ReturnsExternalPathVFSHit) {
1843   IntrusiveRefCntPtr<vfs::InMemoryFileSystem> BaseFS(
1844       new vfs::InMemoryFileSystem);
1845   BaseFS->addFile("//root/foo/realname", 0,
1846                   MemoryBuffer::getMemBuffer("contents of a"));
1847   auto FS =
1848       getFromYAMLString("{ 'use-external-names': true,\n"
1849                         "  'roots': [\n"
1850                         "{\n"
1851                         "  'type': 'directory',\n"
1852                         "  'name': '//root/foo',\n"
1853                         "  'contents': [ {\n"
1854                         "                  'type': 'file',\n"
1855                         "                  'name': 'vfsname',\n"
1856                         "                  'external-contents': 'realname'\n"
1857                         "                }\n"
1858                         "              ]\n"
1859                         "}]}",
1860                         BaseFS);
1861   ASSERT_FALSE(FS->setCurrentWorkingDirectory("//root/foo"));
1862 
1863   auto OpenedF = FS->openFileForRead("vfsname");
1864   ASSERT_FALSE(OpenedF.getError());
1865   llvm::ErrorOr<std::string> Name = (*OpenedF)->getName();
1866   ASSERT_FALSE(Name.getError());
1867   EXPECT_EQ("realname", Name.get());
1868 
1869   auto OpenedS = (*OpenedF)->status();
1870   ASSERT_FALSE(OpenedS.getError());
1871   EXPECT_EQ("realname", OpenedS->getName());
1872   EXPECT_TRUE(OpenedS->IsVFSMapped);
1873   EXPECT_TRUE(OpenedS->ExposesExternalVFSPath);
1874 
1875   auto DirectS = FS->status("vfsname");
1876   ASSERT_FALSE(DirectS.getError());
1877   EXPECT_EQ("realname", DirectS->getName());
1878   EXPECT_TRUE(DirectS->IsVFSMapped);
1879   EXPECT_TRUE(DirectS->ExposesExternalVFSPath);
1880 
1881   EXPECT_EQ(0, NumDiagnostics);
1882 }
1883 
1884 TEST_F(VFSFromYAMLTest, RootRelativeTest) {
1885   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
1886   Lower->addDirectory("//root/foo/bar");
1887   Lower->addRegularFile("//root/foo/bar/a");
1888   IntrusiveRefCntPtr<vfs::FileSystem> FS =
1889       getFromYAMLString("{\n"
1890                         "  'case-sensitive': false,\n"
1891                         "  'root-relative': 'overlay-dir',\n"
1892                         "  'roots': [\n"
1893                         "    { 'name': 'b', 'type': 'file',\n"
1894                         "      'external-contents': '//root/foo/bar/a'\n"
1895                         "    }\n"
1896                         "  ]\n"
1897                         "}",
1898                         Lower, "//root/foo/bar/overlay");
1899 
1900   ASSERT_NE(FS.get(), nullptr);
1901   ErrorOr<vfs::Status> S = FS->status("//root/foo/bar/b");
1902   ASSERT_FALSE(S.getError());
1903   EXPECT_EQ("//root/foo/bar/a", S->getName());
1904 
1905   // On Windows, with overlay-relative set to true, the relative
1906   // path in external-contents field will be prepend by OverlayDir
1907   // with native path separator, regardless of the actual path separator
1908   // used in YAMLFilePath field.
1909 #ifndef _WIN32
1910   FS = getFromYAMLString("{\n"
1911                          "  'case-sensitive': false,\n"
1912                          "  'overlay-relative': true,\n"
1913                          "  'root-relative': 'overlay-dir',\n"
1914                          "  'roots': [\n"
1915                          "    { 'name': 'b', 'type': 'file',\n"
1916                          "      'external-contents': 'a'\n"
1917                          "    }\n"
1918                          "  ]\n"
1919                          "}",
1920                          Lower, "//root/foo/bar/overlay");
1921   ASSERT_NE(FS.get(), nullptr);
1922   S = FS->status("//root/foo/bar/b");
1923   ASSERT_FALSE(S.getError());
1924   EXPECT_EQ("//root/foo/bar/a", S->getName());
1925 #else
1926   IntrusiveRefCntPtr<DummyFileSystem> LowerWindows(new DummyFileSystem());
1927   LowerWindows->addDirectory("\\\\root\\foo\\bar");
1928   LowerWindows->addRegularFile("\\\\root\\foo\\bar\\a");
1929   FS = getFromYAMLString("{\n"
1930                          "  'case-sensitive': false,\n"
1931                          "  'overlay-relative': true,\n"
1932                          "  'root-relative': 'overlay-dir',\n"
1933                          "  'roots': [\n"
1934                          "    { 'name': 'b', 'type': 'file',\n"
1935                          "      'external-contents': 'a'\n"
1936                          "    }\n"
1937                          "  ]\n"
1938                          "}",
1939                          LowerWindows, "\\\\root\\foo\\bar\\overlay");
1940   ASSERT_NE(FS.get(), nullptr);
1941   S = FS->status("\\\\root\\foo\\bar\\b");
1942   ASSERT_FALSE(S.getError());
1943   EXPECT_EQ("\\\\root\\foo\\bar\\a", S->getName());
1944 #endif
1945 }
1946 
1947 TEST_F(VFSFromYAMLTest, ReturnsInternalPathVFSHit) {
1948   IntrusiveRefCntPtr<vfs::InMemoryFileSystem> BaseFS(
1949       new vfs::InMemoryFileSystem);
1950   BaseFS->addFile("//root/foo/realname", 0,
1951                   MemoryBuffer::getMemBuffer("contents of a"));
1952   auto FS =
1953       getFromYAMLString("{ 'use-external-names': false,\n"
1954                         "  'roots': [\n"
1955                         "{\n"
1956                         "  'type': 'directory',\n"
1957                         "  'name': '//root/foo',\n"
1958                         "  'contents': [ {\n"
1959                         "                  'type': 'file',\n"
1960                         "                  'name': 'vfsname',\n"
1961                         "                  'external-contents': 'realname'\n"
1962                         "                }\n"
1963                         "              ]\n"
1964                         "}]}",
1965                         BaseFS);
1966   ASSERT_FALSE(FS->setCurrentWorkingDirectory("//root/foo"));
1967 
1968   auto OpenedF = FS->openFileForRead("vfsname");
1969   ASSERT_FALSE(OpenedF.getError());
1970   llvm::ErrorOr<std::string> Name = (*OpenedF)->getName();
1971   ASSERT_FALSE(Name.getError());
1972   EXPECT_EQ("vfsname", Name.get());
1973 
1974   auto OpenedS = (*OpenedF)->status();
1975   ASSERT_FALSE(OpenedS.getError());
1976   EXPECT_EQ("vfsname", OpenedS->getName());
1977   EXPECT_TRUE(OpenedS->IsVFSMapped);
1978   EXPECT_FALSE(OpenedS->ExposesExternalVFSPath);
1979 
1980   auto DirectS = FS->status("vfsname");
1981   ASSERT_FALSE(DirectS.getError());
1982   EXPECT_EQ("vfsname", DirectS->getName());
1983   EXPECT_TRUE(DirectS->IsVFSMapped);
1984   EXPECT_FALSE(DirectS->ExposesExternalVFSPath);
1985 
1986   EXPECT_EQ(0, NumDiagnostics);
1987 }
1988 
1989 TEST_F(VFSFromYAMLTest, CaseInsensitive) {
1990   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
1991   Lower->addRegularFile("//root/foo/bar/a");
1992   IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
1993       "{ 'case-sensitive': 'false',\n"
1994       "  'roots': [\n"
1995       "{\n"
1996       "  'type': 'directory',\n"
1997       "  'name': '//root/',\n"
1998       "  'contents': [ {\n"
1999       "                  'type': 'file',\n"
2000       "                  'name': 'XX',\n"
2001       "                  'external-contents': '//root/foo/bar/a'\n"
2002       "                }\n"
2003       "              ]\n"
2004       "}]}",
2005       Lower);
2006   ASSERT_NE(FS.get(), nullptr);
2007 
2008   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
2009       new vfs::OverlayFileSystem(Lower));
2010   O->pushOverlay(FS);
2011 
2012   ErrorOr<vfs::Status> S = O->status("//root/XX");
2013   ASSERT_FALSE(S.getError());
2014 
2015   ErrorOr<vfs::Status> SS = O->status("//root/xx");
2016   ASSERT_FALSE(SS.getError());
2017   EXPECT_TRUE(S->equivalent(*SS));
2018   SS = O->status("//root/xX");
2019   EXPECT_TRUE(S->equivalent(*SS));
2020   SS = O->status("//root/Xx");
2021   EXPECT_TRUE(S->equivalent(*SS));
2022   EXPECT_EQ(0, NumDiagnostics);
2023 }
2024 
2025 TEST_F(VFSFromYAMLTest, CaseSensitive) {
2026   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
2027   Lower->addRegularFile("//root/foo/bar/a");
2028   IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
2029       "{ 'case-sensitive': 'true',\n"
2030       "  'roots': [\n"
2031       "{\n"
2032       "  'type': 'directory',\n"
2033       "  'name': '//root/',\n"
2034       "  'contents': [ {\n"
2035       "                  'type': 'file',\n"
2036       "                  'name': 'XX',\n"
2037       "                  'external-contents': '//root/foo/bar/a'\n"
2038       "                }\n"
2039       "              ]\n"
2040       "}]}",
2041       Lower);
2042   ASSERT_NE(FS.get(), nullptr);
2043 
2044   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
2045       new vfs::OverlayFileSystem(Lower));
2046   O->pushOverlay(FS);
2047 
2048   ErrorOr<vfs::Status> SS = O->status("//root/xx");
2049   EXPECT_EQ(SS.getError(), llvm::errc::no_such_file_or_directory);
2050   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   EXPECT_EQ(0, NumDiagnostics);
2055 }
2056 
2057 TEST_F(VFSFromYAMLTest, IllegalVFSFile) {
2058   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
2059 
2060   // invalid YAML at top-level
2061   IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString("{]", Lower);
2062   EXPECT_EQ(nullptr, FS.get());
2063   // invalid YAML in roots
2064   FS = getFromYAMLString("{ 'roots':[}", Lower);
2065   // invalid YAML in directory
2066   FS = getFromYAMLString(
2067       "{ 'roots':[ { 'name': 'foo', 'type': 'directory', 'contents': [}",
2068       Lower);
2069   EXPECT_EQ(nullptr, FS.get());
2070 
2071   // invalid configuration
2072   FS = getFromYAMLString("{ 'knobular': 'true', 'roots':[] }", Lower);
2073   EXPECT_EQ(nullptr, FS.get());
2074   FS = getFromYAMLString("{ 'case-sensitive': 'maybe', 'roots':[] }", Lower);
2075   EXPECT_EQ(nullptr, FS.get());
2076 
2077   // invalid roots
2078   FS = getFromYAMLString("{ 'roots':'' }", Lower);
2079   EXPECT_EQ(nullptr, FS.get());
2080   FS = getFromYAMLString("{ 'roots':{} }", Lower);
2081   EXPECT_EQ(nullptr, FS.get());
2082 
2083   // invalid entries
2084   FS = getFromYAMLString(
2085       "{ 'roots':[ { 'type': 'other', 'name': 'me', 'contents': '' }", Lower);
2086   EXPECT_EQ(nullptr, FS.get());
2087   FS = getFromYAMLString("{ 'roots':[ { 'type': 'file', 'name': [], "
2088                          "'external-contents': 'other' }",
2089                          Lower);
2090   EXPECT_EQ(nullptr, FS.get());
2091   FS = getFromYAMLString(
2092       "{ 'roots':[ { 'type': 'file', 'name': 'me', 'external-contents': [] }",
2093       Lower);
2094   EXPECT_EQ(nullptr, FS.get());
2095   FS = getFromYAMLString(
2096       "{ 'roots':[ { 'type': 'file', 'name': 'me', 'external-contents': {} }",
2097       Lower);
2098   EXPECT_EQ(nullptr, FS.get());
2099   FS = getFromYAMLString(
2100       "{ 'roots':[ { 'type': 'directory', 'name': 'me', 'contents': {} }",
2101       Lower);
2102   EXPECT_EQ(nullptr, FS.get());
2103   FS = getFromYAMLString(
2104       "{ 'roots':[ { 'type': 'directory', 'name': 'me', 'contents': '' }",
2105       Lower);
2106   EXPECT_EQ(nullptr, FS.get());
2107   FS = getFromYAMLString(
2108       "{ 'roots':[ { 'thingy': 'directory', 'name': 'me', 'contents': [] }",
2109       Lower);
2110   EXPECT_EQ(nullptr, FS.get());
2111 
2112   // missing mandatory fields
2113   FS = getFromYAMLString("{ 'roots':[ { 'type': 'file', 'name': 'me' }", Lower);
2114   EXPECT_EQ(nullptr, FS.get());
2115   FS = getFromYAMLString(
2116       "{ 'roots':[ { 'type': 'file', 'external-contents': 'other' }", Lower);
2117   EXPECT_EQ(nullptr, FS.get());
2118   FS = getFromYAMLString("{ 'roots':[ { 'name': 'me', 'contents': [] }", Lower);
2119   EXPECT_EQ(nullptr, FS.get());
2120 
2121   // duplicate keys
2122   FS = getFromYAMLString("{ 'roots':[], 'roots':[] }", Lower);
2123   EXPECT_EQ(nullptr, FS.get());
2124   FS = getFromYAMLString(
2125       "{ 'case-sensitive':'true', 'case-sensitive':'true', 'roots':[] }",
2126       Lower);
2127   EXPECT_EQ(nullptr, FS.get());
2128   FS =
2129       getFromYAMLString("{ 'roots':[{'name':'me', 'name':'you', 'type':'file', "
2130                         "'external-contents':'blah' } ] }",
2131                         Lower);
2132   EXPECT_EQ(nullptr, FS.get());
2133 
2134   // missing version
2135   FS = getFromYAMLRawString("{ 'roots':[] }", Lower);
2136   EXPECT_EQ(nullptr, FS.get());
2137 
2138   // bad version number
2139   FS = getFromYAMLRawString("{ 'version':'foo', 'roots':[] }", Lower);
2140   EXPECT_EQ(nullptr, FS.get());
2141   FS = getFromYAMLRawString("{ 'version':-1, 'roots':[] }", Lower);
2142   EXPECT_EQ(nullptr, FS.get());
2143   FS = getFromYAMLRawString("{ 'version':100000, 'roots':[] }", Lower);
2144   EXPECT_EQ(nullptr, FS.get());
2145 
2146   // both 'external-contents' and 'contents' specified
2147   Lower->addDirectory("//root/external/dir");
2148   FS = getFromYAMLString(
2149       "{ 'roots':[ \n"
2150       "{ 'type': 'directory', 'name': '//root/A', 'contents': [],\n"
2151       "  'external-contents': '//root/external/dir'}]}",
2152       Lower);
2153   EXPECT_EQ(nullptr, FS.get());
2154 
2155   // 'directory-remap' with 'contents'
2156   FS = getFromYAMLString(
2157       "{ 'roots':[ \n"
2158       "{ 'type': 'directory-remap', 'name': '//root/A', 'contents': [] }]}",
2159       Lower);
2160   EXPECT_EQ(nullptr, FS.get());
2161 
2162   // invalid redirect kind
2163   FS = getFromYAMLString("{ 'redirecting-with': 'none', 'roots': [{\n"
2164                          "  'type': 'directory-remap',\n"
2165                          "  'name': '//root/A',\n"
2166                          "  'external-contents': '//root/B' }]}",
2167                          Lower);
2168   EXPECT_EQ(nullptr, FS.get());
2169 
2170   // redirect and fallthrough passed
2171   FS = getFromYAMLString("{ 'redirecting-with': 'fallthrough',\n"
2172                          "  'fallthrough': true,\n"
2173                          "  'roots': [{\n"
2174                          "    'type': 'directory-remap',\n"
2175                          "    'name': '//root/A',\n"
2176                          "    'external-contents': '//root/B' }]}",
2177                          Lower);
2178   EXPECT_EQ(nullptr, FS.get());
2179 
2180   EXPECT_EQ(28, NumDiagnostics);
2181 }
2182 
2183 TEST_F(VFSFromYAMLTest, UseExternalName) {
2184   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
2185   Lower->addRegularFile("//root/external/file");
2186 
2187   IntrusiveRefCntPtr<vfs::FileSystem> FS =
2188       getFromYAMLString("{ 'roots': [\n"
2189                         "  { 'type': 'file', 'name': '//root/A',\n"
2190                         "    'external-contents': '//root/external/file'\n"
2191                         "  },\n"
2192                         "  { 'type': 'file', 'name': '//root/B',\n"
2193                         "    'use-external-name': true,\n"
2194                         "    'external-contents': '//root/external/file'\n"
2195                         "  },\n"
2196                         "  { 'type': 'file', 'name': '//root/C',\n"
2197                         "    'use-external-name': false,\n"
2198                         "    'external-contents': '//root/external/file'\n"
2199                         "  }\n"
2200                         "] }",
2201                         Lower);
2202   ASSERT_NE(nullptr, FS.get());
2203 
2204   // default true
2205   EXPECT_EQ("//root/external/file", FS->status("//root/A")->getName());
2206   // explicit
2207   EXPECT_EQ("//root/external/file", FS->status("//root/B")->getName());
2208   EXPECT_EQ("//root/C", FS->status("//root/C")->getName());
2209 
2210   // global configuration
2211   FS = getFromYAMLString("{ 'use-external-names': false,\n"
2212                          "  'roots': [\n"
2213                          "  { 'type': 'file', 'name': '//root/A',\n"
2214                          "    'external-contents': '//root/external/file'\n"
2215                          "  },\n"
2216                          "  { 'type': 'file', 'name': '//root/B',\n"
2217                          "    'use-external-name': true,\n"
2218                          "    'external-contents': '//root/external/file'\n"
2219                          "  },\n"
2220                          "  { 'type': 'file', 'name': '//root/C',\n"
2221                          "    'use-external-name': false,\n"
2222                          "    'external-contents': '//root/external/file'\n"
2223                          "  }\n"
2224                          "] }",
2225                          Lower);
2226   ASSERT_NE(nullptr, FS.get());
2227 
2228   // default
2229   EXPECT_EQ("//root/A", FS->status("//root/A")->getName());
2230   // explicit
2231   EXPECT_EQ("//root/external/file", FS->status("//root/B")->getName());
2232   EXPECT_EQ("//root/C", FS->status("//root/C")->getName());
2233 }
2234 
2235 TEST_F(VFSFromYAMLTest, MultiComponentPath) {
2236   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
2237   Lower->addRegularFile("//root/other");
2238 
2239   // file in roots
2240   IntrusiveRefCntPtr<vfs::FileSystem> FS =
2241       getFromYAMLString("{ 'roots': [\n"
2242                         "  { 'type': 'file', 'name': '//root/path/to/file',\n"
2243                         "    'external-contents': '//root/other' }]\n"
2244                         "}",
2245                         Lower);
2246   ASSERT_NE(nullptr, FS.get());
2247   EXPECT_FALSE(FS->status("//root/path/to/file").getError());
2248   EXPECT_FALSE(FS->status("//root/path/to").getError());
2249   EXPECT_FALSE(FS->status("//root/path").getError());
2250   EXPECT_FALSE(FS->status("//root/").getError());
2251 
2252   // at the start
2253   FS = getFromYAMLString(
2254       "{ 'roots': [\n"
2255       "  { 'type': 'directory', 'name': '//root/path/to',\n"
2256       "    'contents': [ { 'type': 'file', 'name': 'file',\n"
2257       "                    'external-contents': '//root/other' }]}]\n"
2258       "}",
2259       Lower);
2260   ASSERT_NE(nullptr, FS.get());
2261   EXPECT_FALSE(FS->status("//root/path/to/file").getError());
2262   EXPECT_FALSE(FS->status("//root/path/to").getError());
2263   EXPECT_FALSE(FS->status("//root/path").getError());
2264   EXPECT_FALSE(FS->status("//root/").getError());
2265 
2266   // at the end
2267   FS = getFromYAMLString(
2268       "{ 'roots': [\n"
2269       "  { 'type': 'directory', 'name': '//root/',\n"
2270       "    'contents': [ { 'type': 'file', 'name': 'path/to/file',\n"
2271       "                    'external-contents': '//root/other' }]}]\n"
2272       "}",
2273       Lower);
2274   ASSERT_NE(nullptr, FS.get());
2275   EXPECT_FALSE(FS->status("//root/path/to/file").getError());
2276   EXPECT_FALSE(FS->status("//root/path/to").getError());
2277   EXPECT_FALSE(FS->status("//root/path").getError());
2278   EXPECT_FALSE(FS->status("//root/").getError());
2279 }
2280 
2281 TEST_F(VFSFromYAMLTest, TrailingSlashes) {
2282   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
2283   Lower->addRegularFile("//root/other");
2284 
2285   // file in roots
2286   IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
2287       "{ 'roots': [\n"
2288       "  { 'type': 'directory', 'name': '//root/path/to////',\n"
2289       "    'contents': [ { 'type': 'file', 'name': 'file',\n"
2290       "                    'external-contents': '//root/other' }]}]\n"
2291       "}",
2292       Lower);
2293   ASSERT_NE(nullptr, FS.get());
2294   EXPECT_FALSE(FS->status("//root/path/to/file").getError());
2295   EXPECT_FALSE(FS->status("//root/path/to").getError());
2296   EXPECT_FALSE(FS->status("//root/path").getError());
2297   EXPECT_FALSE(FS->status("//root/").getError());
2298 }
2299 
2300 TEST_F(VFSFromYAMLTest, DirectoryIteration) {
2301   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
2302   Lower->addDirectory("//root/");
2303   Lower->addDirectory("//root/foo");
2304   Lower->addDirectory("//root/foo/bar");
2305   Lower->addRegularFile("//root/foo/bar/a");
2306   Lower->addRegularFile("//root/foo/bar/b");
2307   Lower->addRegularFile("//root/file3");
2308   IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
2309       "{ 'use-external-names': false,\n"
2310       "  'roots': [\n"
2311       "{\n"
2312       "  'type': 'directory',\n"
2313       "  'name': '//root/',\n"
2314       "  'contents': [ {\n"
2315       "                  'type': 'file',\n"
2316       "                  'name': 'file1',\n"
2317       "                  'external-contents': '//root/foo/bar/a'\n"
2318       "                },\n"
2319       "                {\n"
2320       "                  'type': 'file',\n"
2321       "                  'name': 'file2',\n"
2322       "                  'external-contents': '//root/foo/bar/b'\n"
2323       "                }\n"
2324       "              ]\n"
2325       "}\n"
2326       "]\n"
2327       "}",
2328       Lower);
2329   ASSERT_NE(FS.get(), nullptr);
2330 
2331   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
2332       new vfs::OverlayFileSystem(Lower));
2333   O->pushOverlay(FS);
2334 
2335   std::error_code EC;
2336   checkContents(O->dir_begin("//root/", EC),
2337                 {"//root/file1", "//root/file2", "//root/file3", "//root/foo"});
2338 
2339   checkContents(O->dir_begin("//root/foo/bar", EC),
2340                 {"//root/foo/bar/a", "//root/foo/bar/b"});
2341 }
2342 
2343 TEST_F(VFSFromYAMLTest, DirectoryIterationSameDirMultipleEntries) {
2344   // https://llvm.org/bugs/show_bug.cgi?id=27725
2345   if (!supportsSameDirMultipleYAMLEntries())
2346     GTEST_SKIP();
2347 
2348   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
2349   Lower->addDirectory("//root/zab");
2350   Lower->addDirectory("//root/baz");
2351   Lower->addRegularFile("//root/zab/a");
2352   Lower->addRegularFile("//root/zab/b");
2353   IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
2354       "{ 'use-external-names': false,\n"
2355       "  'roots': [\n"
2356       "{\n"
2357       "  'type': 'directory',\n"
2358       "  'name': '//root/baz/',\n"
2359       "  'contents': [ {\n"
2360       "                  'type': 'file',\n"
2361       "                  'name': 'x',\n"
2362       "                  'external-contents': '//root/zab/a'\n"
2363       "                }\n"
2364       "              ]\n"
2365       "},\n"
2366       "{\n"
2367       "  'type': 'directory',\n"
2368       "  'name': '//root/baz/',\n"
2369       "  'contents': [ {\n"
2370       "                  'type': 'file',\n"
2371       "                  'name': 'y',\n"
2372       "                  'external-contents': '//root/zab/b'\n"
2373       "                }\n"
2374       "              ]\n"
2375       "}\n"
2376       "]\n"
2377       "}",
2378       Lower);
2379   ASSERT_NE(FS.get(), nullptr);
2380 
2381   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
2382       new vfs::OverlayFileSystem(Lower));
2383   O->pushOverlay(FS);
2384 
2385   std::error_code EC;
2386 
2387   checkContents(O->dir_begin("//root/baz/", EC),
2388                 {"//root/baz/x", "//root/baz/y"});
2389 }
2390 
2391 TEST_F(VFSFromYAMLTest, RecursiveDirectoryIterationLevel) {
2392 
2393   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
2394   Lower->addDirectory("//root/a");
2395   Lower->addDirectory("//root/a/b");
2396   Lower->addDirectory("//root/a/b/c");
2397   Lower->addRegularFile("//root/a/b/c/file");
2398   IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
2399       "{ 'use-external-names': false,\n"
2400       "  'roots': [\n"
2401       "{\n"
2402       "  'type': 'directory',\n"
2403       "  'name': '//root/a/b/c/',\n"
2404       "  'contents': [ {\n"
2405       "                  'type': 'file',\n"
2406       "                  'name': 'file',\n"
2407       "                  'external-contents': '//root/a/b/c/file'\n"
2408       "                }\n"
2409       "              ]\n"
2410       "},\n"
2411       "]\n"
2412       "}",
2413       Lower);
2414   ASSERT_NE(FS.get(), nullptr);
2415 
2416   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
2417       new vfs::OverlayFileSystem(Lower));
2418   O->pushOverlay(FS);
2419 
2420   std::error_code EC;
2421 
2422   // Test recursive_directory_iterator level()
2423   vfs::recursive_directory_iterator I = vfs::recursive_directory_iterator(
2424                                         *O, "//root", EC),
2425                                     E;
2426   ASSERT_FALSE(EC);
2427   for (int l = 0; I != E; I.increment(EC), ++l) {
2428     ASSERT_FALSE(EC);
2429     EXPECT_EQ(I.level(), l);
2430   }
2431   EXPECT_EQ(I, E);
2432 }
2433 
2434 TEST_F(VFSFromYAMLTest, RelativePaths) {
2435   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
2436   std::error_code EC;
2437   SmallString<128> CWD;
2438   EC = llvm::sys::fs::current_path(CWD);
2439   ASSERT_FALSE(EC);
2440 
2441   // Filename at root level without a parent directory.
2442   IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
2443       "{ 'roots': [\n"
2444       "  { 'type': 'file', 'name': 'file-not-in-directory.h',\n"
2445       "    'external-contents': '//root/external/file'\n"
2446       "  }\n"
2447       "] }",
2448       Lower);
2449   ASSERT_TRUE(FS.get() != nullptr);
2450   SmallString<128> ExpectedPathNotInDir("file-not-in-directory.h");
2451   llvm::sys::fs::make_absolute(ExpectedPathNotInDir);
2452   checkContents(FS->dir_begin(CWD, EC), {ExpectedPathNotInDir});
2453 
2454   // Relative file path.
2455   FS = getFromYAMLString("{ 'roots': [\n"
2456                          "  { 'type': 'file', 'name': 'relative/path.h',\n"
2457                          "    'external-contents': '//root/external/file'\n"
2458                          "  }\n"
2459                          "] }",
2460                          Lower);
2461   ASSERT_TRUE(FS.get() != nullptr);
2462   SmallString<128> Parent("relative");
2463   llvm::sys::fs::make_absolute(Parent);
2464   auto I = FS->dir_begin(Parent, EC);
2465   ASSERT_FALSE(EC);
2466   // Convert to POSIX path for comparison of windows paths
2467   ASSERT_EQ("relative/path.h",
2468             getPosixPath(std::string(I->path().substr(CWD.size() + 1))));
2469 
2470   // Relative directory path.
2471   FS = getFromYAMLString(
2472       "{ 'roots': [\n"
2473       "  { 'type': 'directory', 'name': 'relative/directory/path.h',\n"
2474       "    'contents': []\n"
2475       "  }\n"
2476       "] }",
2477       Lower);
2478   ASSERT_TRUE(FS.get() != nullptr);
2479   SmallString<128> Root("relative/directory");
2480   llvm::sys::fs::make_absolute(Root);
2481   I = FS->dir_begin(Root, EC);
2482   ASSERT_FALSE(EC);
2483   ASSERT_EQ("path.h", std::string(I->path().substr(Root.size() + 1)));
2484 
2485   EXPECT_EQ(0, NumDiagnostics);
2486 }
2487 
2488 TEST_F(VFSFromYAMLTest, NonFallthroughDirectoryIteration) {
2489   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
2490   Lower->addDirectory("//root/");
2491   Lower->addRegularFile("//root/a");
2492   Lower->addRegularFile("//root/b");
2493   IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
2494       "{ 'use-external-names': false,\n"
2495       "  'fallthrough': false,\n"
2496       "  'roots': [\n"
2497       "{\n"
2498       "  'type': 'directory',\n"
2499       "  'name': '//root/',\n"
2500       "  'contents': [ {\n"
2501       "                  'type': 'file',\n"
2502       "                  'name': 'c',\n"
2503       "                  'external-contents': '//root/a'\n"
2504       "                }\n"
2505       "              ]\n"
2506       "}\n"
2507       "]\n"
2508       "}",
2509       Lower);
2510   ASSERT_NE(FS.get(), nullptr);
2511 
2512   std::error_code EC;
2513   checkContents(FS->dir_begin("//root/", EC),
2514                 {"//root/c"});
2515 }
2516 
2517 TEST_F(VFSFromYAMLTest, DirectoryIterationWithDuplicates) {
2518   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
2519   Lower->addDirectory("//root/");
2520   Lower->addRegularFile("//root/a");
2521   Lower->addRegularFile("//root/b");
2522   IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
2523       "{ 'use-external-names': false,\n"
2524       "  'roots': [\n"
2525       "{\n"
2526       "  'type': 'directory',\n"
2527       "  'name': '//root/',\n"
2528       "  'contents': [ {\n"
2529       "                  'type': 'file',\n"
2530       "                  'name': 'a',\n"
2531       "                  'external-contents': '//root/a'\n"
2532       "                }\n"
2533       "              ]\n"
2534       "}\n"
2535       "]\n"
2536       "}",
2537 	  Lower);
2538   ASSERT_NE(FS.get(), nullptr);
2539 
2540   std::error_code EC;
2541   checkContents(FS->dir_begin("//root/", EC),
2542                 {"//root/a", "//root/b"});
2543 }
2544 
2545 TEST_F(VFSFromYAMLTest, DirectoryIterationErrorInVFSLayer) {
2546   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
2547   Lower->addDirectory("//root/");
2548   Lower->addDirectory("//root/foo");
2549   Lower->addRegularFile("//root/foo/a");
2550   Lower->addRegularFile("//root/foo/b");
2551   IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
2552       "{ 'use-external-names': false,\n"
2553       "  'roots': [\n"
2554       "{\n"
2555       "  'type': 'directory',\n"
2556       "  'name': '//root/',\n"
2557       "  'contents': [ {\n"
2558       "                  'type': 'file',\n"
2559       "                  'name': 'bar/a',\n"
2560       "                  'external-contents': '//root/foo/a'\n"
2561       "                }\n"
2562       "              ]\n"
2563       "}\n"
2564       "]\n"
2565       "}",
2566       Lower);
2567   ASSERT_NE(FS.get(), nullptr);
2568 
2569   std::error_code EC;
2570   checkContents(FS->dir_begin("//root/foo", EC),
2571                 {"//root/foo/a", "//root/foo/b"});
2572 }
2573 
2574 TEST_F(VFSFromYAMLTest, GetRealPath) {
2575   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
2576   Lower->addDirectory("//dir/");
2577   Lower->addRegularFile("/foo");
2578   Lower->addSymlink("/link");
2579   IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
2580       "{ 'use-external-names': false,\n"
2581       "  'roots': [\n"
2582       "{\n"
2583       "  'type': 'directory',\n"
2584       "  'name': '//root/',\n"
2585       "  'contents': [ {\n"
2586       "                  'type': 'file',\n"
2587       "                  'name': 'bar',\n"
2588       "                  'external-contents': '/link'\n"
2589       "                }\n"
2590       "              ]\n"
2591       "},\n"
2592       "{\n"
2593       "  'type': 'directory',\n"
2594       "  'name': '//dir/',\n"
2595       "  'contents': []\n"
2596       "}\n"
2597       "]\n"
2598       "}",
2599       Lower);
2600   ASSERT_NE(FS.get(), nullptr);
2601 
2602   // Regular file present in underlying file system.
2603   SmallString<16> RealPath;
2604   EXPECT_FALSE(FS->getRealPath("/foo", RealPath));
2605   EXPECT_EQ(RealPath.str(), "/foo");
2606 
2607   // File present in YAML pointing to symlink in underlying file system.
2608   EXPECT_FALSE(FS->getRealPath("//root/bar", RealPath));
2609   EXPECT_EQ(RealPath.str(), "/symlink");
2610 
2611   // Directories should fall back to the underlying file system is possible.
2612   EXPECT_FALSE(FS->getRealPath("//dir/", RealPath));
2613   EXPECT_EQ(RealPath.str(), "//dir/");
2614 
2615   // Try a non-existing file.
2616   EXPECT_EQ(FS->getRealPath("/non_existing", RealPath),
2617             errc::no_such_file_or_directory);
2618 }
2619 
2620 TEST_F(VFSFromYAMLTest, WorkingDirectory) {
2621   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
2622   Lower->addDirectory("//root/");
2623   Lower->addDirectory("//root/foo");
2624   Lower->addRegularFile("//root/foo/a");
2625   Lower->addRegularFile("//root/foo/b");
2626   IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
2627       "{ 'use-external-names': false,\n"
2628       "  'roots': [\n"
2629       "{\n"
2630       "  'type': 'directory',\n"
2631       "  'name': '//root/bar',\n"
2632       "  'contents': [ {\n"
2633       "                  'type': 'file',\n"
2634       "                  'name': 'a',\n"
2635       "                  'external-contents': '//root/foo/a'\n"
2636       "                }\n"
2637       "              ]\n"
2638       "}\n"
2639       "]\n"
2640       "}",
2641       Lower);
2642   ASSERT_NE(FS.get(), nullptr);
2643   std::error_code EC = FS->setCurrentWorkingDirectory("//root/bar");
2644   ASSERT_FALSE(EC);
2645 
2646   llvm::ErrorOr<std::string> WorkingDir = FS->getCurrentWorkingDirectory();
2647   ASSERT_TRUE(WorkingDir);
2648   EXPECT_EQ(*WorkingDir, "//root/bar");
2649 
2650   llvm::ErrorOr<vfs::Status> Status = FS->status("./a");
2651   ASSERT_FALSE(Status.getError());
2652   EXPECT_TRUE(Status->isStatusKnown());
2653   EXPECT_FALSE(Status->isDirectory());
2654   EXPECT_TRUE(Status->isRegularFile());
2655   EXPECT_FALSE(Status->isSymlink());
2656   EXPECT_FALSE(Status->isOther());
2657   EXPECT_TRUE(Status->exists());
2658 
2659   EC = FS->setCurrentWorkingDirectory("bogus");
2660   ASSERT_TRUE(EC);
2661   WorkingDir = FS->getCurrentWorkingDirectory();
2662   ASSERT_TRUE(WorkingDir);
2663   EXPECT_EQ(*WorkingDir, "//root/bar");
2664 
2665   EC = FS->setCurrentWorkingDirectory("//root/");
2666   ASSERT_FALSE(EC);
2667   WorkingDir = FS->getCurrentWorkingDirectory();
2668   ASSERT_TRUE(WorkingDir);
2669   EXPECT_EQ(*WorkingDir, "//root/");
2670 
2671   EC = FS->setCurrentWorkingDirectory("bar");
2672   ASSERT_FALSE(EC);
2673   WorkingDir = FS->getCurrentWorkingDirectory();
2674   ASSERT_TRUE(WorkingDir);
2675   EXPECT_EQ(*WorkingDir, "//root/bar");
2676 }
2677 
2678 TEST_F(VFSFromYAMLTest, WorkingDirectoryFallthrough) {
2679   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
2680   Lower->addDirectory("//root/");
2681   Lower->addDirectory("//root/foo");
2682   Lower->addRegularFile("//root/foo/a");
2683   Lower->addRegularFile("//root/foo/b");
2684   Lower->addRegularFile("//root/c");
2685   IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
2686       "{ 'use-external-names': false,\n"
2687       "  'roots': [\n"
2688       "{\n"
2689       "  'type': 'directory',\n"
2690       "  'name': '//root/bar',\n"
2691       "  'contents': [ {\n"
2692       "                  'type': 'file',\n"
2693       "                  'name': 'a',\n"
2694       "                  'external-contents': '//root/foo/a'\n"
2695       "                }\n"
2696       "              ]\n"
2697       "},\n"
2698       "{\n"
2699       "  'type': 'directory',\n"
2700       "  'name': '//root/bar/baz',\n"
2701       "  'contents': [ {\n"
2702       "                  'type': 'file',\n"
2703       "                  'name': 'a',\n"
2704       "                  'external-contents': '//root/foo/a'\n"
2705       "                }\n"
2706       "              ]\n"
2707       "}\n"
2708       "]\n"
2709       "}",
2710       Lower);
2711   ASSERT_NE(FS.get(), nullptr);
2712   std::error_code EC = FS->setCurrentWorkingDirectory("//root/");
2713   ASSERT_FALSE(EC);
2714   ASSERT_NE(FS.get(), nullptr);
2715 
2716   llvm::ErrorOr<vfs::Status> Status = FS->status("bar/a");
2717   ASSERT_FALSE(Status.getError());
2718   EXPECT_TRUE(Status->exists());
2719 
2720   Status = FS->status("foo/a");
2721   ASSERT_FALSE(Status.getError());
2722   EXPECT_TRUE(Status->exists());
2723 
2724   EC = FS->setCurrentWorkingDirectory("//root/bar");
2725   ASSERT_FALSE(EC);
2726 
2727   Status = FS->status("./a");
2728   ASSERT_FALSE(Status.getError());
2729   EXPECT_TRUE(Status->exists());
2730 
2731   Status = FS->status("./b");
2732   ASSERT_TRUE(Status.getError());
2733 
2734   Status = FS->status("./c");
2735   ASSERT_TRUE(Status.getError());
2736 
2737   EC = FS->setCurrentWorkingDirectory("//root/");
2738   ASSERT_FALSE(EC);
2739 
2740   Status = FS->status("c");
2741   ASSERT_FALSE(Status.getError());
2742   EXPECT_TRUE(Status->exists());
2743 
2744   Status = FS->status("./bar/baz/a");
2745   ASSERT_FALSE(Status.getError());
2746   EXPECT_TRUE(Status->exists());
2747 
2748   EC = FS->setCurrentWorkingDirectory("//root/bar");
2749   ASSERT_FALSE(EC);
2750 
2751   Status = FS->status("./baz/a");
2752   ASSERT_FALSE(Status.getError());
2753   EXPECT_TRUE(Status->exists());
2754 
2755   Status = FS->status("../bar/baz/a");
2756   ASSERT_FALSE(Status.getError());
2757   EXPECT_TRUE(Status->exists());
2758 }
2759 
2760 TEST_F(VFSFromYAMLTest, WorkingDirectoryFallthroughInvalid) {
2761   IntrusiveRefCntPtr<ErrorDummyFileSystem> Lower(new ErrorDummyFileSystem());
2762   Lower->addDirectory("//root/");
2763   Lower->addDirectory("//root/foo");
2764   Lower->addRegularFile("//root/foo/a");
2765   Lower->addRegularFile("//root/foo/b");
2766   Lower->addRegularFile("//root/c");
2767   IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
2768       "{ 'use-external-names': false,\n"
2769       "  'roots': [\n"
2770       "{\n"
2771       "  'type': 'directory',\n"
2772       "  'name': '//root/bar',\n"
2773       "  'contents': [ {\n"
2774       "                  'type': 'file',\n"
2775       "                  'name': 'a',\n"
2776       "                  'external-contents': '//root/foo/a'\n"
2777       "                }\n"
2778       "              ]\n"
2779       "}\n"
2780       "]\n"
2781       "}",
2782       Lower);
2783   ASSERT_NE(FS.get(), nullptr);
2784   std::error_code EC = FS->setCurrentWorkingDirectory("//root/");
2785   ASSERT_FALSE(EC);
2786   ASSERT_NE(FS.get(), nullptr);
2787 
2788   llvm::ErrorOr<vfs::Status> Status = FS->status("bar/a");
2789   ASSERT_FALSE(Status.getError());
2790   EXPECT_TRUE(Status->exists());
2791 
2792   Status = FS->status("foo/a");
2793   ASSERT_FALSE(Status.getError());
2794   EXPECT_TRUE(Status->exists());
2795 }
2796 
2797 TEST_F(VFSFromYAMLTest, VirtualWorkingDirectory) {
2798   IntrusiveRefCntPtr<ErrorDummyFileSystem> Lower(new ErrorDummyFileSystem());
2799   Lower->addDirectory("//root/");
2800   Lower->addDirectory("//root/foo");
2801   Lower->addRegularFile("//root/foo/a");
2802   Lower->addRegularFile("//root/foo/b");
2803   Lower->addRegularFile("//root/c");
2804   IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
2805       "{ 'use-external-names': false,\n"
2806       "  'roots': [\n"
2807       "{\n"
2808       "  'type': 'directory',\n"
2809       "  'name': '//root/bar',\n"
2810       "  'contents': [ {\n"
2811       "                  'type': 'file',\n"
2812       "                  'name': 'a',\n"
2813       "                  'external-contents': '//root/foo/a'\n"
2814       "                }\n"
2815       "              ]\n"
2816       "}\n"
2817       "]\n"
2818       "}",
2819       Lower);
2820   ASSERT_NE(FS.get(), nullptr);
2821   std::error_code EC = FS->setCurrentWorkingDirectory("//root/bar");
2822   ASSERT_FALSE(EC);
2823   ASSERT_NE(FS.get(), nullptr);
2824 
2825   llvm::ErrorOr<vfs::Status> Status = FS->status("a");
2826   ASSERT_FALSE(Status.getError());
2827   EXPECT_TRUE(Status->exists());
2828 }
2829 
2830 TEST_F(VFSFromYAMLTest, YAMLVFSWriterTest) {
2831   TempDir TestDirectory("virtual-file-system-test", /*Unique*/ true);
2832   TempDir _a(TestDirectory.path("a"));
2833   TempFile _ab(TestDirectory.path("a, b"));
2834   TempDir _c(TestDirectory.path("c"));
2835   TempFile _cd(TestDirectory.path("c/d"));
2836   TempDir _e(TestDirectory.path("e"));
2837   TempDir _ef(TestDirectory.path("e/f"));
2838   TempFile _g(TestDirectory.path("g"));
2839   TempDir _h(TestDirectory.path("h"));
2840 
2841   vfs::YAMLVFSWriter VFSWriter;
2842   VFSWriter.addDirectoryMapping(_a.path(), "//root/a");
2843   VFSWriter.addFileMapping(_ab.path(), "//root/a/b");
2844   VFSWriter.addFileMapping(_cd.path(), "//root/c/d");
2845   VFSWriter.addDirectoryMapping(_e.path(), "//root/e");
2846   VFSWriter.addDirectoryMapping(_ef.path(), "//root/e/f");
2847   VFSWriter.addFileMapping(_g.path(), "//root/g");
2848   VFSWriter.addDirectoryMapping(_h.path(), "//root/h");
2849 
2850   std::string Buffer;
2851   raw_string_ostream OS(Buffer);
2852   VFSWriter.write(OS);
2853   OS.flush();
2854 
2855   IntrusiveRefCntPtr<ErrorDummyFileSystem> Lower(new ErrorDummyFileSystem());
2856   Lower->addDirectory("//root/");
2857   Lower->addDirectory("//root/a");
2858   Lower->addRegularFile("//root/a/b");
2859   Lower->addDirectory("//root/b");
2860   Lower->addDirectory("//root/c");
2861   Lower->addRegularFile("//root/c/d");
2862   Lower->addDirectory("//root/e");
2863   Lower->addDirectory("//root/e/f");
2864   Lower->addRegularFile("//root/g");
2865   Lower->addDirectory("//root/h");
2866 
2867   IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLRawString(Buffer, Lower);
2868   ASSERT_NE(FS.get(), nullptr);
2869 
2870   EXPECT_TRUE(FS->exists(_a.path()));
2871   EXPECT_TRUE(FS->exists(_ab.path()));
2872   EXPECT_TRUE(FS->exists(_c.path()));
2873   EXPECT_TRUE(FS->exists(_cd.path()));
2874   EXPECT_TRUE(FS->exists(_e.path()));
2875   EXPECT_TRUE(FS->exists(_ef.path()));
2876   EXPECT_TRUE(FS->exists(_g.path()));
2877   EXPECT_TRUE(FS->exists(_h.path()));
2878 }
2879 
2880 TEST_F(VFSFromYAMLTest, YAMLVFSWriterTest2) {
2881   TempDir TestDirectory("virtual-file-system-test", /*Unique*/ true);
2882   TempDir _a(TestDirectory.path("a"));
2883   TempFile _ab(TestDirectory.path("a/b"));
2884   TempDir _ac(TestDirectory.path("a/c"));
2885   TempFile _acd(TestDirectory.path("a/c/d"));
2886   TempFile _ace(TestDirectory.path("a/c/e"));
2887   TempFile _acf(TestDirectory.path("a/c/f"));
2888   TempDir _ag(TestDirectory.path("a/g"));
2889   TempFile _agh(TestDirectory.path("a/g/h"));
2890 
2891   vfs::YAMLVFSWriter VFSWriter;
2892   VFSWriter.addDirectoryMapping(_a.path(), "//root/a");
2893   VFSWriter.addFileMapping(_ab.path(), "//root/a/b");
2894   VFSWriter.addDirectoryMapping(_ac.path(), "//root/a/c");
2895   VFSWriter.addFileMapping(_acd.path(), "//root/a/c/d");
2896   VFSWriter.addFileMapping(_ace.path(), "//root/a/c/e");
2897   VFSWriter.addFileMapping(_acf.path(), "//root/a/c/f");
2898   VFSWriter.addDirectoryMapping(_ag.path(), "//root/a/g");
2899   VFSWriter.addFileMapping(_agh.path(), "//root/a/g/h");
2900 
2901   std::string Buffer;
2902   raw_string_ostream OS(Buffer);
2903   VFSWriter.write(OS);
2904   OS.flush();
2905 
2906   IntrusiveRefCntPtr<ErrorDummyFileSystem> Lower(new ErrorDummyFileSystem());
2907   IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLRawString(Buffer, Lower);
2908   EXPECT_NE(FS.get(), nullptr);
2909 }
2910 
2911 TEST_F(VFSFromYAMLTest, YAMLVFSWriterTest3) {
2912   TempDir TestDirectory("virtual-file-system-test", /*Unique*/ true);
2913   TempDir _a(TestDirectory.path("a"));
2914   TempFile _ab(TestDirectory.path("a/b"));
2915   TempDir _ac(TestDirectory.path("a/c"));
2916   TempDir _acd(TestDirectory.path("a/c/d"));
2917   TempDir _acde(TestDirectory.path("a/c/d/e"));
2918   TempFile _acdef(TestDirectory.path("a/c/d/e/f"));
2919   TempFile _acdeg(TestDirectory.path("a/c/d/e/g"));
2920   TempDir _ah(TestDirectory.path("a/h"));
2921   TempFile _ahi(TestDirectory.path("a/h/i"));
2922 
2923   vfs::YAMLVFSWriter VFSWriter;
2924   VFSWriter.addDirectoryMapping(_a.path(), "//root/a");
2925   VFSWriter.addFileMapping(_ab.path(), "//root/a/b");
2926   VFSWriter.addDirectoryMapping(_ac.path(), "//root/a/c");
2927   VFSWriter.addDirectoryMapping(_acd.path(), "//root/a/c/d");
2928   VFSWriter.addDirectoryMapping(_acde.path(), "//root/a/c/d/e");
2929   VFSWriter.addFileMapping(_acdef.path(), "//root/a/c/d/e/f");
2930   VFSWriter.addFileMapping(_acdeg.path(), "//root/a/c/d/e/g");
2931   VFSWriter.addDirectoryMapping(_ahi.path(), "//root/a/h");
2932   VFSWriter.addFileMapping(_ahi.path(), "//root/a/h/i");
2933 
2934   std::string Buffer;
2935   raw_string_ostream OS(Buffer);
2936   VFSWriter.write(OS);
2937   OS.flush();
2938 
2939   IntrusiveRefCntPtr<ErrorDummyFileSystem> Lower(new ErrorDummyFileSystem());
2940   IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLRawString(Buffer, Lower);
2941   EXPECT_NE(FS.get(), nullptr);
2942 }
2943 
2944 TEST_F(VFSFromYAMLTest, YAMLVFSWriterTestHandleDirs) {
2945   TempDir TestDirectory("virtual-file-system-test", /*Unique*/ true);
2946   TempDir _a(TestDirectory.path("a"));
2947   TempDir _b(TestDirectory.path("b"));
2948   TempDir _c(TestDirectory.path("c"));
2949 
2950   vfs::YAMLVFSWriter VFSWriter;
2951   VFSWriter.addDirectoryMapping(_a.path(), "//root/a");
2952   VFSWriter.addDirectoryMapping(_b.path(), "//root/b");
2953   VFSWriter.addDirectoryMapping(_c.path(), "//root/c");
2954 
2955   std::string Buffer;
2956   raw_string_ostream OS(Buffer);
2957   VFSWriter.write(OS);
2958   OS.flush();
2959 
2960   // We didn't add a single file - only directories.
2961   EXPECT_EQ(Buffer.find("'type': 'file'"), std::string::npos);
2962 
2963   IntrusiveRefCntPtr<ErrorDummyFileSystem> Lower(new ErrorDummyFileSystem());
2964   Lower->addDirectory("//root/a");
2965   Lower->addDirectory("//root/b");
2966   Lower->addDirectory("//root/c");
2967   // canaries
2968   Lower->addRegularFile("//root/a/a");
2969   Lower->addRegularFile("//root/b/b");
2970   Lower->addRegularFile("//root/c/c");
2971 
2972   IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLRawString(Buffer, Lower);
2973   ASSERT_NE(FS.get(), nullptr);
2974 
2975   EXPECT_FALSE(FS->exists(_a.path("a")));
2976   EXPECT_FALSE(FS->exists(_b.path("b")));
2977   EXPECT_FALSE(FS->exists(_c.path("c")));
2978 }
2979 
2980 TEST_F(VFSFromYAMLTest, RedirectingWith) {
2981   IntrusiveRefCntPtr<DummyFileSystem> Both(new DummyFileSystem());
2982   Both->addDirectory("//root/a");
2983   Both->addRegularFile("//root/a/f");
2984   Both->addDirectory("//root/b");
2985   Both->addRegularFile("//root/b/f");
2986 
2987   IntrusiveRefCntPtr<DummyFileSystem> AOnly(new DummyFileSystem());
2988   AOnly->addDirectory("//root/a");
2989   AOnly->addRegularFile("//root/a/f");
2990 
2991   IntrusiveRefCntPtr<DummyFileSystem> BOnly(new DummyFileSystem());
2992   BOnly->addDirectory("//root/b");
2993   BOnly->addRegularFile("//root/b/f");
2994 
2995   auto BaseStr = std::string("  'roots': [\n"
2996                              "    {\n"
2997                              "      'type': 'directory-remap',\n"
2998                              "      'name': '//root/a',\n"
2999                              "      'external-contents': '//root/b'\n"
3000                              "    }\n"
3001                              "  ]\n"
3002                              "}");
3003   auto FallthroughStr = "{ 'redirecting-with': 'fallthrough',\n" + BaseStr;
3004   auto FallbackStr = "{ 'redirecting-with': 'fallback',\n" + BaseStr;
3005   auto RedirectOnlyStr = "{ 'redirecting-with': 'redirect-only',\n" + BaseStr;
3006 
3007   auto ExpectPath = [&](vfs::FileSystem &FS, StringRef Expected,
3008                         StringRef Message) {
3009     auto AF = FS.openFileForRead("//root/a/f");
3010     ASSERT_FALSE(AF.getError()) << Message;
3011     auto AFName = (*AF)->getName();
3012     ASSERT_FALSE(AFName.getError()) << Message;
3013     EXPECT_EQ(Expected.str(), AFName.get()) << Message;
3014 
3015     auto AS = FS.status("//root/a/f");
3016     ASSERT_FALSE(AS.getError()) << Message;
3017     EXPECT_EQ(Expected.str(), AS->getName()) << Message;
3018   };
3019 
3020   auto ExpectFailure = [&](vfs::FileSystem &FS, StringRef Message) {
3021     EXPECT_TRUE(FS.openFileForRead("//root/a/f").getError()) << Message;
3022     EXPECT_TRUE(FS.status("//root/a/f").getError()) << Message;
3023   };
3024 
3025   {
3026     // `f` in both `a` and `b`
3027 
3028     // `fallthrough` tries `external-name` first, so should be `b`
3029     IntrusiveRefCntPtr<vfs::FileSystem> Fallthrough =
3030         getFromYAMLString(FallthroughStr, Both);
3031     ASSERT_TRUE(Fallthrough.get() != nullptr);
3032     ExpectPath(*Fallthrough, "//root/b/f", "fallthrough, both exist");
3033 
3034     // `fallback` tries the original name first, so should be `a`
3035     IntrusiveRefCntPtr<vfs::FileSystem> Fallback =
3036         getFromYAMLString(FallbackStr, Both);
3037     ASSERT_TRUE(Fallback.get() != nullptr);
3038     ExpectPath(*Fallback, "//root/a/f", "fallback, both exist");
3039 
3040     // `redirect-only` is the same as `fallthrough` but doesn't try the
3041     // original on failure, so no change here (ie. `b`)
3042     IntrusiveRefCntPtr<vfs::FileSystem> Redirect =
3043         getFromYAMLString(RedirectOnlyStr, Both);
3044     ASSERT_TRUE(Redirect.get() != nullptr);
3045     ExpectPath(*Redirect, "//root/b/f", "redirect-only, both exist");
3046   }
3047 
3048   {
3049     // `f` in `a` only
3050 
3051     // Fallthrough to the original path, `a`
3052     IntrusiveRefCntPtr<vfs::FileSystem> Fallthrough =
3053         getFromYAMLString(FallthroughStr, AOnly);
3054     ASSERT_TRUE(Fallthrough.get() != nullptr);
3055     ExpectPath(*Fallthrough, "//root/a/f", "fallthrough, a only");
3056 
3057     // Original first, so still `a`
3058     IntrusiveRefCntPtr<vfs::FileSystem> Fallback =
3059         getFromYAMLString(FallbackStr, AOnly);
3060     ASSERT_TRUE(Fallback.get() != nullptr);
3061     ExpectPath(*Fallback, "//root/a/f", "fallback, a only");
3062 
3063     // Fails since no fallthrough
3064     IntrusiveRefCntPtr<vfs::FileSystem> Redirect =
3065         getFromYAMLString(RedirectOnlyStr, AOnly);
3066     ASSERT_TRUE(Redirect.get() != nullptr);
3067     ExpectFailure(*Redirect, "redirect-only, a only");
3068   }
3069 
3070   {
3071     // `f` in `b` only
3072 
3073     // Tries `b` first (no fallthrough)
3074     IntrusiveRefCntPtr<vfs::FileSystem> Fallthrough =
3075         getFromYAMLString(FallthroughStr, BOnly);
3076     ASSERT_TRUE(Fallthrough.get() != nullptr);
3077     ExpectPath(*Fallthrough, "//root/b/f", "fallthrough, b only");
3078 
3079     // Tries original first but then fallsback to `b`
3080     IntrusiveRefCntPtr<vfs::FileSystem> Fallback =
3081         getFromYAMLString(FallbackStr, BOnly);
3082     ASSERT_TRUE(Fallback.get() != nullptr);
3083     ExpectPath(*Fallback, "//root/b/f", "fallback, b only");
3084 
3085     // Redirect exists, so uses it (`b`)
3086     IntrusiveRefCntPtr<vfs::FileSystem> Redirect =
3087         getFromYAMLString(RedirectOnlyStr, BOnly);
3088     ASSERT_TRUE(Redirect.get() != nullptr);
3089     ExpectPath(*Redirect, "//root/b/f", "redirect-only, b only");
3090   }
3091 
3092   EXPECT_EQ(0, NumDiagnostics);
3093 }
3094 
3095 TEST(VFSFromRemappedFilesTest, Basic) {
3096   IntrusiveRefCntPtr<vfs::InMemoryFileSystem> BaseFS =
3097       new vfs::InMemoryFileSystem;
3098   BaseFS->addFile("//root/b", 0, MemoryBuffer::getMemBuffer("contents of b"));
3099   BaseFS->addFile("//root/c", 0, MemoryBuffer::getMemBuffer("contents of c"));
3100 
3101   std::vector<std::pair<std::string, std::string>> RemappedFiles = {
3102       {"//root/a/a", "//root/b"},
3103       {"//root/a/b/c", "//root/c"},
3104   };
3105   auto RemappedFS = vfs::RedirectingFileSystem::create(
3106       RemappedFiles, /*UseExternalNames=*/false, *BaseFS);
3107 
3108   auto StatA = RemappedFS->status("//root/a/a");
3109   auto StatB = RemappedFS->status("//root/a/b/c");
3110   ASSERT_TRUE(StatA);
3111   ASSERT_TRUE(StatB);
3112   EXPECT_EQ("//root/a/a", StatA->getName());
3113   EXPECT_EQ("//root/a/b/c", StatB->getName());
3114 
3115   auto BufferA = RemappedFS->getBufferForFile("//root/a/a");
3116   auto BufferB = RemappedFS->getBufferForFile("//root/a/b/c");
3117   ASSERT_TRUE(BufferA);
3118   ASSERT_TRUE(BufferB);
3119   EXPECT_EQ("contents of b", (*BufferA)->getBuffer());
3120   EXPECT_EQ("contents of c", (*BufferB)->getBuffer());
3121 }
3122 
3123 TEST(VFSFromRemappedFilesTest, UseExternalNames) {
3124   IntrusiveRefCntPtr<vfs::InMemoryFileSystem> BaseFS =
3125       new vfs::InMemoryFileSystem;
3126   BaseFS->addFile("//root/b", 0, MemoryBuffer::getMemBuffer("contents of b"));
3127   BaseFS->addFile("//root/c", 0, MemoryBuffer::getMemBuffer("contents of c"));
3128 
3129   std::vector<std::pair<std::string, std::string>> RemappedFiles = {
3130       {"//root/a/a", "//root/b"},
3131       {"//root/a/b/c", "//root/c"},
3132   };
3133   auto RemappedFS = vfs::RedirectingFileSystem::create(
3134       RemappedFiles, /*UseExternalNames=*/true, *BaseFS);
3135 
3136   auto StatA = RemappedFS->status("//root/a/a");
3137   auto StatB = RemappedFS->status("//root/a/b/c");
3138   ASSERT_TRUE(StatA);
3139   ASSERT_TRUE(StatB);
3140   EXPECT_EQ("//root/b", StatA->getName());
3141   EXPECT_EQ("//root/c", StatB->getName());
3142 
3143   auto BufferA = RemappedFS->getBufferForFile("//root/a/a");
3144   auto BufferB = RemappedFS->getBufferForFile("//root/a/b/c");
3145   ASSERT_TRUE(BufferA);
3146   ASSERT_TRUE(BufferB);
3147   EXPECT_EQ("contents of b", (*BufferA)->getBuffer());
3148   EXPECT_EQ("contents of c", (*BufferB)->getBuffer());
3149 }
3150 
3151 TEST(VFSFromRemappedFilesTest, LastMappingWins) {
3152   IntrusiveRefCntPtr<vfs::InMemoryFileSystem> BaseFS =
3153       new vfs::InMemoryFileSystem;
3154   BaseFS->addFile("//root/b", 0, MemoryBuffer::getMemBuffer("contents of b"));
3155   BaseFS->addFile("//root/c", 0, MemoryBuffer::getMemBuffer("contents of c"));
3156 
3157   std::vector<std::pair<std::string, std::string>> RemappedFiles = {
3158       {"//root/a", "//root/b"},
3159       {"//root/a", "//root/c"},
3160   };
3161   auto RemappedFSKeepName = vfs::RedirectingFileSystem::create(
3162       RemappedFiles, /*UseExternalNames=*/false, *BaseFS);
3163   auto RemappedFSExternalName = vfs::RedirectingFileSystem::create(
3164       RemappedFiles, /*UseExternalNames=*/true, *BaseFS);
3165 
3166   auto StatKeepA = RemappedFSKeepName->status("//root/a");
3167   auto StatExternalA = RemappedFSExternalName->status("//root/a");
3168   ASSERT_TRUE(StatKeepA);
3169   ASSERT_TRUE(StatExternalA);
3170   EXPECT_EQ("//root/a", StatKeepA->getName());
3171   EXPECT_EQ("//root/c", StatExternalA->getName());
3172 
3173   auto BufferKeepA = RemappedFSKeepName->getBufferForFile("//root/a");
3174   auto BufferExternalA = RemappedFSExternalName->getBufferForFile("//root/a");
3175   ASSERT_TRUE(BufferKeepA);
3176   ASSERT_TRUE(BufferExternalA);
3177   EXPECT_EQ("contents of c", (*BufferKeepA)->getBuffer());
3178   EXPECT_EQ("contents of c", (*BufferExternalA)->getBuffer());
3179 }
3180 
3181 TEST(RedirectingFileSystemTest, PrintOutput) {
3182   auto Buffer =
3183       MemoryBuffer::getMemBuffer("{\n"
3184                                  "  'version': 0,\n"
3185                                  "  'roots': [\n"
3186                                  "    {\n"
3187                                  "      'type': 'directory-remap',\n"
3188                                  "      'name': '/dremap',\n"
3189                                  "      'external-contents': '/a',\n"
3190                                  "    },"
3191                                  "    {\n"
3192                                  "      'type': 'directory',\n"
3193                                  "      'name': '/vdir',\n"
3194                                  "      'contents': ["
3195                                  "        {\n"
3196                                  "          'type': 'directory-remap',\n"
3197                                  "          'name': 'dremap',\n"
3198                                  "          'external-contents': '/b'\n"
3199                                  "          'use-external-name': 'true'\n"
3200                                  "        },\n"
3201                                  "        {\n"
3202                                  "          'type': 'file',\n"
3203                                  "          'name': 'vfile',\n"
3204                                  "          'external-contents': '/c'\n"
3205                                  "          'use-external-name': 'false'\n"
3206                                  "        }]\n"
3207                                  "    }]\n"
3208                                  "}");
3209 
3210   auto Dummy = makeIntrusiveRefCnt<DummyFileSystem>();
3211   auto Redirecting = vfs::RedirectingFileSystem::create(
3212       std::move(Buffer), nullptr, "", nullptr, Dummy);
3213 
3214   SmallString<0> Output;
3215   raw_svector_ostream OuputStream{Output};
3216 
3217   Redirecting->print(OuputStream, vfs::FileSystem::PrintType::Summary);
3218   ASSERT_EQ("RedirectingFileSystem (UseExternalNames: true)\n", Output);
3219 
3220   Output.clear();
3221   Redirecting->print(OuputStream, vfs::FileSystem::PrintType::Contents);
3222   ASSERT_EQ("RedirectingFileSystem (UseExternalNames: true)\n"
3223             "'/'\n"
3224             "  'dremap' -> '/a'\n"
3225             "  'vdir'\n"
3226             "    'dremap' -> '/b' (UseExternalName: true)\n"
3227             "    'vfile' -> '/c' (UseExternalName: false)\n"
3228             "ExternalFS:\n"
3229             "  DummyFileSystem (Summary)\n",
3230             Output);
3231 
3232   Output.clear();
3233   Redirecting->print(OuputStream, vfs::FileSystem::PrintType::Contents, 1);
3234   ASSERT_EQ("  RedirectingFileSystem (UseExternalNames: true)\n"
3235             "  '/'\n"
3236             "    'dremap' -> '/a'\n"
3237             "    'vdir'\n"
3238             "      'dremap' -> '/b' (UseExternalName: true)\n"
3239             "      'vfile' -> '/c' (UseExternalName: false)\n"
3240             "  ExternalFS:\n"
3241             "    DummyFileSystem (Summary)\n",
3242             Output);
3243 
3244   Output.clear();
3245   Redirecting->print(OuputStream,
3246                      vfs::FileSystem::PrintType::RecursiveContents);
3247   ASSERT_EQ("RedirectingFileSystem (UseExternalNames: true)\n"
3248             "'/'\n"
3249             "  'dremap' -> '/a'\n"
3250             "  'vdir'\n"
3251             "    'dremap' -> '/b' (UseExternalName: true)\n"
3252             "    'vfile' -> '/c' (UseExternalName: false)\n"
3253             "ExternalFS:\n"
3254             "  DummyFileSystem (RecursiveContents)\n",
3255             Output);
3256 }
3257