xref: /llvm-project/llvm/unittests/Support/VirtualFileSystemTest.cpp (revision 99538e89a9dffce36ee4f73728039cb741df5f08)
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 } // end anonymous namespace
386 
387 TEST(VirtualFileSystemTest, BasicRealFSIteration) {
388   ScopedDir TestDirectory("virtual-file-system-test", /*Unique*/ true);
389   IntrusiveRefCntPtr<vfs::FileSystem> FS = vfs::getRealFileSystem();
390 
391   std::error_code EC;
392   vfs::directory_iterator I = FS->dir_begin(Twine(TestDirectory), EC);
393   ASSERT_FALSE(EC);
394   EXPECT_EQ(vfs::directory_iterator(), I); // empty directory is empty
395 
396   ScopedDir _a(TestDirectory + "/a");
397   ScopedDir _ab(TestDirectory + "/a/b");
398   ScopedDir _c(TestDirectory + "/c");
399   ScopedDir _cd(TestDirectory + "/c/d");
400 
401   I = FS->dir_begin(Twine(TestDirectory), EC);
402   ASSERT_FALSE(EC);
403   ASSERT_NE(vfs::directory_iterator(), I);
404   // Check either a or c, since we can't rely on the iteration order.
405   EXPECT_TRUE(I->path().endswith("a") || I->path().endswith("c"));
406   I.increment(EC);
407   ASSERT_FALSE(EC);
408   ASSERT_NE(vfs::directory_iterator(), I);
409   EXPECT_TRUE(I->path().endswith("a") || I->path().endswith("c"));
410   I.increment(EC);
411   EXPECT_EQ(vfs::directory_iterator(), I);
412 }
413 
414 #ifdef LLVM_ON_UNIX
415 TEST(VirtualFileSystemTest, BrokenSymlinkRealFSIteration) {
416   ScopedDir TestDirectory("virtual-file-system-test", /*Unique*/ true);
417   IntrusiveRefCntPtr<vfs::FileSystem> FS = vfs::getRealFileSystem();
418 
419   ScopedLink _a("no_such_file", TestDirectory + "/a");
420   ScopedDir _b(TestDirectory + "/b");
421   ScopedLink _c("no_such_file", TestDirectory + "/c");
422 
423   // Should get no iteration error, but a stat error for the broken symlinks.
424   std::map<std::string, std::error_code> StatResults;
425   std::error_code EC;
426   for (vfs::directory_iterator I = FS->dir_begin(Twine(TestDirectory), EC), E;
427        I != E; I.increment(EC)) {
428     EXPECT_FALSE(EC);
429     StatResults[sys::path::filename(I->path())] =
430         FS->status(I->path()).getError();
431   }
432   EXPECT_THAT(
433       StatResults,
434       ElementsAre(
435           Pair("a", std::make_error_code(std::errc::no_such_file_or_directory)),
436           Pair("b", std::error_code()),
437           Pair("c",
438                std::make_error_code(std::errc::no_such_file_or_directory))));
439 }
440 #endif
441 
442 TEST(VirtualFileSystemTest, BasicRealFSRecursiveIteration) {
443   ScopedDir TestDirectory("virtual-file-system-test", /*Unique*/ true);
444   IntrusiveRefCntPtr<vfs::FileSystem> FS = vfs::getRealFileSystem();
445 
446   std::error_code EC;
447   auto I = vfs::recursive_directory_iterator(*FS, Twine(TestDirectory), EC);
448   ASSERT_FALSE(EC);
449   EXPECT_EQ(vfs::recursive_directory_iterator(), I); // empty directory is empty
450 
451   ScopedDir _a(TestDirectory + "/a");
452   ScopedDir _ab(TestDirectory + "/a/b");
453   ScopedDir _c(TestDirectory + "/c");
454   ScopedDir _cd(TestDirectory + "/c/d");
455 
456   I = vfs::recursive_directory_iterator(*FS, Twine(TestDirectory), EC);
457   ASSERT_FALSE(EC);
458   ASSERT_NE(vfs::recursive_directory_iterator(), I);
459 
460   std::vector<std::string> Contents;
461   for (auto E = vfs::recursive_directory_iterator(); !EC && I != E;
462        I.increment(EC)) {
463     Contents.push_back(I->path());
464   }
465 
466   // Check contents, which may be in any order
467   EXPECT_EQ(4U, Contents.size());
468   int Counts[4] = {0, 0, 0, 0};
469   for (const std::string &Name : Contents) {
470     ASSERT_FALSE(Name.empty());
471     int Index = Name[Name.size() - 1] - 'a';
472     ASSERT_TRUE(Index >= 0 && Index < 4);
473     Counts[Index]++;
474   }
475   EXPECT_EQ(1, Counts[0]); // a
476   EXPECT_EQ(1, Counts[1]); // b
477   EXPECT_EQ(1, Counts[2]); // c
478   EXPECT_EQ(1, Counts[3]); // d
479 }
480 
481 TEST(VirtualFileSystemTest, BasicRealFSRecursiveIterationNoPush) {
482   ScopedDir TestDirectory("virtual-file-system-test", /*Unique*/ true);
483 
484   ScopedDir _a(TestDirectory + "/a");
485   ScopedDir _ab(TestDirectory + "/a/b");
486   ScopedDir _c(TestDirectory + "/c");
487   ScopedDir _cd(TestDirectory + "/c/d");
488   ScopedDir _e(TestDirectory + "/e");
489   ScopedDir _ef(TestDirectory + "/e/f");
490   ScopedDir _g(TestDirectory + "/g");
491 
492   IntrusiveRefCntPtr<vfs::FileSystem> FS = vfs::getRealFileSystem();
493 
494   // Test that calling no_push on entries without subdirectories has no effect.
495   {
496     std::error_code EC;
497     auto I = vfs::recursive_directory_iterator(*FS, Twine(TestDirectory), EC);
498     ASSERT_FALSE(EC);
499 
500     std::vector<std::string> Contents;
501     for (auto E = vfs::recursive_directory_iterator(); !EC && I != E;
502          I.increment(EC)) {
503       Contents.push_back(I->path());
504       char last = I->path().back();
505       switch (last) {
506       case 'b':
507       case 'd':
508       case 'f':
509       case 'g':
510         I.no_push();
511         break;
512       default:
513         break;
514       }
515     }
516     EXPECT_EQ(7U, Contents.size());
517   }
518 
519   // Test that calling no_push skips subdirectories.
520   {
521     std::error_code EC;
522     auto I = vfs::recursive_directory_iterator(*FS, Twine(TestDirectory), EC);
523     ASSERT_FALSE(EC);
524 
525     std::vector<std::string> Contents;
526     for (auto E = vfs::recursive_directory_iterator(); !EC && I != E;
527          I.increment(EC)) {
528       Contents.push_back(I->path());
529       char last = I->path().back();
530       switch (last) {
531       case 'a':
532       case 'c':
533       case 'e':
534         I.no_push();
535         break;
536       default:
537         break;
538       }
539     }
540 
541     // Check contents, which may be in any order
542     EXPECT_EQ(4U, Contents.size());
543     int Counts[7] = {0, 0, 0, 0, 0, 0, 0};
544     for (const std::string &Name : Contents) {
545       ASSERT_FALSE(Name.empty());
546       int Index = Name[Name.size() - 1] - 'a';
547       ASSERT_TRUE(Index >= 0 && Index < 7);
548       Counts[Index]++;
549     }
550     EXPECT_EQ(1, Counts[0]); // a
551     EXPECT_EQ(0, Counts[1]); // b
552     EXPECT_EQ(1, Counts[2]); // c
553     EXPECT_EQ(0, Counts[3]); // d
554     EXPECT_EQ(1, Counts[4]); // e
555     EXPECT_EQ(0, Counts[5]); // f
556     EXPECT_EQ(1, Counts[6]); // g
557   }
558 }
559 
560 #ifdef LLVM_ON_UNIX
561 TEST(VirtualFileSystemTest, BrokenSymlinkRealFSRecursiveIteration) {
562   ScopedDir TestDirectory("virtual-file-system-test", /*Unique*/ true);
563   IntrusiveRefCntPtr<vfs::FileSystem> FS = vfs::getRealFileSystem();
564 
565   ScopedLink _a("no_such_file", TestDirectory + "/a");
566   ScopedDir _b(TestDirectory + "/b");
567   ScopedLink _ba("no_such_file", TestDirectory + "/b/a");
568   ScopedDir _bb(TestDirectory + "/b/b");
569   ScopedLink _bc("no_such_file", TestDirectory + "/b/c");
570   ScopedLink _c("no_such_file", TestDirectory + "/c");
571   ScopedDir _d(TestDirectory + "/d");
572   ScopedDir _dd(TestDirectory + "/d/d");
573   ScopedDir _ddd(TestDirectory + "/d/d/d");
574   ScopedLink _e("no_such_file", TestDirectory + "/e");
575 
576   std::vector<std::string> VisitedBrokenSymlinks;
577   std::vector<std::string> VisitedNonBrokenSymlinks;
578   std::error_code EC;
579   for (vfs::recursive_directory_iterator I(*FS, Twine(TestDirectory), EC), E;
580        I != E; I.increment(EC)) {
581     EXPECT_FALSE(EC);
582     (FS->status(I->path()) ? VisitedNonBrokenSymlinks : VisitedBrokenSymlinks)
583         .push_back(I->path());
584   }
585 
586   // Check visited file names.
587   EXPECT_THAT(VisitedBrokenSymlinks,
588               UnorderedElementsAre(StringRef(_a), StringRef(_ba),
589                                    StringRef(_bc), StringRef(_c),
590                                    StringRef(_e)));
591   EXPECT_THAT(VisitedNonBrokenSymlinks,
592               UnorderedElementsAre(StringRef(_b), StringRef(_bb), StringRef(_d),
593                                    StringRef(_dd), StringRef(_ddd)));
594 }
595 #endif
596 
597 template <typename DirIter>
598 static void checkContents(DirIter I, ArrayRef<StringRef> ExpectedOut) {
599   std::error_code EC;
600   SmallVector<StringRef, 4> Expected(ExpectedOut.begin(), ExpectedOut.end());
601   SmallVector<std::string, 4> InputToCheck;
602 
603   // Do not rely on iteration order to check for contents, sort both
604   // content vectors before comparison.
605   for (DirIter E; !EC && I != E; I.increment(EC))
606     InputToCheck.push_back(I->path());
607 
608   llvm::sort(InputToCheck);
609   llvm::sort(Expected);
610   EXPECT_EQ(InputToCheck.size(), Expected.size());
611 
612   unsigned LastElt = std::min(InputToCheck.size(), Expected.size());
613   for (unsigned Idx = 0; Idx != LastElt; ++Idx)
614     EXPECT_EQ(StringRef(InputToCheck[Idx]), Expected[Idx]);
615 }
616 
617 TEST(VirtualFileSystemTest, OverlayIteration) {
618   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
619   IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem());
620   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
621       new vfs::OverlayFileSystem(Lower));
622   O->pushOverlay(Upper);
623 
624   std::error_code EC;
625   checkContents(O->dir_begin("/", EC), ArrayRef<StringRef>());
626 
627   Lower->addRegularFile("/file1");
628   checkContents(O->dir_begin("/", EC), ArrayRef<StringRef>("/file1"));
629 
630   Upper->addRegularFile("/file2");
631   checkContents(O->dir_begin("/", EC), {"/file2", "/file1"});
632 
633   Lower->addDirectory("/dir1");
634   Lower->addRegularFile("/dir1/foo");
635   Upper->addDirectory("/dir2");
636   Upper->addRegularFile("/dir2/foo");
637   checkContents(O->dir_begin("/dir2", EC), ArrayRef<StringRef>("/dir2/foo"));
638   checkContents(O->dir_begin("/", EC), {"/dir2", "/file2", "/dir1", "/file1"});
639 }
640 
641 TEST(VirtualFileSystemTest, OverlayRecursiveIteration) {
642   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
643   IntrusiveRefCntPtr<DummyFileSystem> Middle(new DummyFileSystem());
644   IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem());
645   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
646       new vfs::OverlayFileSystem(Lower));
647   O->pushOverlay(Middle);
648   O->pushOverlay(Upper);
649 
650   std::error_code EC;
651   checkContents(vfs::recursive_directory_iterator(*O, "/", EC),
652                 ArrayRef<StringRef>());
653 
654   Lower->addRegularFile("/file1");
655   checkContents(vfs::recursive_directory_iterator(*O, "/", EC),
656                 ArrayRef<StringRef>("/file1"));
657 
658   Upper->addDirectory("/dir");
659   Upper->addRegularFile("/dir/file2");
660   checkContents(vfs::recursive_directory_iterator(*O, "/", EC),
661                 {"/dir", "/dir/file2", "/file1"});
662 
663   Lower->addDirectory("/dir1");
664   Lower->addRegularFile("/dir1/foo");
665   Lower->addDirectory("/dir1/a");
666   Lower->addRegularFile("/dir1/a/b");
667   Middle->addDirectory("/a");
668   Middle->addDirectory("/a/b");
669   Middle->addDirectory("/a/b/c");
670   Middle->addRegularFile("/a/b/c/d");
671   Middle->addRegularFile("/hiddenByUp");
672   Upper->addDirectory("/dir2");
673   Upper->addRegularFile("/dir2/foo");
674   Upper->addRegularFile("/hiddenByUp");
675   checkContents(vfs::recursive_directory_iterator(*O, "/dir2", EC),
676                 ArrayRef<StringRef>("/dir2/foo"));
677   checkContents(vfs::recursive_directory_iterator(*O, "/", EC),
678                 {"/dir", "/dir/file2", "/dir2", "/dir2/foo", "/hiddenByUp",
679                  "/a", "/a/b", "/a/b/c", "/a/b/c/d", "/dir1", "/dir1/a",
680                  "/dir1/a/b", "/dir1/foo", "/file1"});
681 }
682 
683 TEST(VirtualFileSystemTest, ThreeLevelIteration) {
684   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
685   IntrusiveRefCntPtr<DummyFileSystem> Middle(new DummyFileSystem());
686   IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem());
687   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
688       new vfs::OverlayFileSystem(Lower));
689   O->pushOverlay(Middle);
690   O->pushOverlay(Upper);
691 
692   std::error_code EC;
693   checkContents(O->dir_begin("/", EC), ArrayRef<StringRef>());
694 
695   Middle->addRegularFile("/file2");
696   checkContents(O->dir_begin("/", EC), ArrayRef<StringRef>("/file2"));
697 
698   Lower->addRegularFile("/file1");
699   Upper->addRegularFile("/file3");
700   checkContents(O->dir_begin("/", EC), {"/file3", "/file2", "/file1"});
701 }
702 
703 TEST(VirtualFileSystemTest, HiddenInIteration) {
704   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
705   IntrusiveRefCntPtr<DummyFileSystem> Middle(new DummyFileSystem());
706   IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem());
707   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
708       new vfs::OverlayFileSystem(Lower));
709   O->pushOverlay(Middle);
710   O->pushOverlay(Upper);
711 
712   std::error_code EC;
713   Lower->addRegularFile("/onlyInLow");
714   Lower->addDirectory("/hiddenByMid");
715   Lower->addDirectory("/hiddenByUp");
716   Middle->addRegularFile("/onlyInMid");
717   Middle->addRegularFile("/hiddenByMid");
718   Middle->addDirectory("/hiddenByUp");
719   Upper->addRegularFile("/onlyInUp");
720   Upper->addRegularFile("/hiddenByUp");
721   checkContents(
722       O->dir_begin("/", EC),
723       {"/hiddenByUp", "/onlyInUp", "/hiddenByMid", "/onlyInMid", "/onlyInLow"});
724 
725   // Make sure we get the top-most entry
726   {
727     std::error_code EC;
728     vfs::directory_iterator I = O->dir_begin("/", EC), E;
729     for (; !EC && I != E; I.increment(EC))
730       if (I->path() == "/hiddenByUp")
731         break;
732     ASSERT_NE(E, I);
733     EXPECT_EQ(sys::fs::file_type::regular_file, I->type());
734   }
735   {
736     std::error_code EC;
737     vfs::directory_iterator I = O->dir_begin("/", EC), E;
738     for (; !EC && I != E; I.increment(EC))
739       if (I->path() == "/hiddenByMid")
740         break;
741     ASSERT_NE(E, I);
742     EXPECT_EQ(sys::fs::file_type::regular_file, I->type());
743   }
744 }
745 
746 class InMemoryFileSystemTest : public ::testing::Test {
747 protected:
748   llvm::vfs::InMemoryFileSystem FS;
749   llvm::vfs::InMemoryFileSystem NormalizedFS;
750 
751   InMemoryFileSystemTest()
752       : FS(/*UseNormalizedPaths=*/false),
753         NormalizedFS(/*UseNormalizedPaths=*/true) {}
754 };
755 
756 MATCHER_P2(IsHardLinkTo, FS, Target, "") {
757   StringRef From = arg;
758   StringRef To = Target;
759   auto OpenedFrom = FS->openFileForRead(From);
760   auto OpenedTo = FS->openFileForRead(To);
761   return !OpenedFrom.getError() && !OpenedTo.getError() &&
762          (*OpenedFrom)->status()->getUniqueID() ==
763              (*OpenedTo)->status()->getUniqueID();
764 }
765 
766 TEST_F(InMemoryFileSystemTest, IsEmpty) {
767   auto Stat = FS.status("/a");
768   ASSERT_EQ(Stat.getError(), errc::no_such_file_or_directory) << FS.toString();
769   Stat = FS.status("/");
770   ASSERT_EQ(Stat.getError(), errc::no_such_file_or_directory) << FS.toString();
771 }
772 
773 TEST_F(InMemoryFileSystemTest, WindowsPath) {
774   FS.addFile("c:/windows/system128/foo.cpp", 0, MemoryBuffer::getMemBuffer(""));
775   auto Stat = FS.status("c:");
776 #if !defined(_WIN32)
777   ASSERT_FALSE(Stat.getError()) << Stat.getError() << FS.toString();
778 #endif
779   Stat = FS.status("c:/windows/system128/foo.cpp");
780   ASSERT_FALSE(Stat.getError()) << Stat.getError() << FS.toString();
781   FS.addFile("d:/windows/foo.cpp", 0, MemoryBuffer::getMemBuffer(""));
782   Stat = FS.status("d:/windows/foo.cpp");
783   ASSERT_FALSE(Stat.getError()) << Stat.getError() << FS.toString();
784 }
785 
786 TEST_F(InMemoryFileSystemTest, OverlayFile) {
787   FS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a"));
788   NormalizedFS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a"));
789   auto Stat = FS.status("/");
790   ASSERT_FALSE(Stat.getError()) << Stat.getError() << FS.toString();
791   Stat = FS.status("/.");
792   ASSERT_FALSE(Stat);
793   Stat = NormalizedFS.status("/.");
794   ASSERT_FALSE(Stat.getError()) << Stat.getError() << FS.toString();
795   Stat = FS.status("/a");
796   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
797   ASSERT_EQ("/a", Stat->getName());
798 }
799 
800 TEST_F(InMemoryFileSystemTest, OverlayFileNoOwn) {
801   auto Buf = MemoryBuffer::getMemBuffer("a");
802   FS.addFileNoOwn("/a", 0, Buf.get());
803   auto Stat = FS.status("/a");
804   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
805   ASSERT_EQ("/a", Stat->getName());
806 }
807 
808 TEST_F(InMemoryFileSystemTest, OpenFileForRead) {
809   FS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a"));
810   FS.addFile("././c", 0, MemoryBuffer::getMemBuffer("c"));
811   FS.addFile("./d/../d", 0, MemoryBuffer::getMemBuffer("d"));
812   NormalizedFS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a"));
813   NormalizedFS.addFile("././c", 0, MemoryBuffer::getMemBuffer("c"));
814   NormalizedFS.addFile("./d/../d", 0, MemoryBuffer::getMemBuffer("d"));
815   auto File = FS.openFileForRead("/a");
816   ASSERT_EQ("a", (*(*File)->getBuffer("ignored"))->getBuffer());
817   File = FS.openFileForRead("/a"); // Open again.
818   ASSERT_EQ("a", (*(*File)->getBuffer("ignored"))->getBuffer());
819   File = NormalizedFS.openFileForRead("/././a"); // Open again.
820   ASSERT_EQ("a", (*(*File)->getBuffer("ignored"))->getBuffer());
821   File = FS.openFileForRead("/");
822   ASSERT_EQ(File.getError(), errc::invalid_argument) << FS.toString();
823   File = FS.openFileForRead("/b");
824   ASSERT_EQ(File.getError(), errc::no_such_file_or_directory) << FS.toString();
825   File = FS.openFileForRead("./c");
826   ASSERT_FALSE(File);
827   File = FS.openFileForRead("e/../d");
828   ASSERT_FALSE(File);
829   File = NormalizedFS.openFileForRead("./c");
830   ASSERT_EQ("c", (*(*File)->getBuffer("ignored"))->getBuffer());
831   File = NormalizedFS.openFileForRead("e/../d");
832   ASSERT_EQ("d", (*(*File)->getBuffer("ignored"))->getBuffer());
833 }
834 
835 TEST_F(InMemoryFileSystemTest, DuplicatedFile) {
836   ASSERT_TRUE(FS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a")));
837   ASSERT_FALSE(FS.addFile("/a/b", 0, MemoryBuffer::getMemBuffer("a")));
838   ASSERT_TRUE(FS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a")));
839   ASSERT_FALSE(FS.addFile("/a", 0, MemoryBuffer::getMemBuffer("b")));
840 }
841 
842 TEST_F(InMemoryFileSystemTest, DirectoryIteration) {
843   FS.addFile("/a", 0, MemoryBuffer::getMemBuffer(""));
844   FS.addFile("/b/c", 0, MemoryBuffer::getMemBuffer(""));
845 
846   std::error_code EC;
847   vfs::directory_iterator I = FS.dir_begin("/", EC);
848   ASSERT_FALSE(EC);
849   ASSERT_EQ("/a", I->path());
850   I.increment(EC);
851   ASSERT_FALSE(EC);
852   ASSERT_EQ("/b", I->path());
853   I.increment(EC);
854   ASSERT_FALSE(EC);
855   ASSERT_EQ(vfs::directory_iterator(), I);
856 
857   I = FS.dir_begin("/b", EC);
858   ASSERT_FALSE(EC);
859   // When on Windows, we end up with "/b\\c" as the name.  Convert to Posix
860   // path for the sake of the comparison.
861   ASSERT_EQ("/b/c", getPosixPath(I->path()));
862   I.increment(EC);
863   ASSERT_FALSE(EC);
864   ASSERT_EQ(vfs::directory_iterator(), I);
865 }
866 
867 TEST_F(InMemoryFileSystemTest, WorkingDirectory) {
868   FS.setCurrentWorkingDirectory("/b");
869   FS.addFile("c", 0, MemoryBuffer::getMemBuffer(""));
870 
871   auto Stat = FS.status("/b/c");
872   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
873   ASSERT_EQ("/b/c", Stat->getName());
874   ASSERT_EQ("/b", *FS.getCurrentWorkingDirectory());
875 
876   Stat = FS.status("c");
877   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
878 
879   NormalizedFS.setCurrentWorkingDirectory("/b/c");
880   NormalizedFS.setCurrentWorkingDirectory(".");
881   ASSERT_EQ("/b/c",
882             getPosixPath(NormalizedFS.getCurrentWorkingDirectory().get()));
883   NormalizedFS.setCurrentWorkingDirectory("..");
884   ASSERT_EQ("/b",
885             getPosixPath(NormalizedFS.getCurrentWorkingDirectory().get()));
886 }
887 
888 TEST_F(InMemoryFileSystemTest, IsLocal) {
889   FS.setCurrentWorkingDirectory("/b");
890   FS.addFile("c", 0, MemoryBuffer::getMemBuffer(""));
891 
892   std::error_code EC;
893   bool IsLocal = true;
894   EC = FS.isLocal("c", IsLocal);
895   ASSERT_FALSE(EC);
896   ASSERT_FALSE(IsLocal);
897 }
898 
899 #if !defined(_WIN32)
900 TEST_F(InMemoryFileSystemTest, GetRealPath) {
901   SmallString<16> Path;
902   EXPECT_EQ(FS.getRealPath("b", Path), errc::operation_not_permitted);
903 
904   auto GetRealPath = [this](StringRef P) {
905     SmallString<16> Output;
906     auto EC = FS.getRealPath(P, Output);
907     EXPECT_FALSE(EC);
908     return Output.str().str();
909   };
910 
911   FS.setCurrentWorkingDirectory("a");
912   EXPECT_EQ(GetRealPath("b"), "a/b");
913   EXPECT_EQ(GetRealPath("../b"), "b");
914   EXPECT_EQ(GetRealPath("b/./c"), "a/b/c");
915 
916   FS.setCurrentWorkingDirectory("/a");
917   EXPECT_EQ(GetRealPath("b"), "/a/b");
918   EXPECT_EQ(GetRealPath("../b"), "/b");
919   EXPECT_EQ(GetRealPath("b/./c"), "/a/b/c");
920 }
921 #endif // _WIN32
922 
923 TEST_F(InMemoryFileSystemTest, AddFileWithUser) {
924   FS.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"), 0xFEEDFACE);
925   auto Stat = FS.status("/a");
926   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
927   ASSERT_TRUE(Stat->isDirectory());
928   ASSERT_EQ(0xFEEDFACE, Stat->getUser());
929   Stat = FS.status("/a/b");
930   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
931   ASSERT_TRUE(Stat->isDirectory());
932   ASSERT_EQ(0xFEEDFACE, Stat->getUser());
933   Stat = FS.status("/a/b/c");
934   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
935   ASSERT_TRUE(Stat->isRegularFile());
936   ASSERT_EQ(sys::fs::perms::all_all, Stat->getPermissions());
937   ASSERT_EQ(0xFEEDFACE, Stat->getUser());
938 }
939 
940 TEST_F(InMemoryFileSystemTest, AddFileWithGroup) {
941   FS.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"), None, 0xDABBAD00);
942   auto Stat = FS.status("/a");
943   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
944   ASSERT_TRUE(Stat->isDirectory());
945   ASSERT_EQ(0xDABBAD00, Stat->getGroup());
946   Stat = FS.status("/a/b");
947   ASSERT_TRUE(Stat->isDirectory());
948   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
949   ASSERT_EQ(0xDABBAD00, Stat->getGroup());
950   Stat = FS.status("/a/b/c");
951   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
952   ASSERT_TRUE(Stat->isRegularFile());
953   ASSERT_EQ(sys::fs::perms::all_all, Stat->getPermissions());
954   ASSERT_EQ(0xDABBAD00, Stat->getGroup());
955 }
956 
957 TEST_F(InMemoryFileSystemTest, AddFileWithFileType) {
958   FS.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"), None, None,
959              sys::fs::file_type::socket_file);
960   auto Stat = FS.status("/a");
961   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
962   ASSERT_TRUE(Stat->isDirectory());
963   Stat = FS.status("/a/b");
964   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
965   ASSERT_TRUE(Stat->isDirectory());
966   Stat = FS.status("/a/b/c");
967   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
968   ASSERT_EQ(sys::fs::file_type::socket_file, Stat->getType());
969   ASSERT_EQ(sys::fs::perms::all_all, Stat->getPermissions());
970 }
971 
972 TEST_F(InMemoryFileSystemTest, AddFileWithPerms) {
973   FS.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"), None, None, None,
974              sys::fs::perms::owner_read | sys::fs::perms::owner_write);
975   auto Stat = FS.status("/a");
976   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
977   ASSERT_TRUE(Stat->isDirectory());
978   ASSERT_EQ(sys::fs::perms::owner_read | sys::fs::perms::owner_write |
979                 sys::fs::perms::owner_exe,
980             Stat->getPermissions());
981   Stat = FS.status("/a/b");
982   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
983   ASSERT_TRUE(Stat->isDirectory());
984   ASSERT_EQ(sys::fs::perms::owner_read | sys::fs::perms::owner_write |
985                 sys::fs::perms::owner_exe,
986             Stat->getPermissions());
987   Stat = FS.status("/a/b/c");
988   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
989   ASSERT_TRUE(Stat->isRegularFile());
990   ASSERT_EQ(sys::fs::perms::owner_read | sys::fs::perms::owner_write,
991             Stat->getPermissions());
992 }
993 
994 TEST_F(InMemoryFileSystemTest, AddDirectoryThenAddChild) {
995   FS.addFile("/a", 0, MemoryBuffer::getMemBuffer(""), /*User=*/None,
996              /*Group=*/None, sys::fs::file_type::directory_file);
997   FS.addFile("/a/b", 0, MemoryBuffer::getMemBuffer("abc"), /*User=*/None,
998              /*Group=*/None, sys::fs::file_type::regular_file);
999   auto Stat = FS.status("/a");
1000   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
1001   ASSERT_TRUE(Stat->isDirectory());
1002   Stat = FS.status("/a/b");
1003   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
1004   ASSERT_TRUE(Stat->isRegularFile());
1005 }
1006 
1007 // Test that the name returned by status() is in the same form as the path that
1008 // was requested (to match the behavior of RealFileSystem).
1009 TEST_F(InMemoryFileSystemTest, StatusName) {
1010   NormalizedFS.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"),
1011                        /*User=*/None,
1012                        /*Group=*/None, sys::fs::file_type::regular_file);
1013   NormalizedFS.setCurrentWorkingDirectory("/a/b");
1014 
1015   // Access using InMemoryFileSystem::status.
1016   auto Stat = NormalizedFS.status("../b/c");
1017   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n"
1018                                 << NormalizedFS.toString();
1019   ASSERT_TRUE(Stat->isRegularFile());
1020   ASSERT_EQ("../b/c", Stat->getName());
1021 
1022   // Access using InMemoryFileAdaptor::status.
1023   auto File = NormalizedFS.openFileForRead("../b/c");
1024   ASSERT_FALSE(File.getError()) << File.getError() << "\n"
1025                                 << NormalizedFS.toString();
1026   Stat = (*File)->status();
1027   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n"
1028                                 << NormalizedFS.toString();
1029   ASSERT_TRUE(Stat->isRegularFile());
1030   ASSERT_EQ("../b/c", Stat->getName());
1031 
1032   // Access using a directory iterator.
1033   std::error_code EC;
1034   llvm::vfs::directory_iterator It = NormalizedFS.dir_begin("../b", EC);
1035   // When on Windows, we end up with "../b\\c" as the name.  Convert to Posix
1036   // path for the sake of the comparison.
1037   ASSERT_EQ("../b/c", getPosixPath(It->path()));
1038 }
1039 
1040 TEST_F(InMemoryFileSystemTest, AddHardLinkToFile) {
1041   StringRef FromLink = "/path/to/FROM/link";
1042   StringRef Target = "/path/to/TO/file";
1043   FS.addFile(Target, 0, MemoryBuffer::getMemBuffer("content of target"));
1044   EXPECT_TRUE(FS.addHardLink(FromLink, Target));
1045   EXPECT_THAT(FromLink, IsHardLinkTo(&FS, Target));
1046   EXPECT_TRUE(FS.status(FromLink)->getSize() == FS.status(Target)->getSize());
1047   EXPECT_TRUE(FS.getBufferForFile(FromLink)->get()->getBuffer() ==
1048               FS.getBufferForFile(Target)->get()->getBuffer());
1049 }
1050 
1051 TEST_F(InMemoryFileSystemTest, AddHardLinkInChainPattern) {
1052   StringRef Link0 = "/path/to/0/link";
1053   StringRef Link1 = "/path/to/1/link";
1054   StringRef Link2 = "/path/to/2/link";
1055   StringRef Target = "/path/to/target";
1056   FS.addFile(Target, 0, MemoryBuffer::getMemBuffer("content of target file"));
1057   EXPECT_TRUE(FS.addHardLink(Link2, Target));
1058   EXPECT_TRUE(FS.addHardLink(Link1, Link2));
1059   EXPECT_TRUE(FS.addHardLink(Link0, Link1));
1060   EXPECT_THAT(Link0, IsHardLinkTo(&FS, Target));
1061   EXPECT_THAT(Link1, IsHardLinkTo(&FS, Target));
1062   EXPECT_THAT(Link2, IsHardLinkTo(&FS, Target));
1063 }
1064 
1065 TEST_F(InMemoryFileSystemTest, AddHardLinkToAFileThatWasNotAddedBefore) {
1066   EXPECT_FALSE(FS.addHardLink("/path/to/link", "/path/to/target"));
1067 }
1068 
1069 TEST_F(InMemoryFileSystemTest, AddHardLinkFromAFileThatWasAddedBefore) {
1070   StringRef Link = "/path/to/link";
1071   StringRef Target = "/path/to/target";
1072   FS.addFile(Target, 0, MemoryBuffer::getMemBuffer("content of target"));
1073   FS.addFile(Link, 0, MemoryBuffer::getMemBuffer("content of link"));
1074   EXPECT_FALSE(FS.addHardLink(Link, Target));
1075 }
1076 
1077 TEST_F(InMemoryFileSystemTest, AddSameHardLinkMoreThanOnce) {
1078   StringRef Link = "/path/to/link";
1079   StringRef Target = "/path/to/target";
1080   FS.addFile(Target, 0, MemoryBuffer::getMemBuffer("content of target"));
1081   EXPECT_TRUE(FS.addHardLink(Link, Target));
1082   EXPECT_FALSE(FS.addHardLink(Link, Target));
1083 }
1084 
1085 TEST_F(InMemoryFileSystemTest, AddFileInPlaceOfAHardLinkWithSameContent) {
1086   StringRef Link = "/path/to/link";
1087   StringRef Target = "/path/to/target";
1088   StringRef Content = "content of target";
1089   EXPECT_TRUE(FS.addFile(Target, 0, MemoryBuffer::getMemBuffer(Content)));
1090   EXPECT_TRUE(FS.addHardLink(Link, Target));
1091   EXPECT_TRUE(FS.addFile(Link, 0, MemoryBuffer::getMemBuffer(Content)));
1092 }
1093 
1094 TEST_F(InMemoryFileSystemTest, AddFileInPlaceOfAHardLinkWithDifferentContent) {
1095   StringRef Link = "/path/to/link";
1096   StringRef Target = "/path/to/target";
1097   StringRef Content = "content of target";
1098   StringRef LinkContent = "different content of link";
1099   EXPECT_TRUE(FS.addFile(Target, 0, MemoryBuffer::getMemBuffer(Content)));
1100   EXPECT_TRUE(FS.addHardLink(Link, Target));
1101   EXPECT_FALSE(FS.addFile(Link, 0, MemoryBuffer::getMemBuffer(LinkContent)));
1102 }
1103 
1104 TEST_F(InMemoryFileSystemTest, AddHardLinkToADirectory) {
1105   StringRef Dir = "path/to/dummy/dir";
1106   StringRef Link = "/path/to/link";
1107   StringRef File = "path/to/dummy/dir/target";
1108   StringRef Content = "content of target";
1109   EXPECT_TRUE(FS.addFile(File, 0, MemoryBuffer::getMemBuffer(Content)));
1110   EXPECT_FALSE(FS.addHardLink(Link, Dir));
1111 }
1112 
1113 TEST_F(InMemoryFileSystemTest, AddHardLinkFromADirectory) {
1114   StringRef Dir = "path/to/dummy/dir";
1115   StringRef Target = "path/to/dummy/dir/target";
1116   StringRef Content = "content of target";
1117   EXPECT_TRUE(FS.addFile(Target, 0, MemoryBuffer::getMemBuffer(Content)));
1118   EXPECT_FALSE(FS.addHardLink(Dir, Target));
1119 }
1120 
1121 TEST_F(InMemoryFileSystemTest, AddHardLinkUnderAFile) {
1122   StringRef CommonContent = "content string";
1123   FS.addFile("/a/b", 0, MemoryBuffer::getMemBuffer(CommonContent));
1124   FS.addFile("/c/d", 0, MemoryBuffer::getMemBuffer(CommonContent));
1125   EXPECT_FALSE(FS.addHardLink("/c/d/e", "/a/b"));
1126 }
1127 
1128 TEST_F(InMemoryFileSystemTest, RecursiveIterationWithHardLink) {
1129   std::error_code EC;
1130   FS.addFile("/a/b", 0, MemoryBuffer::getMemBuffer("content string"));
1131   EXPECT_TRUE(FS.addHardLink("/c/d", "/a/b"));
1132   auto I = vfs::recursive_directory_iterator(FS, "/", EC);
1133   ASSERT_FALSE(EC);
1134   std::vector<std::string> Nodes;
1135   for (auto E = vfs::recursive_directory_iterator(); !EC && I != E;
1136        I.increment(EC)) {
1137     Nodes.push_back(getPosixPath(I->path()));
1138   }
1139   EXPECT_THAT(Nodes, testing::UnorderedElementsAre("/a", "/a/b", "/c", "/c/d"));
1140 }
1141 
1142 // NOTE: in the tests below, we use '//root/' as our root directory, since it is
1143 // a legal *absolute* path on Windows as well as *nix.
1144 class VFSFromYAMLTest : public ::testing::Test {
1145 public:
1146   int NumDiagnostics;
1147 
1148   void SetUp() override { NumDiagnostics = 0; }
1149 
1150   static void CountingDiagHandler(const SMDiagnostic &, void *Context) {
1151     VFSFromYAMLTest *Test = static_cast<VFSFromYAMLTest *>(Context);
1152     ++Test->NumDiagnostics;
1153   }
1154 
1155   IntrusiveRefCntPtr<vfs::FileSystem>
1156   getFromYAMLRawString(StringRef Content,
1157                        IntrusiveRefCntPtr<vfs::FileSystem> ExternalFS) {
1158     std::unique_ptr<MemoryBuffer> Buffer = MemoryBuffer::getMemBuffer(Content);
1159     return getVFSFromYAML(std::move(Buffer), CountingDiagHandler, "", this,
1160                           ExternalFS);
1161   }
1162 
1163   IntrusiveRefCntPtr<vfs::FileSystem> getFromYAMLString(
1164       StringRef Content,
1165       IntrusiveRefCntPtr<vfs::FileSystem> ExternalFS = new DummyFileSystem()) {
1166     std::string VersionPlusContent("{\n  'version':0,\n");
1167     VersionPlusContent += Content.slice(Content.find('{') + 1, StringRef::npos);
1168     return getFromYAMLRawString(VersionPlusContent, ExternalFS);
1169   }
1170 
1171   // This is intended as a "XFAIL" for windows hosts.
1172   bool supportsSameDirMultipleYAMLEntries() {
1173     Triple Host(Triple::normalize(sys::getProcessTriple()));
1174     return !Host.isOSWindows();
1175   }
1176 };
1177 
1178 TEST_F(VFSFromYAMLTest, BasicVFSFromYAML) {
1179   IntrusiveRefCntPtr<vfs::FileSystem> FS;
1180   FS = getFromYAMLString("");
1181   EXPECT_EQ(nullptr, FS.get());
1182   FS = getFromYAMLString("[]");
1183   EXPECT_EQ(nullptr, FS.get());
1184   FS = getFromYAMLString("'string'");
1185   EXPECT_EQ(nullptr, FS.get());
1186   EXPECT_EQ(3, NumDiagnostics);
1187 }
1188 
1189 TEST_F(VFSFromYAMLTest, MappedFiles) {
1190   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
1191   Lower->addRegularFile("//root/foo/bar/a");
1192   IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
1193       "{ 'roots': [\n"
1194       "{\n"
1195       "  'type': 'directory',\n"
1196       "  'name': '//root/',\n"
1197       "  'contents': [ {\n"
1198       "                  'type': 'file',\n"
1199       "                  'name': 'file1',\n"
1200       "                  'external-contents': '//root/foo/bar/a'\n"
1201       "                },\n"
1202       "                {\n"
1203       "                  'type': 'file',\n"
1204       "                  'name': 'file2',\n"
1205       "                  'external-contents': '//root/foo/b'\n"
1206       "                }\n"
1207       "              ]\n"
1208       "}\n"
1209       "]\n"
1210       "}",
1211       Lower);
1212   ASSERT_TRUE(FS.get() != nullptr);
1213 
1214   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
1215       new vfs::OverlayFileSystem(Lower));
1216   O->pushOverlay(FS);
1217 
1218   // file
1219   ErrorOr<vfs::Status> S = O->status("//root/file1");
1220   ASSERT_FALSE(S.getError());
1221   EXPECT_EQ("//root/foo/bar/a", S->getName());
1222   EXPECT_TRUE(S->IsVFSMapped);
1223 
1224   ErrorOr<vfs::Status> SLower = O->status("//root/foo/bar/a");
1225   EXPECT_EQ("//root/foo/bar/a", SLower->getName());
1226   EXPECT_TRUE(S->equivalent(*SLower));
1227   EXPECT_FALSE(SLower->IsVFSMapped);
1228 
1229   // file after opening
1230   auto OpenedF = O->openFileForRead("//root/file1");
1231   ASSERT_FALSE(OpenedF.getError());
1232   auto OpenedS = (*OpenedF)->status();
1233   ASSERT_FALSE(OpenedS.getError());
1234   EXPECT_EQ("//root/foo/bar/a", OpenedS->getName());
1235   EXPECT_TRUE(OpenedS->IsVFSMapped);
1236 
1237   // directory
1238   S = O->status("//root/");
1239   ASSERT_FALSE(S.getError());
1240   EXPECT_TRUE(S->isDirectory());
1241   EXPECT_TRUE(S->equivalent(*O->status("//root/"))); // non-volatile UniqueID
1242 
1243   // broken mapping
1244   EXPECT_EQ(O->status("//root/file2").getError(),
1245             llvm::errc::no_such_file_or_directory);
1246   EXPECT_EQ(0, NumDiagnostics);
1247 }
1248 
1249 TEST_F(VFSFromYAMLTest, CaseInsensitive) {
1250   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
1251   Lower->addRegularFile("//root/foo/bar/a");
1252   IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
1253       "{ 'case-sensitive': 'false',\n"
1254       "  'roots': [\n"
1255       "{\n"
1256       "  'type': 'directory',\n"
1257       "  'name': '//root/',\n"
1258       "  'contents': [ {\n"
1259       "                  'type': 'file',\n"
1260       "                  'name': 'XX',\n"
1261       "                  'external-contents': '//root/foo/bar/a'\n"
1262       "                }\n"
1263       "              ]\n"
1264       "}]}",
1265       Lower);
1266   ASSERT_TRUE(FS.get() != nullptr);
1267 
1268   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
1269       new vfs::OverlayFileSystem(Lower));
1270   O->pushOverlay(FS);
1271 
1272   ErrorOr<vfs::Status> S = O->status("//root/XX");
1273   ASSERT_FALSE(S.getError());
1274 
1275   ErrorOr<vfs::Status> SS = O->status("//root/xx");
1276   ASSERT_FALSE(SS.getError());
1277   EXPECT_TRUE(S->equivalent(*SS));
1278   SS = O->status("//root/xX");
1279   EXPECT_TRUE(S->equivalent(*SS));
1280   SS = O->status("//root/Xx");
1281   EXPECT_TRUE(S->equivalent(*SS));
1282   EXPECT_EQ(0, NumDiagnostics);
1283 }
1284 
1285 TEST_F(VFSFromYAMLTest, CaseSensitive) {
1286   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
1287   Lower->addRegularFile("//root/foo/bar/a");
1288   IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
1289       "{ 'case-sensitive': 'true',\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> SS = O->status("//root/xx");
1309   EXPECT_EQ(SS.getError(), llvm::errc::no_such_file_or_directory);
1310   SS = O->status("//root/xX");
1311   EXPECT_EQ(SS.getError(), llvm::errc::no_such_file_or_directory);
1312   SS = O->status("//root/Xx");
1313   EXPECT_EQ(SS.getError(), llvm::errc::no_such_file_or_directory);
1314   EXPECT_EQ(0, NumDiagnostics);
1315 }
1316 
1317 TEST_F(VFSFromYAMLTest, IllegalVFSFile) {
1318   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
1319 
1320   // invalid YAML at top-level
1321   IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString("{]", Lower);
1322   EXPECT_EQ(nullptr, FS.get());
1323   // invalid YAML in roots
1324   FS = getFromYAMLString("{ 'roots':[}", Lower);
1325   // invalid YAML in directory
1326   FS = getFromYAMLString(
1327       "{ 'roots':[ { 'name': 'foo', 'type': 'directory', 'contents': [}",
1328       Lower);
1329   EXPECT_EQ(nullptr, FS.get());
1330 
1331   // invalid configuration
1332   FS = getFromYAMLString("{ 'knobular': 'true', 'roots':[] }", Lower);
1333   EXPECT_EQ(nullptr, FS.get());
1334   FS = getFromYAMLString("{ 'case-sensitive': 'maybe', 'roots':[] }", Lower);
1335   EXPECT_EQ(nullptr, FS.get());
1336 
1337   // invalid roots
1338   FS = getFromYAMLString("{ 'roots':'' }", Lower);
1339   EXPECT_EQ(nullptr, FS.get());
1340   FS = getFromYAMLString("{ 'roots':{} }", Lower);
1341   EXPECT_EQ(nullptr, FS.get());
1342 
1343   // invalid entries
1344   FS = getFromYAMLString(
1345       "{ 'roots':[ { 'type': 'other', 'name': 'me', 'contents': '' }", Lower);
1346   EXPECT_EQ(nullptr, FS.get());
1347   FS = getFromYAMLString("{ 'roots':[ { 'type': 'file', 'name': [], "
1348                          "'external-contents': 'other' }",
1349                          Lower);
1350   EXPECT_EQ(nullptr, FS.get());
1351   FS = getFromYAMLString(
1352       "{ 'roots':[ { 'type': 'file', 'name': 'me', 'external-contents': [] }",
1353       Lower);
1354   EXPECT_EQ(nullptr, FS.get());
1355   FS = getFromYAMLString(
1356       "{ 'roots':[ { 'type': 'file', 'name': 'me', 'external-contents': {} }",
1357       Lower);
1358   EXPECT_EQ(nullptr, FS.get());
1359   FS = getFromYAMLString(
1360       "{ 'roots':[ { 'type': 'directory', 'name': 'me', 'contents': {} }",
1361       Lower);
1362   EXPECT_EQ(nullptr, FS.get());
1363   FS = getFromYAMLString(
1364       "{ 'roots':[ { 'type': 'directory', 'name': 'me', 'contents': '' }",
1365       Lower);
1366   EXPECT_EQ(nullptr, FS.get());
1367   FS = getFromYAMLString(
1368       "{ 'roots':[ { 'thingy': 'directory', 'name': 'me', 'contents': [] }",
1369       Lower);
1370   EXPECT_EQ(nullptr, FS.get());
1371 
1372   // missing mandatory fields
1373   FS = getFromYAMLString("{ 'roots':[ { 'type': 'file', 'name': 'me' }", Lower);
1374   EXPECT_EQ(nullptr, FS.get());
1375   FS = getFromYAMLString(
1376       "{ 'roots':[ { 'type': 'file', 'external-contents': 'other' }", Lower);
1377   EXPECT_EQ(nullptr, FS.get());
1378   FS = getFromYAMLString("{ 'roots':[ { 'name': 'me', 'contents': [] }", Lower);
1379   EXPECT_EQ(nullptr, FS.get());
1380 
1381   // duplicate keys
1382   FS = getFromYAMLString("{ 'roots':[], 'roots':[] }", Lower);
1383   EXPECT_EQ(nullptr, FS.get());
1384   FS = getFromYAMLString(
1385       "{ 'case-sensitive':'true', 'case-sensitive':'true', 'roots':[] }",
1386       Lower);
1387   EXPECT_EQ(nullptr, FS.get());
1388   FS =
1389       getFromYAMLString("{ 'roots':[{'name':'me', 'name':'you', 'type':'file', "
1390                         "'external-contents':'blah' } ] }",
1391                         Lower);
1392   EXPECT_EQ(nullptr, FS.get());
1393 
1394   // missing version
1395   FS = getFromYAMLRawString("{ 'roots':[] }", Lower);
1396   EXPECT_EQ(nullptr, FS.get());
1397 
1398   // bad version number
1399   FS = getFromYAMLRawString("{ 'version':'foo', 'roots':[] }", Lower);
1400   EXPECT_EQ(nullptr, FS.get());
1401   FS = getFromYAMLRawString("{ 'version':-1, 'roots':[] }", Lower);
1402   EXPECT_EQ(nullptr, FS.get());
1403   FS = getFromYAMLRawString("{ 'version':100000, 'roots':[] }", Lower);
1404   EXPECT_EQ(nullptr, FS.get());
1405   EXPECT_EQ(24, NumDiagnostics);
1406 }
1407 
1408 TEST_F(VFSFromYAMLTest, UseExternalName) {
1409   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
1410   Lower->addRegularFile("//root/external/file");
1411 
1412   IntrusiveRefCntPtr<vfs::FileSystem> FS =
1413       getFromYAMLString("{ 'roots': [\n"
1414                         "  { 'type': 'file', 'name': '//root/A',\n"
1415                         "    'external-contents': '//root/external/file'\n"
1416                         "  },\n"
1417                         "  { 'type': 'file', 'name': '//root/B',\n"
1418                         "    'use-external-name': true,\n"
1419                         "    'external-contents': '//root/external/file'\n"
1420                         "  },\n"
1421                         "  { 'type': 'file', 'name': '//root/C',\n"
1422                         "    'use-external-name': false,\n"
1423                         "    'external-contents': '//root/external/file'\n"
1424                         "  }\n"
1425                         "] }",
1426                         Lower);
1427   ASSERT_TRUE(nullptr != FS.get());
1428 
1429   // default true
1430   EXPECT_EQ("//root/external/file", FS->status("//root/A")->getName());
1431   // explicit
1432   EXPECT_EQ("//root/external/file", FS->status("//root/B")->getName());
1433   EXPECT_EQ("//root/C", FS->status("//root/C")->getName());
1434 
1435   // global configuration
1436   FS = getFromYAMLString("{ 'use-external-names': false,\n"
1437                          "  'roots': [\n"
1438                          "  { 'type': 'file', 'name': '//root/A',\n"
1439                          "    'external-contents': '//root/external/file'\n"
1440                          "  },\n"
1441                          "  { 'type': 'file', 'name': '//root/B',\n"
1442                          "    'use-external-name': true,\n"
1443                          "    'external-contents': '//root/external/file'\n"
1444                          "  },\n"
1445                          "  { 'type': 'file', 'name': '//root/C',\n"
1446                          "    'use-external-name': false,\n"
1447                          "    'external-contents': '//root/external/file'\n"
1448                          "  }\n"
1449                          "] }",
1450                          Lower);
1451   ASSERT_TRUE(nullptr != FS.get());
1452 
1453   // default
1454   EXPECT_EQ("//root/A", FS->status("//root/A")->getName());
1455   // explicit
1456   EXPECT_EQ("//root/external/file", FS->status("//root/B")->getName());
1457   EXPECT_EQ("//root/C", FS->status("//root/C")->getName());
1458 }
1459 
1460 TEST_F(VFSFromYAMLTest, MultiComponentPath) {
1461   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
1462   Lower->addRegularFile("//root/other");
1463 
1464   // file in roots
1465   IntrusiveRefCntPtr<vfs::FileSystem> FS =
1466       getFromYAMLString("{ 'roots': [\n"
1467                         "  { 'type': 'file', 'name': '//root/path/to/file',\n"
1468                         "    'external-contents': '//root/other' }]\n"
1469                         "}",
1470                         Lower);
1471   ASSERT_TRUE(nullptr != FS.get());
1472   EXPECT_FALSE(FS->status("//root/path/to/file").getError());
1473   EXPECT_FALSE(FS->status("//root/path/to").getError());
1474   EXPECT_FALSE(FS->status("//root/path").getError());
1475   EXPECT_FALSE(FS->status("//root/").getError());
1476 
1477   // at the start
1478   FS = getFromYAMLString(
1479       "{ 'roots': [\n"
1480       "  { 'type': 'directory', 'name': '//root/path/to',\n"
1481       "    'contents': [ { 'type': 'file', 'name': 'file',\n"
1482       "                    'external-contents': '//root/other' }]}]\n"
1483       "}",
1484       Lower);
1485   ASSERT_TRUE(nullptr != FS.get());
1486   EXPECT_FALSE(FS->status("//root/path/to/file").getError());
1487   EXPECT_FALSE(FS->status("//root/path/to").getError());
1488   EXPECT_FALSE(FS->status("//root/path").getError());
1489   EXPECT_FALSE(FS->status("//root/").getError());
1490 
1491   // at the end
1492   FS = getFromYAMLString(
1493       "{ 'roots': [\n"
1494       "  { 'type': 'directory', 'name': '//root/',\n"
1495       "    'contents': [ { 'type': 'file', 'name': 'path/to/file',\n"
1496       "                    'external-contents': '//root/other' }]}]\n"
1497       "}",
1498       Lower);
1499   ASSERT_TRUE(nullptr != FS.get());
1500   EXPECT_FALSE(FS->status("//root/path/to/file").getError());
1501   EXPECT_FALSE(FS->status("//root/path/to").getError());
1502   EXPECT_FALSE(FS->status("//root/path").getError());
1503   EXPECT_FALSE(FS->status("//root/").getError());
1504 }
1505 
1506 TEST_F(VFSFromYAMLTest, TrailingSlashes) {
1507   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
1508   Lower->addRegularFile("//root/other");
1509 
1510   // file in roots
1511   IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
1512       "{ 'roots': [\n"
1513       "  { 'type': 'directory', 'name': '//root/path/to////',\n"
1514       "    'contents': [ { 'type': 'file', 'name': 'file',\n"
1515       "                    'external-contents': '//root/other' }]}]\n"
1516       "}",
1517       Lower);
1518   ASSERT_TRUE(nullptr != FS.get());
1519   EXPECT_FALSE(FS->status("//root/path/to/file").getError());
1520   EXPECT_FALSE(FS->status("//root/path/to").getError());
1521   EXPECT_FALSE(FS->status("//root/path").getError());
1522   EXPECT_FALSE(FS->status("//root/").getError());
1523 }
1524 
1525 TEST_F(VFSFromYAMLTest, DirectoryIteration) {
1526   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
1527   Lower->addDirectory("//root/");
1528   Lower->addDirectory("//root/foo");
1529   Lower->addDirectory("//root/foo/bar");
1530   Lower->addRegularFile("//root/foo/bar/a");
1531   Lower->addRegularFile("//root/foo/bar/b");
1532   Lower->addRegularFile("//root/file3");
1533   IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
1534       "{ 'use-external-names': false,\n"
1535       "  'roots': [\n"
1536       "{\n"
1537       "  'type': 'directory',\n"
1538       "  'name': '//root/',\n"
1539       "  'contents': [ {\n"
1540       "                  'type': 'file',\n"
1541       "                  'name': 'file1',\n"
1542       "                  'external-contents': '//root/foo/bar/a'\n"
1543       "                },\n"
1544       "                {\n"
1545       "                  'type': 'file',\n"
1546       "                  'name': 'file2',\n"
1547       "                  'external-contents': '//root/foo/bar/b'\n"
1548       "                }\n"
1549       "              ]\n"
1550       "}\n"
1551       "]\n"
1552       "}",
1553       Lower);
1554   ASSERT_TRUE(FS.get() != nullptr);
1555 
1556   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
1557       new vfs::OverlayFileSystem(Lower));
1558   O->pushOverlay(FS);
1559 
1560   std::error_code EC;
1561   checkContents(O->dir_begin("//root/", EC),
1562                 {"//root/file1", "//root/file2", "//root/file3", "//root/foo"});
1563 
1564   checkContents(O->dir_begin("//root/foo/bar", EC),
1565                 {"//root/foo/bar/a", "//root/foo/bar/b"});
1566 }
1567 
1568 TEST_F(VFSFromYAMLTest, DirectoryIterationSameDirMultipleEntries) {
1569   // https://llvm.org/bugs/show_bug.cgi?id=27725
1570   if (!supportsSameDirMultipleYAMLEntries())
1571     return;
1572 
1573   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
1574   Lower->addDirectory("//root/zab");
1575   Lower->addDirectory("//root/baz");
1576   Lower->addRegularFile("//root/zab/a");
1577   Lower->addRegularFile("//root/zab/b");
1578   IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
1579       "{ 'use-external-names': false,\n"
1580       "  'roots': [\n"
1581       "{\n"
1582       "  'type': 'directory',\n"
1583       "  'name': '//root/baz/',\n"
1584       "  'contents': [ {\n"
1585       "                  'type': 'file',\n"
1586       "                  'name': 'x',\n"
1587       "                  'external-contents': '//root/zab/a'\n"
1588       "                }\n"
1589       "              ]\n"
1590       "},\n"
1591       "{\n"
1592       "  'type': 'directory',\n"
1593       "  'name': '//root/baz/',\n"
1594       "  'contents': [ {\n"
1595       "                  'type': 'file',\n"
1596       "                  'name': 'y',\n"
1597       "                  'external-contents': '//root/zab/b'\n"
1598       "                }\n"
1599       "              ]\n"
1600       "}\n"
1601       "]\n"
1602       "}",
1603       Lower);
1604   ASSERT_TRUE(FS.get() != nullptr);
1605 
1606   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
1607       new vfs::OverlayFileSystem(Lower));
1608   O->pushOverlay(FS);
1609 
1610   std::error_code EC;
1611 
1612   checkContents(O->dir_begin("//root/baz/", EC),
1613                 {"//root/baz/x", "//root/baz/y"});
1614 }
1615 
1616 TEST_F(VFSFromYAMLTest, RecursiveDirectoryIterationLevel) {
1617 
1618   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
1619   Lower->addDirectory("//root/a");
1620   Lower->addDirectory("//root/a/b");
1621   Lower->addDirectory("//root/a/b/c");
1622   Lower->addRegularFile("//root/a/b/c/file");
1623   IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
1624       "{ 'use-external-names': false,\n"
1625       "  'roots': [\n"
1626       "{\n"
1627       "  'type': 'directory',\n"
1628       "  'name': '//root/a/b/c/',\n"
1629       "  'contents': [ {\n"
1630       "                  'type': 'file',\n"
1631       "                  'name': 'file',\n"
1632       "                  'external-contents': '//root/a/b/c/file'\n"
1633       "                }\n"
1634       "              ]\n"
1635       "},\n"
1636       "]\n"
1637       "}",
1638       Lower);
1639   ASSERT_TRUE(FS.get() != nullptr);
1640 
1641   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
1642       new vfs::OverlayFileSystem(Lower));
1643   O->pushOverlay(FS);
1644 
1645   std::error_code EC;
1646 
1647   // Test recursive_directory_iterator level()
1648   vfs::recursive_directory_iterator I = vfs::recursive_directory_iterator(
1649                                         *O, "//root", EC),
1650                                     E;
1651   ASSERT_FALSE(EC);
1652   for (int l = 0; I != E; I.increment(EC), ++l) {
1653     ASSERT_FALSE(EC);
1654     EXPECT_EQ(I.level(), l);
1655   }
1656   EXPECT_EQ(I, E);
1657 }
1658 
1659 TEST_F(VFSFromYAMLTest, RelativePaths) {
1660   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
1661   // Filename at root level without a parent directory.
1662   IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
1663       "{ 'roots': [\n"
1664       "  { 'type': 'file', 'name': 'file-not-in-directory.h',\n"
1665       "    'external-contents': '//root/external/file'\n"
1666       "  }\n"
1667       "] }",
1668       Lower);
1669   EXPECT_EQ(nullptr, FS.get());
1670 
1671   // Relative file path.
1672   FS = getFromYAMLString("{ 'roots': [\n"
1673                          "  { 'type': 'file', 'name': 'relative/file/path.h',\n"
1674                          "    'external-contents': '//root/external/file'\n"
1675                          "  }\n"
1676                          "] }",
1677                          Lower);
1678   EXPECT_EQ(nullptr, FS.get());
1679 
1680   // Relative directory path.
1681   FS = getFromYAMLString(
1682       "{ 'roots': [\n"
1683       "  { 'type': 'directory', 'name': 'relative/directory/path.h',\n"
1684       "    'contents': []\n"
1685       "  }\n"
1686       "] }",
1687       Lower);
1688   EXPECT_EQ(nullptr, FS.get());
1689 
1690   EXPECT_EQ(3, NumDiagnostics);
1691 }
1692 
1693 TEST_F(VFSFromYAMLTest, NonFallthroughDirectoryIteration) {
1694   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
1695   Lower->addDirectory("//root/");
1696   Lower->addRegularFile("//root/a");
1697   Lower->addRegularFile("//root/b");
1698   IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
1699       "{ 'use-external-names': false,\n"
1700       "  'fallthrough': false,\n"
1701       "  'roots': [\n"
1702       "{\n"
1703       "  'type': 'directory',\n"
1704       "  'name': '//root/',\n"
1705       "  'contents': [ {\n"
1706       "                  'type': 'file',\n"
1707       "                  'name': 'c',\n"
1708       "                  'external-contents': '//root/a'\n"
1709       "                }\n"
1710       "              ]\n"
1711       "}\n"
1712       "]\n"
1713       "}",
1714       Lower);
1715   ASSERT_TRUE(FS.get() != nullptr);
1716 
1717   std::error_code EC;
1718   checkContents(FS->dir_begin("//root/", EC),
1719                 {"//root/c"});
1720 }
1721 
1722 TEST_F(VFSFromYAMLTest, DirectoryIterationWithDuplicates) {
1723   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
1724   Lower->addDirectory("//root/");
1725   Lower->addRegularFile("//root/a");
1726   Lower->addRegularFile("//root/b");
1727   IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
1728       "{ 'use-external-names': false,\n"
1729       "  'roots': [\n"
1730       "{\n"
1731       "  'type': 'directory',\n"
1732       "  'name': '//root/',\n"
1733       "  'contents': [ {\n"
1734       "                  'type': 'file',\n"
1735       "                  'name': 'a',\n"
1736       "                  'external-contents': '//root/a'\n"
1737       "                }\n"
1738       "              ]\n"
1739       "}\n"
1740       "]\n"
1741       "}",
1742 	  Lower);
1743   ASSERT_TRUE(FS.get() != nullptr);
1744 
1745   std::error_code EC;
1746   checkContents(FS->dir_begin("//root/", EC),
1747                 {"//root/a", "//root/b"});
1748 }
1749 
1750 TEST_F(VFSFromYAMLTest, DirectoryIterationErrorInVFSLayer) {
1751   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
1752   Lower->addDirectory("//root/");
1753   Lower->addDirectory("//root/foo");
1754   Lower->addRegularFile("//root/foo/a");
1755   Lower->addRegularFile("//root/foo/b");
1756   IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
1757       "{ 'use-external-names': false,\n"
1758       "  'roots': [\n"
1759       "{\n"
1760       "  'type': 'directory',\n"
1761       "  'name': '//root/',\n"
1762       "  'contents': [ {\n"
1763       "                  'type': 'file',\n"
1764       "                  'name': 'bar/a',\n"
1765       "                  'external-contents': '//root/foo/a'\n"
1766       "                }\n"
1767       "              ]\n"
1768       "}\n"
1769       "]\n"
1770       "}",
1771       Lower);
1772   ASSERT_TRUE(FS.get() != nullptr);
1773 
1774   std::error_code EC;
1775   checkContents(FS->dir_begin("//root/foo", EC),
1776                 {"//root/foo/a", "//root/foo/b"});
1777 }
1778