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