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