xref: /llvm-project/llvm/unittests/Support/VirtualFileSystemTest.cpp (revision c2b310aedf69f83c0b7252a969a6bda023c9de34)
1 //===- unittests/Support/VirtualFileSystem.cpp -------------- VFS tests ---===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 
10 #include "llvm/Support/VirtualFileSystem.h"
11 #include "llvm/ADT/Triple.h"
12 #include "llvm/Config/llvm-config.h"
13 #include "llvm/Support/Errc.h"
14 #include "llvm/Support/Host.h"
15 #include "llvm/Support/MemoryBuffer.h"
16 #include "llvm/Support/Path.h"
17 #include "llvm/Support/SourceMgr.h"
18 #include "gmock/gmock.h"
19 #include "gtest/gtest.h"
20 #include <map>
21 #include <string>
22 
23 using namespace llvm;
24 using llvm::sys::fs::UniqueID;
25 using testing::ElementsAre;
26 using testing::Pair;
27 using testing::UnorderedElementsAre;
28 
29 namespace {
30 struct DummyFile : public vfs::File {
31   vfs::Status S;
32   explicit DummyFile(vfs::Status S) : S(S) {}
33   llvm::ErrorOr<vfs::Status> status() override { return S; }
34   llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
35   getBuffer(const Twine &Name, int64_t FileSize, bool RequiresNullTerminator,
36             bool IsVolatile) override {
37     llvm_unreachable("unimplemented");
38   }
39   std::error_code close() override { return std::error_code(); }
40 };
41 
42 class DummyFileSystem : public vfs::FileSystem {
43   int FSID;   // used to produce UniqueIDs
44   int FileID; // used to produce UniqueIDs
45   std::map<std::string, vfs::Status> FilesAndDirs;
46 
47   static int getNextFSID() {
48     static int Count = 0;
49     return Count++;
50   }
51 
52 public:
53   DummyFileSystem() : FSID(getNextFSID()), FileID(0) {}
54 
55   ErrorOr<vfs::Status> status(const Twine &Path) override {
56     std::map<std::string, vfs::Status>::iterator I =
57         FilesAndDirs.find(Path.str());
58     if (I == FilesAndDirs.end())
59       return make_error_code(llvm::errc::no_such_file_or_directory);
60     return I->second;
61   }
62   ErrorOr<std::unique_ptr<vfs::File>>
63   openFileForRead(const Twine &Path) override {
64     auto S = status(Path);
65     if (S)
66       return std::unique_ptr<vfs::File>(new DummyFile{*S});
67     return S.getError();
68   }
69   llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const override {
70     return std::string();
71   }
72   std::error_code setCurrentWorkingDirectory(const Twine &Path) override {
73     return std::error_code();
74   }
75   // Map any symlink to "/symlink".
76   std::error_code getRealPath(const Twine &Path,
77                               SmallVectorImpl<char> &Output) const override {
78     auto I = FilesAndDirs.find(Path.str());
79     if (I == FilesAndDirs.end())
80       return make_error_code(llvm::errc::no_such_file_or_directory);
81     if (I->second.isSymlink()) {
82       Output.clear();
83       Twine("/symlink").toVector(Output);
84       return std::error_code();
85     }
86     Output.clear();
87     Path.toVector(Output);
88     return std::error_code();
89   }
90 
91   struct DirIterImpl : public llvm::vfs::detail::DirIterImpl {
92     std::map<std::string, vfs::Status> &FilesAndDirs;
93     std::map<std::string, vfs::Status>::iterator I;
94     std::string Path;
95     bool isInPath(StringRef S) {
96       if (Path.size() < S.size() && S.find(Path) == 0) {
97         auto LastSep = S.find_last_of('/');
98         if (LastSep == Path.size() || LastSep == Path.size() - 1)
99           return true;
100       }
101       return false;
102     }
103     DirIterImpl(std::map<std::string, vfs::Status> &FilesAndDirs,
104                 const Twine &_Path)
105         : FilesAndDirs(FilesAndDirs), I(FilesAndDirs.begin()),
106           Path(_Path.str()) {
107       for (; I != FilesAndDirs.end(); ++I) {
108         if (isInPath(I->first)) {
109           CurrentEntry =
110               vfs::directory_entry(I->second.getName(), I->second.getType());
111           break;
112         }
113       }
114     }
115     std::error_code increment() override {
116       ++I;
117       for (; I != FilesAndDirs.end(); ++I) {
118         if (isInPath(I->first)) {
119           CurrentEntry =
120               vfs::directory_entry(I->second.getName(), I->second.getType());
121           break;
122         }
123       }
124       if (I == FilesAndDirs.end())
125         CurrentEntry = vfs::directory_entry();
126       return std::error_code();
127     }
128   };
129 
130   vfs::directory_iterator dir_begin(const Twine &Dir,
131                                     std::error_code &EC) override {
132     return vfs::directory_iterator(
133         std::make_shared<DirIterImpl>(FilesAndDirs, Dir));
134   }
135 
136   void addEntry(StringRef Path, const vfs::Status &Status) {
137     FilesAndDirs[Path] = Status;
138   }
139 
140   void addRegularFile(StringRef Path, sys::fs::perms Perms = sys::fs::all_all) {
141     vfs::Status S(Path, UniqueID(FSID, FileID++),
142                   std::chrono::system_clock::now(), 0, 0, 1024,
143                   sys::fs::file_type::regular_file, Perms);
144     addEntry(Path, S);
145   }
146 
147   void addDirectory(StringRef Path, sys::fs::perms Perms = sys::fs::all_all) {
148     vfs::Status S(Path, UniqueID(FSID, FileID++),
149                   std::chrono::system_clock::now(), 0, 0, 0,
150                   sys::fs::file_type::directory_file, Perms);
151     addEntry(Path, S);
152   }
153 
154   void addSymlink(StringRef Path) {
155     vfs::Status S(Path, UniqueID(FSID, FileID++),
156                   std::chrono::system_clock::now(), 0, 0, 0,
157                   sys::fs::file_type::symlink_file, sys::fs::all_all);
158     addEntry(Path, S);
159   }
160 };
161 
162 /// Replace back-slashes by front-slashes.
163 std::string getPosixPath(std::string S) {
164   SmallString<128> Result;
165   llvm::sys::path::native(S, Result, llvm::sys::path::Style::posix);
166   return Result.str();
167 }
168 } // end anonymous namespace
169 
170 TEST(VirtualFileSystemTest, StatusQueries) {
171   IntrusiveRefCntPtr<DummyFileSystem> D(new DummyFileSystem());
172   ErrorOr<vfs::Status> Status((std::error_code()));
173 
174   D->addRegularFile("/foo");
175   Status = D->status("/foo");
176   ASSERT_FALSE(Status.getError());
177   EXPECT_TRUE(Status->isStatusKnown());
178   EXPECT_FALSE(Status->isDirectory());
179   EXPECT_TRUE(Status->isRegularFile());
180   EXPECT_FALSE(Status->isSymlink());
181   EXPECT_FALSE(Status->isOther());
182   EXPECT_TRUE(Status->exists());
183 
184   D->addDirectory("/bar");
185   Status = D->status("/bar");
186   ASSERT_FALSE(Status.getError());
187   EXPECT_TRUE(Status->isStatusKnown());
188   EXPECT_TRUE(Status->isDirectory());
189   EXPECT_FALSE(Status->isRegularFile());
190   EXPECT_FALSE(Status->isSymlink());
191   EXPECT_FALSE(Status->isOther());
192   EXPECT_TRUE(Status->exists());
193 
194   D->addSymlink("/baz");
195   Status = D->status("/baz");
196   ASSERT_FALSE(Status.getError());
197   EXPECT_TRUE(Status->isStatusKnown());
198   EXPECT_FALSE(Status->isDirectory());
199   EXPECT_FALSE(Status->isRegularFile());
200   EXPECT_TRUE(Status->isSymlink());
201   EXPECT_FALSE(Status->isOther());
202   EXPECT_TRUE(Status->exists());
203 
204   EXPECT_TRUE(Status->equivalent(*Status));
205   ErrorOr<vfs::Status> Status2 = D->status("/foo");
206   ASSERT_FALSE(Status2.getError());
207   EXPECT_FALSE(Status->equivalent(*Status2));
208 }
209 
210 TEST(VirtualFileSystemTest, BaseOnlyOverlay) {
211   IntrusiveRefCntPtr<DummyFileSystem> D(new DummyFileSystem());
212   ErrorOr<vfs::Status> Status((std::error_code()));
213   EXPECT_FALSE(Status = D->status("/foo"));
214 
215   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(new vfs::OverlayFileSystem(D));
216   EXPECT_FALSE(Status = O->status("/foo"));
217 
218   D->addRegularFile("/foo");
219   Status = D->status("/foo");
220   EXPECT_FALSE(Status.getError());
221 
222   ErrorOr<vfs::Status> Status2((std::error_code()));
223   Status2 = O->status("/foo");
224   EXPECT_FALSE(Status2.getError());
225   EXPECT_TRUE(Status->equivalent(*Status2));
226 }
227 
228 TEST(VirtualFileSystemTest, GetRealPathInOverlay) {
229   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
230   Lower->addRegularFile("/foo");
231   Lower->addSymlink("/lower_link");
232   IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem());
233 
234   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
235       new vfs::OverlayFileSystem(Lower));
236   O->pushOverlay(Upper);
237 
238   // Regular file.
239   SmallString<16> RealPath;
240   EXPECT_FALSE(O->getRealPath("/foo", RealPath));
241   EXPECT_EQ(RealPath.str(), "/foo");
242 
243   // Expect no error getting real path for symlink in lower overlay.
244   EXPECT_FALSE(O->getRealPath("/lower_link", RealPath));
245   EXPECT_EQ(RealPath.str(), "/symlink");
246 
247   // Try a non-existing link.
248   EXPECT_EQ(O->getRealPath("/upper_link", RealPath),
249             errc::no_such_file_or_directory);
250 
251   // Add a new symlink in upper.
252   Upper->addSymlink("/upper_link");
253   EXPECT_FALSE(O->getRealPath("/upper_link", RealPath));
254   EXPECT_EQ(RealPath.str(), "/symlink");
255 }
256 
257 TEST(VirtualFileSystemTest, OverlayFiles) {
258   IntrusiveRefCntPtr<DummyFileSystem> Base(new DummyFileSystem());
259   IntrusiveRefCntPtr<DummyFileSystem> Middle(new DummyFileSystem());
260   IntrusiveRefCntPtr<DummyFileSystem> Top(new DummyFileSystem());
261   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
262       new vfs::OverlayFileSystem(Base));
263   O->pushOverlay(Middle);
264   O->pushOverlay(Top);
265 
266   ErrorOr<vfs::Status> Status1((std::error_code())),
267       Status2((std::error_code())), Status3((std::error_code())),
268       StatusB((std::error_code())), StatusM((std::error_code())),
269       StatusT((std::error_code()));
270 
271   Base->addRegularFile("/foo");
272   StatusB = Base->status("/foo");
273   ASSERT_FALSE(StatusB.getError());
274   Status1 = O->status("/foo");
275   ASSERT_FALSE(Status1.getError());
276   Middle->addRegularFile("/foo");
277   StatusM = Middle->status("/foo");
278   ASSERT_FALSE(StatusM.getError());
279   Status2 = O->status("/foo");
280   ASSERT_FALSE(Status2.getError());
281   Top->addRegularFile("/foo");
282   StatusT = Top->status("/foo");
283   ASSERT_FALSE(StatusT.getError());
284   Status3 = O->status("/foo");
285   ASSERT_FALSE(Status3.getError());
286 
287   EXPECT_TRUE(Status1->equivalent(*StatusB));
288   EXPECT_TRUE(Status2->equivalent(*StatusM));
289   EXPECT_TRUE(Status3->equivalent(*StatusT));
290 
291   EXPECT_FALSE(Status1->equivalent(*Status2));
292   EXPECT_FALSE(Status2->equivalent(*Status3));
293   EXPECT_FALSE(Status1->equivalent(*Status3));
294 }
295 
296 TEST(VirtualFileSystemTest, OverlayDirsNonMerged) {
297   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
298   IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem());
299   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
300       new vfs::OverlayFileSystem(Lower));
301   O->pushOverlay(Upper);
302 
303   Lower->addDirectory("/lower-only");
304   Upper->addDirectory("/upper-only");
305 
306   // non-merged paths should be the same
307   ErrorOr<vfs::Status> Status1 = Lower->status("/lower-only");
308   ASSERT_FALSE(Status1.getError());
309   ErrorOr<vfs::Status> Status2 = O->status("/lower-only");
310   ASSERT_FALSE(Status2.getError());
311   EXPECT_TRUE(Status1->equivalent(*Status2));
312 
313   Status1 = Upper->status("/upper-only");
314   ASSERT_FALSE(Status1.getError());
315   Status2 = O->status("/upper-only");
316   ASSERT_FALSE(Status2.getError());
317   EXPECT_TRUE(Status1->equivalent(*Status2));
318 }
319 
320 TEST(VirtualFileSystemTest, MergedDirPermissions) {
321   // merged directories get the permissions of the upper dir
322   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
323   IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem());
324   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
325       new vfs::OverlayFileSystem(Lower));
326   O->pushOverlay(Upper);
327 
328   ErrorOr<vfs::Status> Status((std::error_code()));
329   Lower->addDirectory("/both", sys::fs::owner_read);
330   Upper->addDirectory("/both", sys::fs::owner_all | sys::fs::group_read);
331   Status = O->status("/both");
332   ASSERT_FALSE(Status.getError());
333   EXPECT_EQ(0740, Status->getPermissions());
334 
335   // permissions (as usual) are not recursively applied
336   Lower->addRegularFile("/both/foo", sys::fs::owner_read);
337   Upper->addRegularFile("/both/bar", sys::fs::owner_write);
338   Status = O->status("/both/foo");
339   ASSERT_FALSE(Status.getError());
340   EXPECT_EQ(0400, Status->getPermissions());
341   Status = O->status("/both/bar");
342   ASSERT_FALSE(Status.getError());
343   EXPECT_EQ(0200, Status->getPermissions());
344 }
345 
346 namespace {
347 struct ScopedDir {
348   SmallString<128> Path;
349   ScopedDir(const Twine &Name, bool Unique = false) {
350     std::error_code EC;
351     if (Unique) {
352       EC = llvm::sys::fs::createUniqueDirectory(Name, Path);
353     } else {
354       Path = Name.str();
355       EC = llvm::sys::fs::create_directory(Twine(Path));
356     }
357     if (EC)
358       Path = "";
359     EXPECT_FALSE(EC);
360   }
361   ~ScopedDir() {
362     if (Path != "") {
363       EXPECT_FALSE(llvm::sys::fs::remove(Path.str()));
364     }
365   }
366   operator StringRef() { return Path.str(); }
367 };
368 
369 struct ScopedLink {
370   SmallString<128> Path;
371   ScopedLink(const Twine &To, const Twine &From) {
372     Path = From.str();
373     std::error_code EC = sys::fs::create_link(To, From);
374     if (EC)
375       Path = "";
376     EXPECT_FALSE(EC);
377   }
378   ~ScopedLink() {
379     if (Path != "") {
380       EXPECT_FALSE(llvm::sys::fs::remove(Path.str()));
381     }
382   }
383   operator StringRef() { return Path.str(); }
384 };
385 
386 struct ScopedFile {
387   SmallString<128> Path;
388   ScopedFile(const Twine &Path, StringRef Contents) {
389     Path.toVector(this->Path);
390     std::error_code EC;
391     raw_fd_ostream OS(this->Path, EC);
392     EXPECT_FALSE(EC);
393     OS << Contents;
394     OS.flush();
395     EXPECT_FALSE(OS.error());
396     if (EC || OS.error())
397       this->Path = "";
398   }
399   ~ScopedFile() {
400     if (Path != "")
401       EXPECT_FALSE(llvm::sys::fs::remove(Path.str()));
402   }
403 };
404 } // end anonymous namespace
405 
406 TEST(VirtualFileSystemTest, BasicRealFSIteration) {
407   ScopedDir TestDirectory("virtual-file-system-test", /*Unique*/ true);
408   IntrusiveRefCntPtr<vfs::FileSystem> FS = vfs::getRealFileSystem();
409 
410   std::error_code EC;
411   vfs::directory_iterator I = FS->dir_begin(Twine(TestDirectory), EC);
412   ASSERT_FALSE(EC);
413   EXPECT_EQ(vfs::directory_iterator(), I); // empty directory is empty
414 
415   ScopedDir _a(TestDirectory + "/a");
416   ScopedDir _ab(TestDirectory + "/a/b");
417   ScopedDir _c(TestDirectory + "/c");
418   ScopedDir _cd(TestDirectory + "/c/d");
419 
420   I = FS->dir_begin(Twine(TestDirectory), EC);
421   ASSERT_FALSE(EC);
422   ASSERT_NE(vfs::directory_iterator(), I);
423   // Check either a or c, since we can't rely on the iteration order.
424   EXPECT_TRUE(I->path().endswith("a") || I->path().endswith("c"));
425   I.increment(EC);
426   ASSERT_FALSE(EC);
427   ASSERT_NE(vfs::directory_iterator(), I);
428   EXPECT_TRUE(I->path().endswith("a") || I->path().endswith("c"));
429   I.increment(EC);
430   EXPECT_EQ(vfs::directory_iterator(), I);
431 }
432 
433 TEST(VirtualFileSystemTest, MultipleWorkingDirs) {
434   // Our root contains a/aa, b/bb, c, where c is a link to a/.
435   // Run tests both in root/b/ and root/c/ (to test "normal" and symlink dirs).
436   // Interleave operations to show the working directories are independent.
437   ScopedDir Root("r", true), ADir(Root.Path + "/a"), BDir(Root.Path + "/b");
438   ScopedLink C(ADir.Path, Root.Path + "/c");
439   ScopedFile AA(ADir.Path + "/aa", "aaaa"), BB(BDir.Path + "/bb", "bbbb");
440   std::unique_ptr<vfs::FileSystem> BFS = vfs::createPhysicalFileSystem(),
441                                    CFS = vfs::createPhysicalFileSystem();
442 
443   ASSERT_FALSE(BFS->setCurrentWorkingDirectory(BDir.Path));
444   ASSERT_FALSE(CFS->setCurrentWorkingDirectory(C.Path));
445   EXPECT_EQ(BDir.Path, *BFS->getCurrentWorkingDirectory());
446   EXPECT_EQ(C.Path, *CFS->getCurrentWorkingDirectory());
447 
448   // openFileForRead(), indirectly.
449   auto BBuf = BFS->getBufferForFile("bb");
450   ASSERT_TRUE(BBuf);
451   EXPECT_EQ("bbbb", (*BBuf)->getBuffer());
452 
453   auto ABuf = CFS->getBufferForFile("aa");
454   ASSERT_TRUE(ABuf);
455   EXPECT_EQ("aaaa", (*ABuf)->getBuffer());
456 
457   // status()
458   auto BStat = BFS->status("bb");
459   ASSERT_TRUE(BStat);
460   EXPECT_EQ("bb", BStat->getName());
461 
462   auto AStat = CFS->status("aa");
463   ASSERT_TRUE(AStat);
464   EXPECT_EQ("aa", AStat->getName()); // unresolved name
465 
466   // getRealPath()
467   SmallString<128> BPath;
468   ASSERT_FALSE(BFS->getRealPath("bb", BPath));
469   EXPECT_EQ(BB.Path, BPath);
470 
471   SmallString<128> APath;
472   ASSERT_FALSE(CFS->getRealPath("aa", APath));
473   EXPECT_EQ(AA.Path, APath); // Reports resolved name.
474 
475   // dir_begin
476   std::error_code EC;
477   auto BIt = BFS->dir_begin(".", EC);
478   ASSERT_FALSE(EC);
479   ASSERT_NE(BIt, vfs::directory_iterator());
480   EXPECT_EQ((BDir.Path + "/./bb").str(), BIt->path());
481   BIt.increment(EC);
482   ASSERT_FALSE(EC);
483   ASSERT_EQ(BIt, vfs::directory_iterator());
484 
485   auto CIt = CFS->dir_begin(".", EC);
486   ASSERT_FALSE(EC);
487   ASSERT_NE(CIt, vfs::directory_iterator());
488   EXPECT_EQ((ADir.Path + "/./aa").str(), CIt->path()); // Partly resolved name!
489   CIt.increment(EC); // Because likely to read through this path.
490   ASSERT_FALSE(EC);
491   ASSERT_EQ(CIt, vfs::directory_iterator());
492 }
493 
494 #ifdef LLVM_ON_UNIX
495 TEST(VirtualFileSystemTest, BrokenSymlinkRealFSIteration) {
496   ScopedDir TestDirectory("virtual-file-system-test", /*Unique*/ true);
497   IntrusiveRefCntPtr<vfs::FileSystem> FS = vfs::getRealFileSystem();
498 
499   ScopedLink _a("no_such_file", TestDirectory + "/a");
500   ScopedDir _b(TestDirectory + "/b");
501   ScopedLink _c("no_such_file", TestDirectory + "/c");
502 
503   // Should get no iteration error, but a stat error for the broken symlinks.
504   std::map<std::string, std::error_code> StatResults;
505   std::error_code EC;
506   for (vfs::directory_iterator I = FS->dir_begin(Twine(TestDirectory), EC), E;
507        I != E; I.increment(EC)) {
508     EXPECT_FALSE(EC);
509     StatResults[sys::path::filename(I->path())] =
510         FS->status(I->path()).getError();
511   }
512   EXPECT_THAT(
513       StatResults,
514       ElementsAre(
515           Pair("a", std::make_error_code(std::errc::no_such_file_or_directory)),
516           Pair("b", std::error_code()),
517           Pair("c",
518                std::make_error_code(std::errc::no_such_file_or_directory))));
519 }
520 #endif
521 
522 TEST(VirtualFileSystemTest, BasicRealFSRecursiveIteration) {
523   ScopedDir TestDirectory("virtual-file-system-test", /*Unique*/ true);
524   IntrusiveRefCntPtr<vfs::FileSystem> FS = vfs::getRealFileSystem();
525 
526   std::error_code EC;
527   auto I = vfs::recursive_directory_iterator(*FS, Twine(TestDirectory), EC);
528   ASSERT_FALSE(EC);
529   EXPECT_EQ(vfs::recursive_directory_iterator(), I); // empty directory is empty
530 
531   ScopedDir _a(TestDirectory + "/a");
532   ScopedDir _ab(TestDirectory + "/a/b");
533   ScopedDir _c(TestDirectory + "/c");
534   ScopedDir _cd(TestDirectory + "/c/d");
535 
536   I = vfs::recursive_directory_iterator(*FS, Twine(TestDirectory), EC);
537   ASSERT_FALSE(EC);
538   ASSERT_NE(vfs::recursive_directory_iterator(), I);
539 
540   std::vector<std::string> Contents;
541   for (auto E = vfs::recursive_directory_iterator(); !EC && I != E;
542        I.increment(EC)) {
543     Contents.push_back(I->path());
544   }
545 
546   // Check contents, which may be in any order
547   EXPECT_EQ(4U, Contents.size());
548   int Counts[4] = {0, 0, 0, 0};
549   for (const std::string &Name : Contents) {
550     ASSERT_FALSE(Name.empty());
551     int Index = Name[Name.size() - 1] - 'a';
552     ASSERT_TRUE(Index >= 0 && Index < 4);
553     Counts[Index]++;
554   }
555   EXPECT_EQ(1, Counts[0]); // a
556   EXPECT_EQ(1, Counts[1]); // b
557   EXPECT_EQ(1, Counts[2]); // c
558   EXPECT_EQ(1, Counts[3]); // d
559 }
560 
561 TEST(VirtualFileSystemTest, BasicRealFSRecursiveIterationNoPush) {
562   ScopedDir TestDirectory("virtual-file-system-test", /*Unique*/ true);
563 
564   ScopedDir _a(TestDirectory + "/a");
565   ScopedDir _ab(TestDirectory + "/a/b");
566   ScopedDir _c(TestDirectory + "/c");
567   ScopedDir _cd(TestDirectory + "/c/d");
568   ScopedDir _e(TestDirectory + "/e");
569   ScopedDir _ef(TestDirectory + "/e/f");
570   ScopedDir _g(TestDirectory + "/g");
571 
572   IntrusiveRefCntPtr<vfs::FileSystem> FS = vfs::getRealFileSystem();
573 
574   // Test that calling no_push on entries without subdirectories has no effect.
575   {
576     std::error_code EC;
577     auto I = vfs::recursive_directory_iterator(*FS, Twine(TestDirectory), EC);
578     ASSERT_FALSE(EC);
579 
580     std::vector<std::string> Contents;
581     for (auto E = vfs::recursive_directory_iterator(); !EC && I != E;
582          I.increment(EC)) {
583       Contents.push_back(I->path());
584       char last = I->path().back();
585       switch (last) {
586       case 'b':
587       case 'd':
588       case 'f':
589       case 'g':
590         I.no_push();
591         break;
592       default:
593         break;
594       }
595     }
596     EXPECT_EQ(7U, Contents.size());
597   }
598 
599   // Test that calling no_push skips subdirectories.
600   {
601     std::error_code EC;
602     auto I = vfs::recursive_directory_iterator(*FS, Twine(TestDirectory), EC);
603     ASSERT_FALSE(EC);
604 
605     std::vector<std::string> Contents;
606     for (auto E = vfs::recursive_directory_iterator(); !EC && I != E;
607          I.increment(EC)) {
608       Contents.push_back(I->path());
609       char last = I->path().back();
610       switch (last) {
611       case 'a':
612       case 'c':
613       case 'e':
614         I.no_push();
615         break;
616       default:
617         break;
618       }
619     }
620 
621     // Check contents, which may be in any order
622     EXPECT_EQ(4U, Contents.size());
623     int Counts[7] = {0, 0, 0, 0, 0, 0, 0};
624     for (const std::string &Name : Contents) {
625       ASSERT_FALSE(Name.empty());
626       int Index = Name[Name.size() - 1] - 'a';
627       ASSERT_TRUE(Index >= 0 && Index < 7);
628       Counts[Index]++;
629     }
630     EXPECT_EQ(1, Counts[0]); // a
631     EXPECT_EQ(0, Counts[1]); // b
632     EXPECT_EQ(1, Counts[2]); // c
633     EXPECT_EQ(0, Counts[3]); // d
634     EXPECT_EQ(1, Counts[4]); // e
635     EXPECT_EQ(0, Counts[5]); // f
636     EXPECT_EQ(1, Counts[6]); // g
637   }
638 }
639 
640 #ifdef LLVM_ON_UNIX
641 TEST(VirtualFileSystemTest, BrokenSymlinkRealFSRecursiveIteration) {
642   ScopedDir TestDirectory("virtual-file-system-test", /*Unique*/ true);
643   IntrusiveRefCntPtr<vfs::FileSystem> FS = vfs::getRealFileSystem();
644 
645   ScopedLink _a("no_such_file", TestDirectory + "/a");
646   ScopedDir _b(TestDirectory + "/b");
647   ScopedLink _ba("no_such_file", TestDirectory + "/b/a");
648   ScopedDir _bb(TestDirectory + "/b/b");
649   ScopedLink _bc("no_such_file", TestDirectory + "/b/c");
650   ScopedLink _c("no_such_file", TestDirectory + "/c");
651   ScopedDir _d(TestDirectory + "/d");
652   ScopedDir _dd(TestDirectory + "/d/d");
653   ScopedDir _ddd(TestDirectory + "/d/d/d");
654   ScopedLink _e("no_such_file", TestDirectory + "/e");
655 
656   std::vector<std::string> VisitedBrokenSymlinks;
657   std::vector<std::string> VisitedNonBrokenSymlinks;
658   std::error_code EC;
659   for (vfs::recursive_directory_iterator I(*FS, Twine(TestDirectory), EC), E;
660        I != E; I.increment(EC)) {
661     EXPECT_FALSE(EC);
662     (FS->status(I->path()) ? VisitedNonBrokenSymlinks : VisitedBrokenSymlinks)
663         .push_back(I->path());
664   }
665 
666   // Check visited file names.
667   EXPECT_THAT(VisitedBrokenSymlinks,
668               UnorderedElementsAre(StringRef(_a), StringRef(_ba),
669                                    StringRef(_bc), StringRef(_c),
670                                    StringRef(_e)));
671   EXPECT_THAT(VisitedNonBrokenSymlinks,
672               UnorderedElementsAre(StringRef(_b), StringRef(_bb), StringRef(_d),
673                                    StringRef(_dd), StringRef(_ddd)));
674 }
675 #endif
676 
677 template <typename DirIter>
678 static void checkContents(DirIter I, ArrayRef<StringRef> ExpectedOut) {
679   std::error_code EC;
680   SmallVector<StringRef, 4> Expected(ExpectedOut.begin(), ExpectedOut.end());
681   SmallVector<std::string, 4> InputToCheck;
682 
683   // Do not rely on iteration order to check for contents, sort both
684   // content vectors before comparison.
685   for (DirIter E; !EC && I != E; I.increment(EC))
686     InputToCheck.push_back(I->path());
687 
688   llvm::sort(InputToCheck);
689   llvm::sort(Expected);
690   EXPECT_EQ(InputToCheck.size(), Expected.size());
691 
692   unsigned LastElt = std::min(InputToCheck.size(), Expected.size());
693   for (unsigned Idx = 0; Idx != LastElt; ++Idx)
694     EXPECT_EQ(StringRef(InputToCheck[Idx]), Expected[Idx]);
695 }
696 
697 TEST(VirtualFileSystemTest, OverlayIteration) {
698   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
699   IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem());
700   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
701       new vfs::OverlayFileSystem(Lower));
702   O->pushOverlay(Upper);
703 
704   std::error_code EC;
705   checkContents(O->dir_begin("/", EC), ArrayRef<StringRef>());
706 
707   Lower->addRegularFile("/file1");
708   checkContents(O->dir_begin("/", EC), ArrayRef<StringRef>("/file1"));
709 
710   Upper->addRegularFile("/file2");
711   checkContents(O->dir_begin("/", EC), {"/file2", "/file1"});
712 
713   Lower->addDirectory("/dir1");
714   Lower->addRegularFile("/dir1/foo");
715   Upper->addDirectory("/dir2");
716   Upper->addRegularFile("/dir2/foo");
717   checkContents(O->dir_begin("/dir2", EC), ArrayRef<StringRef>("/dir2/foo"));
718   checkContents(O->dir_begin("/", EC), {"/dir2", "/file2", "/dir1", "/file1"});
719 }
720 
721 TEST(VirtualFileSystemTest, OverlayRecursiveIteration) {
722   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
723   IntrusiveRefCntPtr<DummyFileSystem> Middle(new DummyFileSystem());
724   IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem());
725   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
726       new vfs::OverlayFileSystem(Lower));
727   O->pushOverlay(Middle);
728   O->pushOverlay(Upper);
729 
730   std::error_code EC;
731   checkContents(vfs::recursive_directory_iterator(*O, "/", EC),
732                 ArrayRef<StringRef>());
733 
734   Lower->addRegularFile("/file1");
735   checkContents(vfs::recursive_directory_iterator(*O, "/", EC),
736                 ArrayRef<StringRef>("/file1"));
737 
738   Upper->addDirectory("/dir");
739   Upper->addRegularFile("/dir/file2");
740   checkContents(vfs::recursive_directory_iterator(*O, "/", EC),
741                 {"/dir", "/dir/file2", "/file1"});
742 
743   Lower->addDirectory("/dir1");
744   Lower->addRegularFile("/dir1/foo");
745   Lower->addDirectory("/dir1/a");
746   Lower->addRegularFile("/dir1/a/b");
747   Middle->addDirectory("/a");
748   Middle->addDirectory("/a/b");
749   Middle->addDirectory("/a/b/c");
750   Middle->addRegularFile("/a/b/c/d");
751   Middle->addRegularFile("/hiddenByUp");
752   Upper->addDirectory("/dir2");
753   Upper->addRegularFile("/dir2/foo");
754   Upper->addRegularFile("/hiddenByUp");
755   checkContents(vfs::recursive_directory_iterator(*O, "/dir2", EC),
756                 ArrayRef<StringRef>("/dir2/foo"));
757   checkContents(vfs::recursive_directory_iterator(*O, "/", EC),
758                 {"/dir", "/dir/file2", "/dir2", "/dir2/foo", "/hiddenByUp",
759                  "/a", "/a/b", "/a/b/c", "/a/b/c/d", "/dir1", "/dir1/a",
760                  "/dir1/a/b", "/dir1/foo", "/file1"});
761 }
762 
763 TEST(VirtualFileSystemTest, ThreeLevelIteration) {
764   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
765   IntrusiveRefCntPtr<DummyFileSystem> Middle(new DummyFileSystem());
766   IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem());
767   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
768       new vfs::OverlayFileSystem(Lower));
769   O->pushOverlay(Middle);
770   O->pushOverlay(Upper);
771 
772   std::error_code EC;
773   checkContents(O->dir_begin("/", EC), ArrayRef<StringRef>());
774 
775   Middle->addRegularFile("/file2");
776   checkContents(O->dir_begin("/", EC), ArrayRef<StringRef>("/file2"));
777 
778   Lower->addRegularFile("/file1");
779   Upper->addRegularFile("/file3");
780   checkContents(O->dir_begin("/", EC), {"/file3", "/file2", "/file1"});
781 }
782 
783 TEST(VirtualFileSystemTest, HiddenInIteration) {
784   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
785   IntrusiveRefCntPtr<DummyFileSystem> Middle(new DummyFileSystem());
786   IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem());
787   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
788       new vfs::OverlayFileSystem(Lower));
789   O->pushOverlay(Middle);
790   O->pushOverlay(Upper);
791 
792   std::error_code EC;
793   Lower->addRegularFile("/onlyInLow");
794   Lower->addDirectory("/hiddenByMid");
795   Lower->addDirectory("/hiddenByUp");
796   Middle->addRegularFile("/onlyInMid");
797   Middle->addRegularFile("/hiddenByMid");
798   Middle->addDirectory("/hiddenByUp");
799   Upper->addRegularFile("/onlyInUp");
800   Upper->addRegularFile("/hiddenByUp");
801   checkContents(
802       O->dir_begin("/", EC),
803       {"/hiddenByUp", "/onlyInUp", "/hiddenByMid", "/onlyInMid", "/onlyInLow"});
804 
805   // Make sure we get the top-most entry
806   {
807     std::error_code EC;
808     vfs::directory_iterator I = O->dir_begin("/", EC), E;
809     for (; !EC && I != E; I.increment(EC))
810       if (I->path() == "/hiddenByUp")
811         break;
812     ASSERT_NE(E, I);
813     EXPECT_EQ(sys::fs::file_type::regular_file, I->type());
814   }
815   {
816     std::error_code EC;
817     vfs::directory_iterator I = O->dir_begin("/", EC), E;
818     for (; !EC && I != E; I.increment(EC))
819       if (I->path() == "/hiddenByMid")
820         break;
821     ASSERT_NE(E, I);
822     EXPECT_EQ(sys::fs::file_type::regular_file, I->type());
823   }
824 }
825 
826 TEST(ProxyFileSystemTest, Basic) {
827   IntrusiveRefCntPtr<vfs::InMemoryFileSystem> Base(
828       new vfs::InMemoryFileSystem());
829   vfs::ProxyFileSystem PFS(Base);
830 
831   Base->addFile("/a", 0, MemoryBuffer::getMemBuffer("test"));
832 
833   auto Stat = PFS.status("/a");
834   ASSERT_FALSE(Stat.getError());
835 
836   auto File = PFS.openFileForRead("/a");
837   ASSERT_FALSE(File.getError());
838   EXPECT_EQ("test", (*(*File)->getBuffer("ignored"))->getBuffer());
839 
840   std::error_code EC;
841   vfs::directory_iterator I = PFS.dir_begin("/", EC);
842   ASSERT_FALSE(EC);
843   ASSERT_EQ("/a", I->path());
844   I.increment(EC);
845   ASSERT_FALSE(EC);
846   ASSERT_EQ(vfs::directory_iterator(), I);
847 
848   ASSERT_FALSE(PFS.setCurrentWorkingDirectory("/"));
849 
850   auto PWD = PFS.getCurrentWorkingDirectory();
851   ASSERT_FALSE(PWD.getError());
852   ASSERT_EQ("/", *PWD);
853 
854   SmallString<16> Path;
855   ASSERT_FALSE(PFS.getRealPath("a", Path));
856   ASSERT_EQ("/a", Path);
857 
858   bool Local = true;
859   ASSERT_FALSE(PFS.isLocal("/a", Local));
860   ASSERT_EQ(false, Local);
861 }
862 
863 class InMemoryFileSystemTest : public ::testing::Test {
864 protected:
865   llvm::vfs::InMemoryFileSystem FS;
866   llvm::vfs::InMemoryFileSystem NormalizedFS;
867 
868   InMemoryFileSystemTest()
869       : FS(/*UseNormalizedPaths=*/false),
870         NormalizedFS(/*UseNormalizedPaths=*/true) {}
871 };
872 
873 MATCHER_P2(IsHardLinkTo, FS, Target, "") {
874   StringRef From = arg;
875   StringRef To = Target;
876   auto OpenedFrom = FS->openFileForRead(From);
877   auto OpenedTo = FS->openFileForRead(To);
878   return !OpenedFrom.getError() && !OpenedTo.getError() &&
879          (*OpenedFrom)->status()->getUniqueID() ==
880              (*OpenedTo)->status()->getUniqueID();
881 }
882 
883 TEST_F(InMemoryFileSystemTest, IsEmpty) {
884   auto Stat = FS.status("/a");
885   ASSERT_EQ(Stat.getError(), errc::no_such_file_or_directory) << FS.toString();
886   Stat = FS.status("/");
887   ASSERT_EQ(Stat.getError(), errc::no_such_file_or_directory) << FS.toString();
888 }
889 
890 TEST_F(InMemoryFileSystemTest, WindowsPath) {
891   FS.addFile("c:/windows/system128/foo.cpp", 0, MemoryBuffer::getMemBuffer(""));
892   auto Stat = FS.status("c:");
893 #if !defined(_WIN32)
894   ASSERT_FALSE(Stat.getError()) << Stat.getError() << FS.toString();
895 #endif
896   Stat = FS.status("c:/windows/system128/foo.cpp");
897   ASSERT_FALSE(Stat.getError()) << Stat.getError() << FS.toString();
898   FS.addFile("d:/windows/foo.cpp", 0, MemoryBuffer::getMemBuffer(""));
899   Stat = FS.status("d:/windows/foo.cpp");
900   ASSERT_FALSE(Stat.getError()) << Stat.getError() << FS.toString();
901 }
902 
903 TEST_F(InMemoryFileSystemTest, OverlayFile) {
904   FS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a"));
905   NormalizedFS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a"));
906   auto Stat = FS.status("/");
907   ASSERT_FALSE(Stat.getError()) << Stat.getError() << FS.toString();
908   Stat = FS.status("/.");
909   ASSERT_FALSE(Stat);
910   Stat = NormalizedFS.status("/.");
911   ASSERT_FALSE(Stat.getError()) << Stat.getError() << FS.toString();
912   Stat = FS.status("/a");
913   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
914   ASSERT_EQ("/a", Stat->getName());
915 }
916 
917 TEST_F(InMemoryFileSystemTest, OverlayFileNoOwn) {
918   auto Buf = MemoryBuffer::getMemBuffer("a");
919   FS.addFileNoOwn("/a", 0, Buf.get());
920   auto Stat = FS.status("/a");
921   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
922   ASSERT_EQ("/a", Stat->getName());
923 }
924 
925 TEST_F(InMemoryFileSystemTest, OpenFileForRead) {
926   FS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a"));
927   FS.addFile("././c", 0, MemoryBuffer::getMemBuffer("c"));
928   FS.addFile("./d/../d", 0, MemoryBuffer::getMemBuffer("d"));
929   NormalizedFS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a"));
930   NormalizedFS.addFile("././c", 0, MemoryBuffer::getMemBuffer("c"));
931   NormalizedFS.addFile("./d/../d", 0, MemoryBuffer::getMemBuffer("d"));
932   auto File = FS.openFileForRead("/a");
933   ASSERT_EQ("a", (*(*File)->getBuffer("ignored"))->getBuffer());
934   File = FS.openFileForRead("/a"); // Open again.
935   ASSERT_EQ("a", (*(*File)->getBuffer("ignored"))->getBuffer());
936   File = NormalizedFS.openFileForRead("/././a"); // Open again.
937   ASSERT_EQ("a", (*(*File)->getBuffer("ignored"))->getBuffer());
938   File = FS.openFileForRead("/");
939   ASSERT_EQ(File.getError(), errc::invalid_argument) << FS.toString();
940   File = FS.openFileForRead("/b");
941   ASSERT_EQ(File.getError(), errc::no_such_file_or_directory) << FS.toString();
942   File = FS.openFileForRead("./c");
943   ASSERT_FALSE(File);
944   File = FS.openFileForRead("e/../d");
945   ASSERT_FALSE(File);
946   File = NormalizedFS.openFileForRead("./c");
947   ASSERT_EQ("c", (*(*File)->getBuffer("ignored"))->getBuffer());
948   File = NormalizedFS.openFileForRead("e/../d");
949   ASSERT_EQ("d", (*(*File)->getBuffer("ignored"))->getBuffer());
950 }
951 
952 TEST_F(InMemoryFileSystemTest, DuplicatedFile) {
953   ASSERT_TRUE(FS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a")));
954   ASSERT_FALSE(FS.addFile("/a/b", 0, MemoryBuffer::getMemBuffer("a")));
955   ASSERT_TRUE(FS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a")));
956   ASSERT_FALSE(FS.addFile("/a", 0, MemoryBuffer::getMemBuffer("b")));
957 }
958 
959 TEST_F(InMemoryFileSystemTest, DirectoryIteration) {
960   FS.addFile("/a", 0, MemoryBuffer::getMemBuffer(""));
961   FS.addFile("/b/c", 0, MemoryBuffer::getMemBuffer(""));
962 
963   std::error_code EC;
964   vfs::directory_iterator I = FS.dir_begin("/", EC);
965   ASSERT_FALSE(EC);
966   ASSERT_EQ("/a", I->path());
967   I.increment(EC);
968   ASSERT_FALSE(EC);
969   ASSERT_EQ("/b", I->path());
970   I.increment(EC);
971   ASSERT_FALSE(EC);
972   ASSERT_EQ(vfs::directory_iterator(), I);
973 
974   I = FS.dir_begin("/b", EC);
975   ASSERT_FALSE(EC);
976   // When on Windows, we end up with "/b\\c" as the name.  Convert to Posix
977   // path for the sake of the comparison.
978   ASSERT_EQ("/b/c", getPosixPath(I->path()));
979   I.increment(EC);
980   ASSERT_FALSE(EC);
981   ASSERT_EQ(vfs::directory_iterator(), I);
982 }
983 
984 TEST_F(InMemoryFileSystemTest, WorkingDirectory) {
985   FS.setCurrentWorkingDirectory("/b");
986   FS.addFile("c", 0, MemoryBuffer::getMemBuffer(""));
987 
988   auto Stat = FS.status("/b/c");
989   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
990   ASSERT_EQ("/b/c", Stat->getName());
991   ASSERT_EQ("/b", *FS.getCurrentWorkingDirectory());
992 
993   Stat = FS.status("c");
994   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
995 
996   NormalizedFS.setCurrentWorkingDirectory("/b/c");
997   NormalizedFS.setCurrentWorkingDirectory(".");
998   ASSERT_EQ("/b/c",
999             getPosixPath(NormalizedFS.getCurrentWorkingDirectory().get()));
1000   NormalizedFS.setCurrentWorkingDirectory("..");
1001   ASSERT_EQ("/b",
1002             getPosixPath(NormalizedFS.getCurrentWorkingDirectory().get()));
1003 }
1004 
1005 TEST_F(InMemoryFileSystemTest, IsLocal) {
1006   FS.setCurrentWorkingDirectory("/b");
1007   FS.addFile("c", 0, MemoryBuffer::getMemBuffer(""));
1008 
1009   std::error_code EC;
1010   bool IsLocal = true;
1011   EC = FS.isLocal("c", IsLocal);
1012   ASSERT_FALSE(EC);
1013   ASSERT_FALSE(IsLocal);
1014 }
1015 
1016 #if !defined(_WIN32)
1017 TEST_F(InMemoryFileSystemTest, GetRealPath) {
1018   SmallString<16> Path;
1019   EXPECT_EQ(FS.getRealPath("b", Path), errc::operation_not_permitted);
1020 
1021   auto GetRealPath = [this](StringRef P) {
1022     SmallString<16> Output;
1023     auto EC = FS.getRealPath(P, Output);
1024     EXPECT_FALSE(EC);
1025     return Output.str().str();
1026   };
1027 
1028   FS.setCurrentWorkingDirectory("a");
1029   EXPECT_EQ(GetRealPath("b"), "a/b");
1030   EXPECT_EQ(GetRealPath("../b"), "b");
1031   EXPECT_EQ(GetRealPath("b/./c"), "a/b/c");
1032 
1033   FS.setCurrentWorkingDirectory("/a");
1034   EXPECT_EQ(GetRealPath("b"), "/a/b");
1035   EXPECT_EQ(GetRealPath("../b"), "/b");
1036   EXPECT_EQ(GetRealPath("b/./c"), "/a/b/c");
1037 }
1038 #endif // _WIN32
1039 
1040 TEST_F(InMemoryFileSystemTest, AddFileWithUser) {
1041   FS.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"), 0xFEEDFACE);
1042   auto Stat = FS.status("/a");
1043   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
1044   ASSERT_TRUE(Stat->isDirectory());
1045   ASSERT_EQ(0xFEEDFACE, Stat->getUser());
1046   Stat = FS.status("/a/b");
1047   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
1048   ASSERT_TRUE(Stat->isDirectory());
1049   ASSERT_EQ(0xFEEDFACE, Stat->getUser());
1050   Stat = FS.status("/a/b/c");
1051   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
1052   ASSERT_TRUE(Stat->isRegularFile());
1053   ASSERT_EQ(sys::fs::perms::all_all, Stat->getPermissions());
1054   ASSERT_EQ(0xFEEDFACE, Stat->getUser());
1055 }
1056 
1057 TEST_F(InMemoryFileSystemTest, AddFileWithGroup) {
1058   FS.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"), None, 0xDABBAD00);
1059   auto Stat = FS.status("/a");
1060   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
1061   ASSERT_TRUE(Stat->isDirectory());
1062   ASSERT_EQ(0xDABBAD00, Stat->getGroup());
1063   Stat = FS.status("/a/b");
1064   ASSERT_TRUE(Stat->isDirectory());
1065   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
1066   ASSERT_EQ(0xDABBAD00, Stat->getGroup());
1067   Stat = FS.status("/a/b/c");
1068   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
1069   ASSERT_TRUE(Stat->isRegularFile());
1070   ASSERT_EQ(sys::fs::perms::all_all, Stat->getPermissions());
1071   ASSERT_EQ(0xDABBAD00, Stat->getGroup());
1072 }
1073 
1074 TEST_F(InMemoryFileSystemTest, AddFileWithFileType) {
1075   FS.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"), None, None,
1076              sys::fs::file_type::socket_file);
1077   auto Stat = FS.status("/a");
1078   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
1079   ASSERT_TRUE(Stat->isDirectory());
1080   Stat = FS.status("/a/b");
1081   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
1082   ASSERT_TRUE(Stat->isDirectory());
1083   Stat = FS.status("/a/b/c");
1084   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
1085   ASSERT_EQ(sys::fs::file_type::socket_file, Stat->getType());
1086   ASSERT_EQ(sys::fs::perms::all_all, Stat->getPermissions());
1087 }
1088 
1089 TEST_F(InMemoryFileSystemTest, AddFileWithPerms) {
1090   FS.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"), None, None, None,
1091              sys::fs::perms::owner_read | sys::fs::perms::owner_write);
1092   auto Stat = FS.status("/a");
1093   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
1094   ASSERT_TRUE(Stat->isDirectory());
1095   ASSERT_EQ(sys::fs::perms::owner_read | sys::fs::perms::owner_write |
1096                 sys::fs::perms::owner_exe,
1097             Stat->getPermissions());
1098   Stat = FS.status("/a/b");
1099   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
1100   ASSERT_TRUE(Stat->isDirectory());
1101   ASSERT_EQ(sys::fs::perms::owner_read | sys::fs::perms::owner_write |
1102                 sys::fs::perms::owner_exe,
1103             Stat->getPermissions());
1104   Stat = FS.status("/a/b/c");
1105   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
1106   ASSERT_TRUE(Stat->isRegularFile());
1107   ASSERT_EQ(sys::fs::perms::owner_read | sys::fs::perms::owner_write,
1108             Stat->getPermissions());
1109 }
1110 
1111 TEST_F(InMemoryFileSystemTest, AddDirectoryThenAddChild) {
1112   FS.addFile("/a", 0, MemoryBuffer::getMemBuffer(""), /*User=*/None,
1113              /*Group=*/None, sys::fs::file_type::directory_file);
1114   FS.addFile("/a/b", 0, MemoryBuffer::getMemBuffer("abc"), /*User=*/None,
1115              /*Group=*/None, sys::fs::file_type::regular_file);
1116   auto Stat = FS.status("/a");
1117   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
1118   ASSERT_TRUE(Stat->isDirectory());
1119   Stat = FS.status("/a/b");
1120   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
1121   ASSERT_TRUE(Stat->isRegularFile());
1122 }
1123 
1124 // Test that the name returned by status() is in the same form as the path that
1125 // was requested (to match the behavior of RealFileSystem).
1126 TEST_F(InMemoryFileSystemTest, StatusName) {
1127   NormalizedFS.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"),
1128                        /*User=*/None,
1129                        /*Group=*/None, sys::fs::file_type::regular_file);
1130   NormalizedFS.setCurrentWorkingDirectory("/a/b");
1131 
1132   // Access using InMemoryFileSystem::status.
1133   auto Stat = NormalizedFS.status("../b/c");
1134   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n"
1135                                 << NormalizedFS.toString();
1136   ASSERT_TRUE(Stat->isRegularFile());
1137   ASSERT_EQ("../b/c", Stat->getName());
1138 
1139   // Access using InMemoryFileAdaptor::status.
1140   auto File = NormalizedFS.openFileForRead("../b/c");
1141   ASSERT_FALSE(File.getError()) << File.getError() << "\n"
1142                                 << NormalizedFS.toString();
1143   Stat = (*File)->status();
1144   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n"
1145                                 << NormalizedFS.toString();
1146   ASSERT_TRUE(Stat->isRegularFile());
1147   ASSERT_EQ("../b/c", Stat->getName());
1148 
1149   // Access using a directory iterator.
1150   std::error_code EC;
1151   llvm::vfs::directory_iterator It = NormalizedFS.dir_begin("../b", EC);
1152   // When on Windows, we end up with "../b\\c" as the name.  Convert to Posix
1153   // path for the sake of the comparison.
1154   ASSERT_EQ("../b/c", getPosixPath(It->path()));
1155 }
1156 
1157 TEST_F(InMemoryFileSystemTest, AddHardLinkToFile) {
1158   StringRef FromLink = "/path/to/FROM/link";
1159   StringRef Target = "/path/to/TO/file";
1160   FS.addFile(Target, 0, MemoryBuffer::getMemBuffer("content of target"));
1161   EXPECT_TRUE(FS.addHardLink(FromLink, Target));
1162   EXPECT_THAT(FromLink, IsHardLinkTo(&FS, Target));
1163   EXPECT_TRUE(FS.status(FromLink)->getSize() == FS.status(Target)->getSize());
1164   EXPECT_TRUE(FS.getBufferForFile(FromLink)->get()->getBuffer() ==
1165               FS.getBufferForFile(Target)->get()->getBuffer());
1166 }
1167 
1168 TEST_F(InMemoryFileSystemTest, AddHardLinkInChainPattern) {
1169   StringRef Link0 = "/path/to/0/link";
1170   StringRef Link1 = "/path/to/1/link";
1171   StringRef Link2 = "/path/to/2/link";
1172   StringRef Target = "/path/to/target";
1173   FS.addFile(Target, 0, MemoryBuffer::getMemBuffer("content of target file"));
1174   EXPECT_TRUE(FS.addHardLink(Link2, Target));
1175   EXPECT_TRUE(FS.addHardLink(Link1, Link2));
1176   EXPECT_TRUE(FS.addHardLink(Link0, Link1));
1177   EXPECT_THAT(Link0, IsHardLinkTo(&FS, Target));
1178   EXPECT_THAT(Link1, IsHardLinkTo(&FS, Target));
1179   EXPECT_THAT(Link2, IsHardLinkTo(&FS, Target));
1180 }
1181 
1182 TEST_F(InMemoryFileSystemTest, AddHardLinkToAFileThatWasNotAddedBefore) {
1183   EXPECT_FALSE(FS.addHardLink("/path/to/link", "/path/to/target"));
1184 }
1185 
1186 TEST_F(InMemoryFileSystemTest, AddHardLinkFromAFileThatWasAddedBefore) {
1187   StringRef Link = "/path/to/link";
1188   StringRef Target = "/path/to/target";
1189   FS.addFile(Target, 0, MemoryBuffer::getMemBuffer("content of target"));
1190   FS.addFile(Link, 0, MemoryBuffer::getMemBuffer("content of link"));
1191   EXPECT_FALSE(FS.addHardLink(Link, Target));
1192 }
1193 
1194 TEST_F(InMemoryFileSystemTest, AddSameHardLinkMoreThanOnce) {
1195   StringRef Link = "/path/to/link";
1196   StringRef Target = "/path/to/target";
1197   FS.addFile(Target, 0, MemoryBuffer::getMemBuffer("content of target"));
1198   EXPECT_TRUE(FS.addHardLink(Link, Target));
1199   EXPECT_FALSE(FS.addHardLink(Link, Target));
1200 }
1201 
1202 TEST_F(InMemoryFileSystemTest, AddFileInPlaceOfAHardLinkWithSameContent) {
1203   StringRef Link = "/path/to/link";
1204   StringRef Target = "/path/to/target";
1205   StringRef Content = "content of target";
1206   EXPECT_TRUE(FS.addFile(Target, 0, MemoryBuffer::getMemBuffer(Content)));
1207   EXPECT_TRUE(FS.addHardLink(Link, Target));
1208   EXPECT_TRUE(FS.addFile(Link, 0, MemoryBuffer::getMemBuffer(Content)));
1209 }
1210 
1211 TEST_F(InMemoryFileSystemTest, AddFileInPlaceOfAHardLinkWithDifferentContent) {
1212   StringRef Link = "/path/to/link";
1213   StringRef Target = "/path/to/target";
1214   StringRef Content = "content of target";
1215   StringRef LinkContent = "different content of link";
1216   EXPECT_TRUE(FS.addFile(Target, 0, MemoryBuffer::getMemBuffer(Content)));
1217   EXPECT_TRUE(FS.addHardLink(Link, Target));
1218   EXPECT_FALSE(FS.addFile(Link, 0, MemoryBuffer::getMemBuffer(LinkContent)));
1219 }
1220 
1221 TEST_F(InMemoryFileSystemTest, AddHardLinkToADirectory) {
1222   StringRef Dir = "path/to/dummy/dir";
1223   StringRef Link = "/path/to/link";
1224   StringRef File = "path/to/dummy/dir/target";
1225   StringRef Content = "content of target";
1226   EXPECT_TRUE(FS.addFile(File, 0, MemoryBuffer::getMemBuffer(Content)));
1227   EXPECT_FALSE(FS.addHardLink(Link, Dir));
1228 }
1229 
1230 TEST_F(InMemoryFileSystemTest, AddHardLinkFromADirectory) {
1231   StringRef Dir = "path/to/dummy/dir";
1232   StringRef Target = "path/to/dummy/dir/target";
1233   StringRef Content = "content of target";
1234   EXPECT_TRUE(FS.addFile(Target, 0, MemoryBuffer::getMemBuffer(Content)));
1235   EXPECT_FALSE(FS.addHardLink(Dir, Target));
1236 }
1237 
1238 TEST_F(InMemoryFileSystemTest, AddHardLinkUnderAFile) {
1239   StringRef CommonContent = "content string";
1240   FS.addFile("/a/b", 0, MemoryBuffer::getMemBuffer(CommonContent));
1241   FS.addFile("/c/d", 0, MemoryBuffer::getMemBuffer(CommonContent));
1242   EXPECT_FALSE(FS.addHardLink("/c/d/e", "/a/b"));
1243 }
1244 
1245 TEST_F(InMemoryFileSystemTest, RecursiveIterationWithHardLink) {
1246   std::error_code EC;
1247   FS.addFile("/a/b", 0, MemoryBuffer::getMemBuffer("content string"));
1248   EXPECT_TRUE(FS.addHardLink("/c/d", "/a/b"));
1249   auto I = vfs::recursive_directory_iterator(FS, "/", EC);
1250   ASSERT_FALSE(EC);
1251   std::vector<std::string> Nodes;
1252   for (auto E = vfs::recursive_directory_iterator(); !EC && I != E;
1253        I.increment(EC)) {
1254     Nodes.push_back(getPosixPath(I->path()));
1255   }
1256   EXPECT_THAT(Nodes, testing::UnorderedElementsAre("/a", "/a/b", "/c", "/c/d"));
1257 }
1258 
1259 // NOTE: in the tests below, we use '//root/' as our root directory, since it is
1260 // a legal *absolute* path on Windows as well as *nix.
1261 class VFSFromYAMLTest : public ::testing::Test {
1262 public:
1263   int NumDiagnostics;
1264 
1265   void SetUp() override { NumDiagnostics = 0; }
1266 
1267   static void CountingDiagHandler(const SMDiagnostic &, void *Context) {
1268     VFSFromYAMLTest *Test = static_cast<VFSFromYAMLTest *>(Context);
1269     ++Test->NumDiagnostics;
1270   }
1271 
1272   IntrusiveRefCntPtr<vfs::FileSystem>
1273   getFromYAMLRawString(StringRef Content,
1274                        IntrusiveRefCntPtr<vfs::FileSystem> ExternalFS) {
1275     std::unique_ptr<MemoryBuffer> Buffer = MemoryBuffer::getMemBuffer(Content);
1276     return getVFSFromYAML(std::move(Buffer), CountingDiagHandler, "", this,
1277                           ExternalFS);
1278   }
1279 
1280   IntrusiveRefCntPtr<vfs::FileSystem> getFromYAMLString(
1281       StringRef Content,
1282       IntrusiveRefCntPtr<vfs::FileSystem> ExternalFS = new DummyFileSystem()) {
1283     std::string VersionPlusContent("{\n  'version':0,\n");
1284     VersionPlusContent += Content.slice(Content.find('{') + 1, StringRef::npos);
1285     return getFromYAMLRawString(VersionPlusContent, ExternalFS);
1286   }
1287 
1288   // This is intended as a "XFAIL" for windows hosts.
1289   bool supportsSameDirMultipleYAMLEntries() {
1290     Triple Host(Triple::normalize(sys::getProcessTriple()));
1291     return !Host.isOSWindows();
1292   }
1293 };
1294 
1295 TEST_F(VFSFromYAMLTest, BasicVFSFromYAML) {
1296   IntrusiveRefCntPtr<vfs::FileSystem> FS;
1297   FS = getFromYAMLString("");
1298   EXPECT_EQ(nullptr, FS.get());
1299   FS = getFromYAMLString("[]");
1300   EXPECT_EQ(nullptr, FS.get());
1301   FS = getFromYAMLString("'string'");
1302   EXPECT_EQ(nullptr, FS.get());
1303   EXPECT_EQ(3, NumDiagnostics);
1304 }
1305 
1306 TEST_F(VFSFromYAMLTest, MappedFiles) {
1307   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
1308   Lower->addRegularFile("//root/foo/bar/a");
1309   IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
1310       "{ 'roots': [\n"
1311       "{\n"
1312       "  'type': 'directory',\n"
1313       "  'name': '//root/',\n"
1314       "  'contents': [ {\n"
1315       "                  'type': 'file',\n"
1316       "                  'name': 'file1',\n"
1317       "                  'external-contents': '//root/foo/bar/a'\n"
1318       "                },\n"
1319       "                {\n"
1320       "                  'type': 'file',\n"
1321       "                  'name': 'file2',\n"
1322       "                  'external-contents': '//root/foo/b'\n"
1323       "                }\n"
1324       "              ]\n"
1325       "}\n"
1326       "]\n"
1327       "}",
1328       Lower);
1329   ASSERT_TRUE(FS.get() != nullptr);
1330 
1331   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
1332       new vfs::OverlayFileSystem(Lower));
1333   O->pushOverlay(FS);
1334 
1335   // file
1336   ErrorOr<vfs::Status> S = O->status("//root/file1");
1337   ASSERT_FALSE(S.getError());
1338   EXPECT_EQ("//root/foo/bar/a", S->getName());
1339   EXPECT_TRUE(S->IsVFSMapped);
1340 
1341   ErrorOr<vfs::Status> SLower = O->status("//root/foo/bar/a");
1342   EXPECT_EQ("//root/foo/bar/a", SLower->getName());
1343   EXPECT_TRUE(S->equivalent(*SLower));
1344   EXPECT_FALSE(SLower->IsVFSMapped);
1345 
1346   // file after opening
1347   auto OpenedF = O->openFileForRead("//root/file1");
1348   ASSERT_FALSE(OpenedF.getError());
1349   auto OpenedS = (*OpenedF)->status();
1350   ASSERT_FALSE(OpenedS.getError());
1351   EXPECT_EQ("//root/foo/bar/a", OpenedS->getName());
1352   EXPECT_TRUE(OpenedS->IsVFSMapped);
1353 
1354   // directory
1355   S = O->status("//root/");
1356   ASSERT_FALSE(S.getError());
1357   EXPECT_TRUE(S->isDirectory());
1358   EXPECT_TRUE(S->equivalent(*O->status("//root/"))); // non-volatile UniqueID
1359 
1360   // broken mapping
1361   EXPECT_EQ(O->status("//root/file2").getError(),
1362             llvm::errc::no_such_file_or_directory);
1363   EXPECT_EQ(0, NumDiagnostics);
1364 }
1365 
1366 TEST_F(VFSFromYAMLTest, CaseInsensitive) {
1367   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
1368   Lower->addRegularFile("//root/foo/bar/a");
1369   IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
1370       "{ 'case-sensitive': 'false',\n"
1371       "  'roots': [\n"
1372       "{\n"
1373       "  'type': 'directory',\n"
1374       "  'name': '//root/',\n"
1375       "  'contents': [ {\n"
1376       "                  'type': 'file',\n"
1377       "                  'name': 'XX',\n"
1378       "                  'external-contents': '//root/foo/bar/a'\n"
1379       "                }\n"
1380       "              ]\n"
1381       "}]}",
1382       Lower);
1383   ASSERT_TRUE(FS.get() != nullptr);
1384 
1385   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
1386       new vfs::OverlayFileSystem(Lower));
1387   O->pushOverlay(FS);
1388 
1389   ErrorOr<vfs::Status> S = O->status("//root/XX");
1390   ASSERT_FALSE(S.getError());
1391 
1392   ErrorOr<vfs::Status> SS = O->status("//root/xx");
1393   ASSERT_FALSE(SS.getError());
1394   EXPECT_TRUE(S->equivalent(*SS));
1395   SS = O->status("//root/xX");
1396   EXPECT_TRUE(S->equivalent(*SS));
1397   SS = O->status("//root/Xx");
1398   EXPECT_TRUE(S->equivalent(*SS));
1399   EXPECT_EQ(0, NumDiagnostics);
1400 }
1401 
1402 TEST_F(VFSFromYAMLTest, CaseSensitive) {
1403   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
1404   Lower->addRegularFile("//root/foo/bar/a");
1405   IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
1406       "{ 'case-sensitive': 'true',\n"
1407       "  'roots': [\n"
1408       "{\n"
1409       "  'type': 'directory',\n"
1410       "  'name': '//root/',\n"
1411       "  'contents': [ {\n"
1412       "                  'type': 'file',\n"
1413       "                  'name': 'XX',\n"
1414       "                  'external-contents': '//root/foo/bar/a'\n"
1415       "                }\n"
1416       "              ]\n"
1417       "}]}",
1418       Lower);
1419   ASSERT_TRUE(FS.get() != nullptr);
1420 
1421   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
1422       new vfs::OverlayFileSystem(Lower));
1423   O->pushOverlay(FS);
1424 
1425   ErrorOr<vfs::Status> SS = O->status("//root/xx");
1426   EXPECT_EQ(SS.getError(), llvm::errc::no_such_file_or_directory);
1427   SS = O->status("//root/xX");
1428   EXPECT_EQ(SS.getError(), llvm::errc::no_such_file_or_directory);
1429   SS = O->status("//root/Xx");
1430   EXPECT_EQ(SS.getError(), llvm::errc::no_such_file_or_directory);
1431   EXPECT_EQ(0, NumDiagnostics);
1432 }
1433 
1434 TEST_F(VFSFromYAMLTest, IllegalVFSFile) {
1435   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
1436 
1437   // invalid YAML at top-level
1438   IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString("{]", Lower);
1439   EXPECT_EQ(nullptr, FS.get());
1440   // invalid YAML in roots
1441   FS = getFromYAMLString("{ 'roots':[}", Lower);
1442   // invalid YAML in directory
1443   FS = getFromYAMLString(
1444       "{ 'roots':[ { 'name': 'foo', 'type': 'directory', 'contents': [}",
1445       Lower);
1446   EXPECT_EQ(nullptr, FS.get());
1447 
1448   // invalid configuration
1449   FS = getFromYAMLString("{ 'knobular': 'true', 'roots':[] }", Lower);
1450   EXPECT_EQ(nullptr, FS.get());
1451   FS = getFromYAMLString("{ 'case-sensitive': 'maybe', 'roots':[] }", Lower);
1452   EXPECT_EQ(nullptr, FS.get());
1453 
1454   // invalid roots
1455   FS = getFromYAMLString("{ 'roots':'' }", Lower);
1456   EXPECT_EQ(nullptr, FS.get());
1457   FS = getFromYAMLString("{ 'roots':{} }", Lower);
1458   EXPECT_EQ(nullptr, FS.get());
1459 
1460   // invalid entries
1461   FS = getFromYAMLString(
1462       "{ 'roots':[ { 'type': 'other', 'name': 'me', 'contents': '' }", Lower);
1463   EXPECT_EQ(nullptr, FS.get());
1464   FS = getFromYAMLString("{ 'roots':[ { 'type': 'file', 'name': [], "
1465                          "'external-contents': 'other' }",
1466                          Lower);
1467   EXPECT_EQ(nullptr, FS.get());
1468   FS = getFromYAMLString(
1469       "{ 'roots':[ { 'type': 'file', 'name': 'me', 'external-contents': [] }",
1470       Lower);
1471   EXPECT_EQ(nullptr, FS.get());
1472   FS = getFromYAMLString(
1473       "{ 'roots':[ { 'type': 'file', 'name': 'me', 'external-contents': {} }",
1474       Lower);
1475   EXPECT_EQ(nullptr, FS.get());
1476   FS = getFromYAMLString(
1477       "{ 'roots':[ { 'type': 'directory', 'name': 'me', 'contents': {} }",
1478       Lower);
1479   EXPECT_EQ(nullptr, FS.get());
1480   FS = getFromYAMLString(
1481       "{ 'roots':[ { 'type': 'directory', 'name': 'me', 'contents': '' }",
1482       Lower);
1483   EXPECT_EQ(nullptr, FS.get());
1484   FS = getFromYAMLString(
1485       "{ 'roots':[ { 'thingy': 'directory', 'name': 'me', 'contents': [] }",
1486       Lower);
1487   EXPECT_EQ(nullptr, FS.get());
1488 
1489   // missing mandatory fields
1490   FS = getFromYAMLString("{ 'roots':[ { 'type': 'file', 'name': 'me' }", Lower);
1491   EXPECT_EQ(nullptr, FS.get());
1492   FS = getFromYAMLString(
1493       "{ 'roots':[ { 'type': 'file', 'external-contents': 'other' }", Lower);
1494   EXPECT_EQ(nullptr, FS.get());
1495   FS = getFromYAMLString("{ 'roots':[ { 'name': 'me', 'contents': [] }", Lower);
1496   EXPECT_EQ(nullptr, FS.get());
1497 
1498   // duplicate keys
1499   FS = getFromYAMLString("{ 'roots':[], 'roots':[] }", Lower);
1500   EXPECT_EQ(nullptr, FS.get());
1501   FS = getFromYAMLString(
1502       "{ 'case-sensitive':'true', 'case-sensitive':'true', 'roots':[] }",
1503       Lower);
1504   EXPECT_EQ(nullptr, FS.get());
1505   FS =
1506       getFromYAMLString("{ 'roots':[{'name':'me', 'name':'you', 'type':'file', "
1507                         "'external-contents':'blah' } ] }",
1508                         Lower);
1509   EXPECT_EQ(nullptr, FS.get());
1510 
1511   // missing version
1512   FS = getFromYAMLRawString("{ 'roots':[] }", Lower);
1513   EXPECT_EQ(nullptr, FS.get());
1514 
1515   // bad version number
1516   FS = getFromYAMLRawString("{ 'version':'foo', 'roots':[] }", Lower);
1517   EXPECT_EQ(nullptr, FS.get());
1518   FS = getFromYAMLRawString("{ 'version':-1, 'roots':[] }", Lower);
1519   EXPECT_EQ(nullptr, FS.get());
1520   FS = getFromYAMLRawString("{ 'version':100000, 'roots':[] }", Lower);
1521   EXPECT_EQ(nullptr, FS.get());
1522   EXPECT_EQ(24, NumDiagnostics);
1523 }
1524 
1525 TEST_F(VFSFromYAMLTest, UseExternalName) {
1526   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
1527   Lower->addRegularFile("//root/external/file");
1528 
1529   IntrusiveRefCntPtr<vfs::FileSystem> FS =
1530       getFromYAMLString("{ 'roots': [\n"
1531                         "  { 'type': 'file', 'name': '//root/A',\n"
1532                         "    'external-contents': '//root/external/file'\n"
1533                         "  },\n"
1534                         "  { 'type': 'file', 'name': '//root/B',\n"
1535                         "    'use-external-name': true,\n"
1536                         "    'external-contents': '//root/external/file'\n"
1537                         "  },\n"
1538                         "  { 'type': 'file', 'name': '//root/C',\n"
1539                         "    'use-external-name': false,\n"
1540                         "    'external-contents': '//root/external/file'\n"
1541                         "  }\n"
1542                         "] }",
1543                         Lower);
1544   ASSERT_TRUE(nullptr != FS.get());
1545 
1546   // default true
1547   EXPECT_EQ("//root/external/file", FS->status("//root/A")->getName());
1548   // explicit
1549   EXPECT_EQ("//root/external/file", FS->status("//root/B")->getName());
1550   EXPECT_EQ("//root/C", FS->status("//root/C")->getName());
1551 
1552   // global configuration
1553   FS = getFromYAMLString("{ 'use-external-names': false,\n"
1554                          "  'roots': [\n"
1555                          "  { 'type': 'file', 'name': '//root/A',\n"
1556                          "    'external-contents': '//root/external/file'\n"
1557                          "  },\n"
1558                          "  { 'type': 'file', 'name': '//root/B',\n"
1559                          "    'use-external-name': true,\n"
1560                          "    'external-contents': '//root/external/file'\n"
1561                          "  },\n"
1562                          "  { 'type': 'file', 'name': '//root/C',\n"
1563                          "    'use-external-name': false,\n"
1564                          "    'external-contents': '//root/external/file'\n"
1565                          "  }\n"
1566                          "] }",
1567                          Lower);
1568   ASSERT_TRUE(nullptr != FS.get());
1569 
1570   // default
1571   EXPECT_EQ("//root/A", FS->status("//root/A")->getName());
1572   // explicit
1573   EXPECT_EQ("//root/external/file", FS->status("//root/B")->getName());
1574   EXPECT_EQ("//root/C", FS->status("//root/C")->getName());
1575 }
1576 
1577 TEST_F(VFSFromYAMLTest, MultiComponentPath) {
1578   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
1579   Lower->addRegularFile("//root/other");
1580 
1581   // file in roots
1582   IntrusiveRefCntPtr<vfs::FileSystem> FS =
1583       getFromYAMLString("{ 'roots': [\n"
1584                         "  { 'type': 'file', 'name': '//root/path/to/file',\n"
1585                         "    'external-contents': '//root/other' }]\n"
1586                         "}",
1587                         Lower);
1588   ASSERT_TRUE(nullptr != FS.get());
1589   EXPECT_FALSE(FS->status("//root/path/to/file").getError());
1590   EXPECT_FALSE(FS->status("//root/path/to").getError());
1591   EXPECT_FALSE(FS->status("//root/path").getError());
1592   EXPECT_FALSE(FS->status("//root/").getError());
1593 
1594   // at the start
1595   FS = getFromYAMLString(
1596       "{ 'roots': [\n"
1597       "  { 'type': 'directory', 'name': '//root/path/to',\n"
1598       "    'contents': [ { 'type': 'file', 'name': 'file',\n"
1599       "                    'external-contents': '//root/other' }]}]\n"
1600       "}",
1601       Lower);
1602   ASSERT_TRUE(nullptr != FS.get());
1603   EXPECT_FALSE(FS->status("//root/path/to/file").getError());
1604   EXPECT_FALSE(FS->status("//root/path/to").getError());
1605   EXPECT_FALSE(FS->status("//root/path").getError());
1606   EXPECT_FALSE(FS->status("//root/").getError());
1607 
1608   // at the end
1609   FS = getFromYAMLString(
1610       "{ 'roots': [\n"
1611       "  { 'type': 'directory', 'name': '//root/',\n"
1612       "    'contents': [ { 'type': 'file', 'name': 'path/to/file',\n"
1613       "                    'external-contents': '//root/other' }]}]\n"
1614       "}",
1615       Lower);
1616   ASSERT_TRUE(nullptr != FS.get());
1617   EXPECT_FALSE(FS->status("//root/path/to/file").getError());
1618   EXPECT_FALSE(FS->status("//root/path/to").getError());
1619   EXPECT_FALSE(FS->status("//root/path").getError());
1620   EXPECT_FALSE(FS->status("//root/").getError());
1621 }
1622 
1623 TEST_F(VFSFromYAMLTest, TrailingSlashes) {
1624   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
1625   Lower->addRegularFile("//root/other");
1626 
1627   // file in roots
1628   IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
1629       "{ 'roots': [\n"
1630       "  { 'type': 'directory', 'name': '//root/path/to////',\n"
1631       "    'contents': [ { 'type': 'file', 'name': 'file',\n"
1632       "                    'external-contents': '//root/other' }]}]\n"
1633       "}",
1634       Lower);
1635   ASSERT_TRUE(nullptr != FS.get());
1636   EXPECT_FALSE(FS->status("//root/path/to/file").getError());
1637   EXPECT_FALSE(FS->status("//root/path/to").getError());
1638   EXPECT_FALSE(FS->status("//root/path").getError());
1639   EXPECT_FALSE(FS->status("//root/").getError());
1640 }
1641 
1642 TEST_F(VFSFromYAMLTest, DirectoryIteration) {
1643   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
1644   Lower->addDirectory("//root/");
1645   Lower->addDirectory("//root/foo");
1646   Lower->addDirectory("//root/foo/bar");
1647   Lower->addRegularFile("//root/foo/bar/a");
1648   Lower->addRegularFile("//root/foo/bar/b");
1649   Lower->addRegularFile("//root/file3");
1650   IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
1651       "{ 'use-external-names': false,\n"
1652       "  'roots': [\n"
1653       "{\n"
1654       "  'type': 'directory',\n"
1655       "  'name': '//root/',\n"
1656       "  'contents': [ {\n"
1657       "                  'type': 'file',\n"
1658       "                  'name': 'file1',\n"
1659       "                  'external-contents': '//root/foo/bar/a'\n"
1660       "                },\n"
1661       "                {\n"
1662       "                  'type': 'file',\n"
1663       "                  'name': 'file2',\n"
1664       "                  'external-contents': '//root/foo/bar/b'\n"
1665       "                }\n"
1666       "              ]\n"
1667       "}\n"
1668       "]\n"
1669       "}",
1670       Lower);
1671   ASSERT_TRUE(FS.get() != nullptr);
1672 
1673   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
1674       new vfs::OverlayFileSystem(Lower));
1675   O->pushOverlay(FS);
1676 
1677   std::error_code EC;
1678   checkContents(O->dir_begin("//root/", EC),
1679                 {"//root/file1", "//root/file2", "//root/file3", "//root/foo"});
1680 
1681   checkContents(O->dir_begin("//root/foo/bar", EC),
1682                 {"//root/foo/bar/a", "//root/foo/bar/b"});
1683 }
1684 
1685 TEST_F(VFSFromYAMLTest, DirectoryIterationSameDirMultipleEntries) {
1686   // https://llvm.org/bugs/show_bug.cgi?id=27725
1687   if (!supportsSameDirMultipleYAMLEntries())
1688     return;
1689 
1690   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
1691   Lower->addDirectory("//root/zab");
1692   Lower->addDirectory("//root/baz");
1693   Lower->addRegularFile("//root/zab/a");
1694   Lower->addRegularFile("//root/zab/b");
1695   IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
1696       "{ 'use-external-names': false,\n"
1697       "  'roots': [\n"
1698       "{\n"
1699       "  'type': 'directory',\n"
1700       "  'name': '//root/baz/',\n"
1701       "  'contents': [ {\n"
1702       "                  'type': 'file',\n"
1703       "                  'name': 'x',\n"
1704       "                  'external-contents': '//root/zab/a'\n"
1705       "                }\n"
1706       "              ]\n"
1707       "},\n"
1708       "{\n"
1709       "  'type': 'directory',\n"
1710       "  'name': '//root/baz/',\n"
1711       "  'contents': [ {\n"
1712       "                  'type': 'file',\n"
1713       "                  'name': 'y',\n"
1714       "                  'external-contents': '//root/zab/b'\n"
1715       "                }\n"
1716       "              ]\n"
1717       "}\n"
1718       "]\n"
1719       "}",
1720       Lower);
1721   ASSERT_TRUE(FS.get() != nullptr);
1722 
1723   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
1724       new vfs::OverlayFileSystem(Lower));
1725   O->pushOverlay(FS);
1726 
1727   std::error_code EC;
1728 
1729   checkContents(O->dir_begin("//root/baz/", EC),
1730                 {"//root/baz/x", "//root/baz/y"});
1731 }
1732 
1733 TEST_F(VFSFromYAMLTest, RecursiveDirectoryIterationLevel) {
1734 
1735   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
1736   Lower->addDirectory("//root/a");
1737   Lower->addDirectory("//root/a/b");
1738   Lower->addDirectory("//root/a/b/c");
1739   Lower->addRegularFile("//root/a/b/c/file");
1740   IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
1741       "{ 'use-external-names': false,\n"
1742       "  'roots': [\n"
1743       "{\n"
1744       "  'type': 'directory',\n"
1745       "  'name': '//root/a/b/c/',\n"
1746       "  'contents': [ {\n"
1747       "                  'type': 'file',\n"
1748       "                  'name': 'file',\n"
1749       "                  'external-contents': '//root/a/b/c/file'\n"
1750       "                }\n"
1751       "              ]\n"
1752       "},\n"
1753       "]\n"
1754       "}",
1755       Lower);
1756   ASSERT_TRUE(FS.get() != nullptr);
1757 
1758   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
1759       new vfs::OverlayFileSystem(Lower));
1760   O->pushOverlay(FS);
1761 
1762   std::error_code EC;
1763 
1764   // Test recursive_directory_iterator level()
1765   vfs::recursive_directory_iterator I = vfs::recursive_directory_iterator(
1766                                         *O, "//root", EC),
1767                                     E;
1768   ASSERT_FALSE(EC);
1769   for (int l = 0; I != E; I.increment(EC), ++l) {
1770     ASSERT_FALSE(EC);
1771     EXPECT_EQ(I.level(), l);
1772   }
1773   EXPECT_EQ(I, E);
1774 }
1775 
1776 TEST_F(VFSFromYAMLTest, RelativePaths) {
1777   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
1778   // Filename at root level without a parent directory.
1779   IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
1780       "{ 'roots': [\n"
1781       "  { 'type': 'file', 'name': 'file-not-in-directory.h',\n"
1782       "    'external-contents': '//root/external/file'\n"
1783       "  }\n"
1784       "] }",
1785       Lower);
1786   EXPECT_EQ(nullptr, FS.get());
1787 
1788   // Relative file path.
1789   FS = getFromYAMLString("{ 'roots': [\n"
1790                          "  { 'type': 'file', 'name': 'relative/file/path.h',\n"
1791                          "    'external-contents': '//root/external/file'\n"
1792                          "  }\n"
1793                          "] }",
1794                          Lower);
1795   EXPECT_EQ(nullptr, FS.get());
1796 
1797   // Relative directory path.
1798   FS = getFromYAMLString(
1799       "{ 'roots': [\n"
1800       "  { 'type': 'directory', 'name': 'relative/directory/path.h',\n"
1801       "    'contents': []\n"
1802       "  }\n"
1803       "] }",
1804       Lower);
1805   EXPECT_EQ(nullptr, FS.get());
1806 
1807   EXPECT_EQ(3, NumDiagnostics);
1808 }
1809 
1810 TEST_F(VFSFromYAMLTest, NonFallthroughDirectoryIteration) {
1811   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
1812   Lower->addDirectory("//root/");
1813   Lower->addRegularFile("//root/a");
1814   Lower->addRegularFile("//root/b");
1815   IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
1816       "{ 'use-external-names': false,\n"
1817       "  'fallthrough': false,\n"
1818       "  'roots': [\n"
1819       "{\n"
1820       "  'type': 'directory',\n"
1821       "  'name': '//root/',\n"
1822       "  'contents': [ {\n"
1823       "                  'type': 'file',\n"
1824       "                  'name': 'c',\n"
1825       "                  'external-contents': '//root/a'\n"
1826       "                }\n"
1827       "              ]\n"
1828       "}\n"
1829       "]\n"
1830       "}",
1831       Lower);
1832   ASSERT_TRUE(FS.get() != nullptr);
1833 
1834   std::error_code EC;
1835   checkContents(FS->dir_begin("//root/", EC),
1836                 {"//root/c"});
1837 }
1838 
1839 TEST_F(VFSFromYAMLTest, DirectoryIterationWithDuplicates) {
1840   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
1841   Lower->addDirectory("//root/");
1842   Lower->addRegularFile("//root/a");
1843   Lower->addRegularFile("//root/b");
1844   IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
1845       "{ 'use-external-names': false,\n"
1846       "  'roots': [\n"
1847       "{\n"
1848       "  'type': 'directory',\n"
1849       "  'name': '//root/',\n"
1850       "  'contents': [ {\n"
1851       "                  'type': 'file',\n"
1852       "                  'name': 'a',\n"
1853       "                  'external-contents': '//root/a'\n"
1854       "                }\n"
1855       "              ]\n"
1856       "}\n"
1857       "]\n"
1858       "}",
1859 	  Lower);
1860   ASSERT_TRUE(FS.get() != nullptr);
1861 
1862   std::error_code EC;
1863   checkContents(FS->dir_begin("//root/", EC),
1864                 {"//root/a", "//root/b"});
1865 }
1866 
1867 TEST_F(VFSFromYAMLTest, DirectoryIterationErrorInVFSLayer) {
1868   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
1869   Lower->addDirectory("//root/");
1870   Lower->addDirectory("//root/foo");
1871   Lower->addRegularFile("//root/foo/a");
1872   Lower->addRegularFile("//root/foo/b");
1873   IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
1874       "{ 'use-external-names': false,\n"
1875       "  'roots': [\n"
1876       "{\n"
1877       "  'type': 'directory',\n"
1878       "  'name': '//root/',\n"
1879       "  'contents': [ {\n"
1880       "                  'type': 'file',\n"
1881       "                  'name': 'bar/a',\n"
1882       "                  'external-contents': '//root/foo/a'\n"
1883       "                }\n"
1884       "              ]\n"
1885       "}\n"
1886       "]\n"
1887       "}",
1888       Lower);
1889   ASSERT_TRUE(FS.get() != nullptr);
1890 
1891   std::error_code EC;
1892   checkContents(FS->dir_begin("//root/foo", EC),
1893                 {"//root/foo/a", "//root/foo/b"});
1894 }
1895 
1896 TEST_F(VFSFromYAMLTest, GetRealPath) {
1897   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
1898   Lower->addDirectory("//dir/");
1899   Lower->addRegularFile("/foo");
1900   Lower->addSymlink("/link");
1901   IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
1902       "{ 'use-external-names': false,\n"
1903       "  'roots': [\n"
1904       "{\n"
1905       "  'type': 'directory',\n"
1906       "  'name': '//root/',\n"
1907       "  'contents': [ {\n"
1908       "                  'type': 'file',\n"
1909       "                  'name': 'bar',\n"
1910       "                  'external-contents': '/link'\n"
1911       "                }\n"
1912       "              ]\n"
1913       "},\n"
1914       "{\n"
1915       "  'type': 'directory',\n"
1916       "  'name': '//dir/',\n"
1917       "  'contents': []\n"
1918       "}\n"
1919       "]\n"
1920       "}",
1921       Lower);
1922   ASSERT_TRUE(FS.get() != nullptr);
1923 
1924   // Regular file present in underlying file system.
1925   SmallString<16> RealPath;
1926   EXPECT_FALSE(FS->getRealPath("/foo", RealPath));
1927   EXPECT_EQ(RealPath.str(), "/foo");
1928 
1929   // File present in YAML pointing to symlink in underlying file system.
1930   EXPECT_FALSE(FS->getRealPath("//root/bar", RealPath));
1931   EXPECT_EQ(RealPath.str(), "/symlink");
1932 
1933   // Directories should fall back to the underlying file system is possible.
1934   EXPECT_FALSE(FS->getRealPath("//dir/", RealPath));
1935   EXPECT_EQ(RealPath.str(), "//dir/");
1936 
1937   // Try a non-existing file.
1938   EXPECT_EQ(FS->getRealPath("/non_existing", RealPath),
1939             errc::no_such_file_or_directory);
1940 }
1941