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