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