xref: /llvm-project/llvm/unittests/Support/VirtualFileSystemTest.cpp (revision 41fb951f874977f4c28c27797cd0693ee17c1b15)
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 #if !defined(_WIN32)
889 TEST_F(InMemoryFileSystemTest, GetRealPath) {
890   SmallString<16> Path;
891   EXPECT_EQ(FS.getRealPath("b", Path), errc::operation_not_permitted);
892 
893   auto GetRealPath = [this](StringRef P) {
894     SmallString<16> Output;
895     auto EC = FS.getRealPath(P, Output);
896     EXPECT_FALSE(EC);
897     return Output.str().str();
898   };
899 
900   FS.setCurrentWorkingDirectory("a");
901   EXPECT_EQ(GetRealPath("b"), "a/b");
902   EXPECT_EQ(GetRealPath("../b"), "b");
903   EXPECT_EQ(GetRealPath("b/./c"), "a/b/c");
904 
905   FS.setCurrentWorkingDirectory("/a");
906   EXPECT_EQ(GetRealPath("b"), "/a/b");
907   EXPECT_EQ(GetRealPath("../b"), "/b");
908   EXPECT_EQ(GetRealPath("b/./c"), "/a/b/c");
909 }
910 #endif // _WIN32
911 
912 TEST_F(InMemoryFileSystemTest, AddFileWithUser) {
913   FS.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"), 0xFEEDFACE);
914   auto Stat = FS.status("/a");
915   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
916   ASSERT_TRUE(Stat->isDirectory());
917   ASSERT_EQ(0xFEEDFACE, Stat->getUser());
918   Stat = FS.status("/a/b");
919   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
920   ASSERT_TRUE(Stat->isDirectory());
921   ASSERT_EQ(0xFEEDFACE, Stat->getUser());
922   Stat = FS.status("/a/b/c");
923   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
924   ASSERT_TRUE(Stat->isRegularFile());
925   ASSERT_EQ(sys::fs::perms::all_all, Stat->getPermissions());
926   ASSERT_EQ(0xFEEDFACE, Stat->getUser());
927 }
928 
929 TEST_F(InMemoryFileSystemTest, AddFileWithGroup) {
930   FS.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"), None, 0xDABBAD00);
931   auto Stat = FS.status("/a");
932   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
933   ASSERT_TRUE(Stat->isDirectory());
934   ASSERT_EQ(0xDABBAD00, Stat->getGroup());
935   Stat = FS.status("/a/b");
936   ASSERT_TRUE(Stat->isDirectory());
937   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
938   ASSERT_EQ(0xDABBAD00, Stat->getGroup());
939   Stat = FS.status("/a/b/c");
940   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
941   ASSERT_TRUE(Stat->isRegularFile());
942   ASSERT_EQ(sys::fs::perms::all_all, Stat->getPermissions());
943   ASSERT_EQ(0xDABBAD00, Stat->getGroup());
944 }
945 
946 TEST_F(InMemoryFileSystemTest, AddFileWithFileType) {
947   FS.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"), None, None,
948              sys::fs::file_type::socket_file);
949   auto Stat = FS.status("/a");
950   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
951   ASSERT_TRUE(Stat->isDirectory());
952   Stat = FS.status("/a/b");
953   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
954   ASSERT_TRUE(Stat->isDirectory());
955   Stat = FS.status("/a/b/c");
956   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
957   ASSERT_EQ(sys::fs::file_type::socket_file, Stat->getType());
958   ASSERT_EQ(sys::fs::perms::all_all, Stat->getPermissions());
959 }
960 
961 TEST_F(InMemoryFileSystemTest, AddFileWithPerms) {
962   FS.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"), None, None, None,
963              sys::fs::perms::owner_read | sys::fs::perms::owner_write);
964   auto Stat = FS.status("/a");
965   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
966   ASSERT_TRUE(Stat->isDirectory());
967   ASSERT_EQ(sys::fs::perms::owner_read | sys::fs::perms::owner_write |
968                 sys::fs::perms::owner_exe,
969             Stat->getPermissions());
970   Stat = FS.status("/a/b");
971   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
972   ASSERT_TRUE(Stat->isDirectory());
973   ASSERT_EQ(sys::fs::perms::owner_read | sys::fs::perms::owner_write |
974                 sys::fs::perms::owner_exe,
975             Stat->getPermissions());
976   Stat = FS.status("/a/b/c");
977   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
978   ASSERT_TRUE(Stat->isRegularFile());
979   ASSERT_EQ(sys::fs::perms::owner_read | sys::fs::perms::owner_write,
980             Stat->getPermissions());
981 }
982 
983 TEST_F(InMemoryFileSystemTest, AddDirectoryThenAddChild) {
984   FS.addFile("/a", 0, MemoryBuffer::getMemBuffer(""), /*User=*/None,
985              /*Group=*/None, sys::fs::file_type::directory_file);
986   FS.addFile("/a/b", 0, MemoryBuffer::getMemBuffer("abc"), /*User=*/None,
987              /*Group=*/None, sys::fs::file_type::regular_file);
988   auto Stat = FS.status("/a");
989   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
990   ASSERT_TRUE(Stat->isDirectory());
991   Stat = FS.status("/a/b");
992   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
993   ASSERT_TRUE(Stat->isRegularFile());
994 }
995 
996 // Test that the name returned by status() is in the same form as the path that
997 // was requested (to match the behavior of RealFileSystem).
998 TEST_F(InMemoryFileSystemTest, StatusName) {
999   NormalizedFS.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"),
1000                        /*User=*/None,
1001                        /*Group=*/None, sys::fs::file_type::regular_file);
1002   NormalizedFS.setCurrentWorkingDirectory("/a/b");
1003 
1004   // Access using InMemoryFileSystem::status.
1005   auto Stat = NormalizedFS.status("../b/c");
1006   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n"
1007                                 << NormalizedFS.toString();
1008   ASSERT_TRUE(Stat->isRegularFile());
1009   ASSERT_EQ("../b/c", Stat->getName());
1010 
1011   // Access using InMemoryFileAdaptor::status.
1012   auto File = NormalizedFS.openFileForRead("../b/c");
1013   ASSERT_FALSE(File.getError()) << File.getError() << "\n"
1014                                 << NormalizedFS.toString();
1015   Stat = (*File)->status();
1016   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n"
1017                                 << NormalizedFS.toString();
1018   ASSERT_TRUE(Stat->isRegularFile());
1019   ASSERT_EQ("../b/c", Stat->getName());
1020 
1021   // Access using a directory iterator.
1022   std::error_code EC;
1023   llvm::vfs::directory_iterator It = NormalizedFS.dir_begin("../b", EC);
1024   // When on Windows, we end up with "../b\\c" as the name.  Convert to Posix
1025   // path for the sake of the comparison.
1026   ASSERT_EQ("../b/c", getPosixPath(It->path()));
1027 }
1028 
1029 TEST_F(InMemoryFileSystemTest, AddHardLinkToFile) {
1030   StringRef FromLink = "/path/to/FROM/link";
1031   StringRef Target = "/path/to/TO/file";
1032   FS.addFile(Target, 0, MemoryBuffer::getMemBuffer("content of target"));
1033   EXPECT_TRUE(FS.addHardLink(FromLink, Target));
1034   EXPECT_THAT(FromLink, IsHardLinkTo(&FS, Target));
1035   EXPECT_TRUE(FS.status(FromLink)->getSize() == FS.status(Target)->getSize());
1036   EXPECT_TRUE(FS.getBufferForFile(FromLink)->get()->getBuffer() ==
1037               FS.getBufferForFile(Target)->get()->getBuffer());
1038 }
1039 
1040 TEST_F(InMemoryFileSystemTest, AddHardLinkInChainPattern) {
1041   StringRef Link0 = "/path/to/0/link";
1042   StringRef Link1 = "/path/to/1/link";
1043   StringRef Link2 = "/path/to/2/link";
1044   StringRef Target = "/path/to/target";
1045   FS.addFile(Target, 0, MemoryBuffer::getMemBuffer("content of target file"));
1046   EXPECT_TRUE(FS.addHardLink(Link2, Target));
1047   EXPECT_TRUE(FS.addHardLink(Link1, Link2));
1048   EXPECT_TRUE(FS.addHardLink(Link0, Link1));
1049   EXPECT_THAT(Link0, IsHardLinkTo(&FS, Target));
1050   EXPECT_THAT(Link1, IsHardLinkTo(&FS, Target));
1051   EXPECT_THAT(Link2, IsHardLinkTo(&FS, Target));
1052 }
1053 
1054 TEST_F(InMemoryFileSystemTest, AddHardLinkToAFileThatWasNotAddedBefore) {
1055   EXPECT_FALSE(FS.addHardLink("/path/to/link", "/path/to/target"));
1056 }
1057 
1058 TEST_F(InMemoryFileSystemTest, AddHardLinkFromAFileThatWasAddedBefore) {
1059   StringRef Link = "/path/to/link";
1060   StringRef Target = "/path/to/target";
1061   FS.addFile(Target, 0, MemoryBuffer::getMemBuffer("content of target"));
1062   FS.addFile(Link, 0, MemoryBuffer::getMemBuffer("content of link"));
1063   EXPECT_FALSE(FS.addHardLink(Link, Target));
1064 }
1065 
1066 TEST_F(InMemoryFileSystemTest, AddSameHardLinkMoreThanOnce) {
1067   StringRef Link = "/path/to/link";
1068   StringRef Target = "/path/to/target";
1069   FS.addFile(Target, 0, MemoryBuffer::getMemBuffer("content of target"));
1070   EXPECT_TRUE(FS.addHardLink(Link, Target));
1071   EXPECT_FALSE(FS.addHardLink(Link, Target));
1072 }
1073 
1074 TEST_F(InMemoryFileSystemTest, AddFileInPlaceOfAHardLinkWithSameContent) {
1075   StringRef Link = "/path/to/link";
1076   StringRef Target = "/path/to/target";
1077   StringRef Content = "content of target";
1078   EXPECT_TRUE(FS.addFile(Target, 0, MemoryBuffer::getMemBuffer(Content)));
1079   EXPECT_TRUE(FS.addHardLink(Link, Target));
1080   EXPECT_TRUE(FS.addFile(Link, 0, MemoryBuffer::getMemBuffer(Content)));
1081 }
1082 
1083 TEST_F(InMemoryFileSystemTest, AddFileInPlaceOfAHardLinkWithDifferentContent) {
1084   StringRef Link = "/path/to/link";
1085   StringRef Target = "/path/to/target";
1086   StringRef Content = "content of target";
1087   StringRef LinkContent = "different content of link";
1088   EXPECT_TRUE(FS.addFile(Target, 0, MemoryBuffer::getMemBuffer(Content)));
1089   EXPECT_TRUE(FS.addHardLink(Link, Target));
1090   EXPECT_FALSE(FS.addFile(Link, 0, MemoryBuffer::getMemBuffer(LinkContent)));
1091 }
1092 
1093 TEST_F(InMemoryFileSystemTest, AddHardLinkToADirectory) {
1094   StringRef Dir = "path/to/dummy/dir";
1095   StringRef Link = "/path/to/link";
1096   StringRef File = "path/to/dummy/dir/target";
1097   StringRef Content = "content of target";
1098   EXPECT_TRUE(FS.addFile(File, 0, MemoryBuffer::getMemBuffer(Content)));
1099   EXPECT_FALSE(FS.addHardLink(Link, Dir));
1100 }
1101 
1102 TEST_F(InMemoryFileSystemTest, AddHardLinkFromADirectory) {
1103   StringRef Dir = "path/to/dummy/dir";
1104   StringRef Target = "path/to/dummy/dir/target";
1105   StringRef Content = "content of target";
1106   EXPECT_TRUE(FS.addFile(Target, 0, MemoryBuffer::getMemBuffer(Content)));
1107   EXPECT_FALSE(FS.addHardLink(Dir, Target));
1108 }
1109 
1110 TEST_F(InMemoryFileSystemTest, AddHardLinkUnderAFile) {
1111   StringRef CommonContent = "content string";
1112   FS.addFile("/a/b", 0, MemoryBuffer::getMemBuffer(CommonContent));
1113   FS.addFile("/c/d", 0, MemoryBuffer::getMemBuffer(CommonContent));
1114   EXPECT_FALSE(FS.addHardLink("/c/d/e", "/a/b"));
1115 }
1116 
1117 TEST_F(InMemoryFileSystemTest, RecursiveIterationWithHardLink) {
1118   std::error_code EC;
1119   FS.addFile("/a/b", 0, MemoryBuffer::getMemBuffer("content string"));
1120   EXPECT_TRUE(FS.addHardLink("/c/d", "/a/b"));
1121   auto I = vfs::recursive_directory_iterator(FS, "/", EC);
1122   ASSERT_FALSE(EC);
1123   std::vector<std::string> Nodes;
1124   for (auto E = vfs::recursive_directory_iterator(); !EC && I != E;
1125        I.increment(EC)) {
1126     Nodes.push_back(getPosixPath(I->path()));
1127   }
1128   EXPECT_THAT(Nodes, testing::UnorderedElementsAre("/a", "/a/b", "/c", "/c/d"));
1129 }
1130 
1131 // NOTE: in the tests below, we use '//root/' as our root directory, since it is
1132 // a legal *absolute* path on Windows as well as *nix.
1133 class VFSFromYAMLTest : public ::testing::Test {
1134 public:
1135   int NumDiagnostics;
1136 
1137   void SetUp() override { NumDiagnostics = 0; }
1138 
1139   static void CountingDiagHandler(const SMDiagnostic &, void *Context) {
1140     VFSFromYAMLTest *Test = static_cast<VFSFromYAMLTest *>(Context);
1141     ++Test->NumDiagnostics;
1142   }
1143 
1144   IntrusiveRefCntPtr<vfs::FileSystem>
1145   getFromYAMLRawString(StringRef Content,
1146                        IntrusiveRefCntPtr<vfs::FileSystem> ExternalFS) {
1147     std::unique_ptr<MemoryBuffer> Buffer = MemoryBuffer::getMemBuffer(Content);
1148     return getVFSFromYAML(std::move(Buffer), CountingDiagHandler, "", this,
1149                           ExternalFS);
1150   }
1151 
1152   IntrusiveRefCntPtr<vfs::FileSystem> getFromYAMLString(
1153       StringRef Content,
1154       IntrusiveRefCntPtr<vfs::FileSystem> ExternalFS = new DummyFileSystem()) {
1155     std::string VersionPlusContent("{\n  'version':0,\n");
1156     VersionPlusContent += Content.slice(Content.find('{') + 1, StringRef::npos);
1157     return getFromYAMLRawString(VersionPlusContent, ExternalFS);
1158   }
1159 
1160   // This is intended as a "XFAIL" for windows hosts.
1161   bool supportsSameDirMultipleYAMLEntries() {
1162     Triple Host(Triple::normalize(sys::getProcessTriple()));
1163     return !Host.isOSWindows();
1164   }
1165 };
1166 
1167 TEST_F(VFSFromYAMLTest, BasicVFSFromYAML) {
1168   IntrusiveRefCntPtr<vfs::FileSystem> FS;
1169   FS = getFromYAMLString("");
1170   EXPECT_EQ(nullptr, FS.get());
1171   FS = getFromYAMLString("[]");
1172   EXPECT_EQ(nullptr, FS.get());
1173   FS = getFromYAMLString("'string'");
1174   EXPECT_EQ(nullptr, FS.get());
1175   EXPECT_EQ(3, NumDiagnostics);
1176 }
1177 
1178 TEST_F(VFSFromYAMLTest, MappedFiles) {
1179   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
1180   Lower->addRegularFile("//root/foo/bar/a");
1181   IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
1182       "{ 'roots': [\n"
1183       "{\n"
1184       "  'type': 'directory',\n"
1185       "  'name': '//root/',\n"
1186       "  'contents': [ {\n"
1187       "                  'type': 'file',\n"
1188       "                  'name': 'file1',\n"
1189       "                  'external-contents': '//root/foo/bar/a'\n"
1190       "                },\n"
1191       "                {\n"
1192       "                  'type': 'file',\n"
1193       "                  'name': 'file2',\n"
1194       "                  'external-contents': '//root/foo/b'\n"
1195       "                }\n"
1196       "              ]\n"
1197       "}\n"
1198       "]\n"
1199       "}",
1200       Lower);
1201   ASSERT_TRUE(FS.get() != nullptr);
1202 
1203   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
1204       new vfs::OverlayFileSystem(Lower));
1205   O->pushOverlay(FS);
1206 
1207   // file
1208   ErrorOr<vfs::Status> S = O->status("//root/file1");
1209   ASSERT_FALSE(S.getError());
1210   EXPECT_EQ("//root/foo/bar/a", S->getName());
1211   EXPECT_TRUE(S->IsVFSMapped);
1212 
1213   ErrorOr<vfs::Status> SLower = O->status("//root/foo/bar/a");
1214   EXPECT_EQ("//root/foo/bar/a", SLower->getName());
1215   EXPECT_TRUE(S->equivalent(*SLower));
1216   EXPECT_FALSE(SLower->IsVFSMapped);
1217 
1218   // file after opening
1219   auto OpenedF = O->openFileForRead("//root/file1");
1220   ASSERT_FALSE(OpenedF.getError());
1221   auto OpenedS = (*OpenedF)->status();
1222   ASSERT_FALSE(OpenedS.getError());
1223   EXPECT_EQ("//root/foo/bar/a", OpenedS->getName());
1224   EXPECT_TRUE(OpenedS->IsVFSMapped);
1225 
1226   // directory
1227   S = O->status("//root/");
1228   ASSERT_FALSE(S.getError());
1229   EXPECT_TRUE(S->isDirectory());
1230   EXPECT_TRUE(S->equivalent(*O->status("//root/"))); // non-volatile UniqueID
1231 
1232   // broken mapping
1233   EXPECT_EQ(O->status("//root/file2").getError(),
1234             llvm::errc::no_such_file_or_directory);
1235   EXPECT_EQ(0, NumDiagnostics);
1236 }
1237 
1238 TEST_F(VFSFromYAMLTest, CaseInsensitive) {
1239   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
1240   Lower->addRegularFile("//root/foo/bar/a");
1241   IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
1242       "{ 'case-sensitive': 'false',\n"
1243       "  'roots': [\n"
1244       "{\n"
1245       "  'type': 'directory',\n"
1246       "  'name': '//root/',\n"
1247       "  'contents': [ {\n"
1248       "                  'type': 'file',\n"
1249       "                  'name': 'XX',\n"
1250       "                  'external-contents': '//root/foo/bar/a'\n"
1251       "                }\n"
1252       "              ]\n"
1253       "}]}",
1254       Lower);
1255   ASSERT_TRUE(FS.get() != nullptr);
1256 
1257   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
1258       new vfs::OverlayFileSystem(Lower));
1259   O->pushOverlay(FS);
1260 
1261   ErrorOr<vfs::Status> S = O->status("//root/XX");
1262   ASSERT_FALSE(S.getError());
1263 
1264   ErrorOr<vfs::Status> SS = O->status("//root/xx");
1265   ASSERT_FALSE(SS.getError());
1266   EXPECT_TRUE(S->equivalent(*SS));
1267   SS = O->status("//root/xX");
1268   EXPECT_TRUE(S->equivalent(*SS));
1269   SS = O->status("//root/Xx");
1270   EXPECT_TRUE(S->equivalent(*SS));
1271   EXPECT_EQ(0, NumDiagnostics);
1272 }
1273 
1274 TEST_F(VFSFromYAMLTest, CaseSensitive) {
1275   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
1276   Lower->addRegularFile("//root/foo/bar/a");
1277   IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
1278       "{ 'case-sensitive': 'true',\n"
1279       "  'roots': [\n"
1280       "{\n"
1281       "  'type': 'directory',\n"
1282       "  'name': '//root/',\n"
1283       "  'contents': [ {\n"
1284       "                  'type': 'file',\n"
1285       "                  'name': 'XX',\n"
1286       "                  'external-contents': '//root/foo/bar/a'\n"
1287       "                }\n"
1288       "              ]\n"
1289       "}]}",
1290       Lower);
1291   ASSERT_TRUE(FS.get() != nullptr);
1292 
1293   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
1294       new vfs::OverlayFileSystem(Lower));
1295   O->pushOverlay(FS);
1296 
1297   ErrorOr<vfs::Status> SS = O->status("//root/xx");
1298   EXPECT_EQ(SS.getError(), llvm::errc::no_such_file_or_directory);
1299   SS = O->status("//root/xX");
1300   EXPECT_EQ(SS.getError(), llvm::errc::no_such_file_or_directory);
1301   SS = O->status("//root/Xx");
1302   EXPECT_EQ(SS.getError(), llvm::errc::no_such_file_or_directory);
1303   EXPECT_EQ(0, NumDiagnostics);
1304 }
1305 
1306 TEST_F(VFSFromYAMLTest, IllegalVFSFile) {
1307   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
1308 
1309   // invalid YAML at top-level
1310   IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString("{]", Lower);
1311   EXPECT_EQ(nullptr, FS.get());
1312   // invalid YAML in roots
1313   FS = getFromYAMLString("{ 'roots':[}", Lower);
1314   // invalid YAML in directory
1315   FS = getFromYAMLString(
1316       "{ 'roots':[ { 'name': 'foo', 'type': 'directory', 'contents': [}",
1317       Lower);
1318   EXPECT_EQ(nullptr, FS.get());
1319 
1320   // invalid configuration
1321   FS = getFromYAMLString("{ 'knobular': 'true', 'roots':[] }", Lower);
1322   EXPECT_EQ(nullptr, FS.get());
1323   FS = getFromYAMLString("{ 'case-sensitive': 'maybe', 'roots':[] }", Lower);
1324   EXPECT_EQ(nullptr, FS.get());
1325 
1326   // invalid roots
1327   FS = getFromYAMLString("{ 'roots':'' }", Lower);
1328   EXPECT_EQ(nullptr, FS.get());
1329   FS = getFromYAMLString("{ 'roots':{} }", Lower);
1330   EXPECT_EQ(nullptr, FS.get());
1331 
1332   // invalid entries
1333   FS = getFromYAMLString(
1334       "{ 'roots':[ { 'type': 'other', 'name': 'me', 'contents': '' }", Lower);
1335   EXPECT_EQ(nullptr, FS.get());
1336   FS = getFromYAMLString("{ 'roots':[ { 'type': 'file', 'name': [], "
1337                          "'external-contents': 'other' }",
1338                          Lower);
1339   EXPECT_EQ(nullptr, FS.get());
1340   FS = getFromYAMLString(
1341       "{ 'roots':[ { 'type': 'file', 'name': 'me', 'external-contents': [] }",
1342       Lower);
1343   EXPECT_EQ(nullptr, FS.get());
1344   FS = getFromYAMLString(
1345       "{ 'roots':[ { 'type': 'file', 'name': 'me', 'external-contents': {} }",
1346       Lower);
1347   EXPECT_EQ(nullptr, FS.get());
1348   FS = getFromYAMLString(
1349       "{ 'roots':[ { 'type': 'directory', 'name': 'me', 'contents': {} }",
1350       Lower);
1351   EXPECT_EQ(nullptr, FS.get());
1352   FS = getFromYAMLString(
1353       "{ 'roots':[ { 'type': 'directory', 'name': 'me', 'contents': '' }",
1354       Lower);
1355   EXPECT_EQ(nullptr, FS.get());
1356   FS = getFromYAMLString(
1357       "{ 'roots':[ { 'thingy': 'directory', 'name': 'me', 'contents': [] }",
1358       Lower);
1359   EXPECT_EQ(nullptr, FS.get());
1360 
1361   // missing mandatory fields
1362   FS = getFromYAMLString("{ 'roots':[ { 'type': 'file', 'name': 'me' }", Lower);
1363   EXPECT_EQ(nullptr, FS.get());
1364   FS = getFromYAMLString(
1365       "{ 'roots':[ { 'type': 'file', 'external-contents': 'other' }", Lower);
1366   EXPECT_EQ(nullptr, FS.get());
1367   FS = getFromYAMLString("{ 'roots':[ { 'name': 'me', 'contents': [] }", Lower);
1368   EXPECT_EQ(nullptr, FS.get());
1369 
1370   // duplicate keys
1371   FS = getFromYAMLString("{ 'roots':[], 'roots':[] }", Lower);
1372   EXPECT_EQ(nullptr, FS.get());
1373   FS = getFromYAMLString(
1374       "{ 'case-sensitive':'true', 'case-sensitive':'true', 'roots':[] }",
1375       Lower);
1376   EXPECT_EQ(nullptr, FS.get());
1377   FS =
1378       getFromYAMLString("{ 'roots':[{'name':'me', 'name':'you', 'type':'file', "
1379                         "'external-contents':'blah' } ] }",
1380                         Lower);
1381   EXPECT_EQ(nullptr, FS.get());
1382 
1383   // missing version
1384   FS = getFromYAMLRawString("{ 'roots':[] }", Lower);
1385   EXPECT_EQ(nullptr, FS.get());
1386 
1387   // bad version number
1388   FS = getFromYAMLRawString("{ 'version':'foo', 'roots':[] }", Lower);
1389   EXPECT_EQ(nullptr, FS.get());
1390   FS = getFromYAMLRawString("{ 'version':-1, 'roots':[] }", Lower);
1391   EXPECT_EQ(nullptr, FS.get());
1392   FS = getFromYAMLRawString("{ 'version':100000, 'roots':[] }", Lower);
1393   EXPECT_EQ(nullptr, FS.get());
1394   EXPECT_EQ(24, NumDiagnostics);
1395 }
1396 
1397 TEST_F(VFSFromYAMLTest, UseExternalName) {
1398   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
1399   Lower->addRegularFile("//root/external/file");
1400 
1401   IntrusiveRefCntPtr<vfs::FileSystem> FS =
1402       getFromYAMLString("{ 'roots': [\n"
1403                         "  { 'type': 'file', 'name': '//root/A',\n"
1404                         "    'external-contents': '//root/external/file'\n"
1405                         "  },\n"
1406                         "  { 'type': 'file', 'name': '//root/B',\n"
1407                         "    'use-external-name': true,\n"
1408                         "    'external-contents': '//root/external/file'\n"
1409                         "  },\n"
1410                         "  { 'type': 'file', 'name': '//root/C',\n"
1411                         "    'use-external-name': false,\n"
1412                         "    'external-contents': '//root/external/file'\n"
1413                         "  }\n"
1414                         "] }",
1415                         Lower);
1416   ASSERT_TRUE(nullptr != FS.get());
1417 
1418   // default true
1419   EXPECT_EQ("//root/external/file", FS->status("//root/A")->getName());
1420   // explicit
1421   EXPECT_EQ("//root/external/file", FS->status("//root/B")->getName());
1422   EXPECT_EQ("//root/C", FS->status("//root/C")->getName());
1423 
1424   // global configuration
1425   FS = getFromYAMLString("{ 'use-external-names': false,\n"
1426                          "  'roots': [\n"
1427                          "  { 'type': 'file', 'name': '//root/A',\n"
1428                          "    'external-contents': '//root/external/file'\n"
1429                          "  },\n"
1430                          "  { 'type': 'file', 'name': '//root/B',\n"
1431                          "    'use-external-name': true,\n"
1432                          "    'external-contents': '//root/external/file'\n"
1433                          "  },\n"
1434                          "  { 'type': 'file', 'name': '//root/C',\n"
1435                          "    'use-external-name': false,\n"
1436                          "    'external-contents': '//root/external/file'\n"
1437                          "  }\n"
1438                          "] }",
1439                          Lower);
1440   ASSERT_TRUE(nullptr != FS.get());
1441 
1442   // default
1443   EXPECT_EQ("//root/A", FS->status("//root/A")->getName());
1444   // explicit
1445   EXPECT_EQ("//root/external/file", FS->status("//root/B")->getName());
1446   EXPECT_EQ("//root/C", FS->status("//root/C")->getName());
1447 }
1448 
1449 TEST_F(VFSFromYAMLTest, MultiComponentPath) {
1450   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
1451   Lower->addRegularFile("//root/other");
1452 
1453   // file in roots
1454   IntrusiveRefCntPtr<vfs::FileSystem> FS =
1455       getFromYAMLString("{ 'roots': [\n"
1456                         "  { 'type': 'file', 'name': '//root/path/to/file',\n"
1457                         "    'external-contents': '//root/other' }]\n"
1458                         "}",
1459                         Lower);
1460   ASSERT_TRUE(nullptr != FS.get());
1461   EXPECT_FALSE(FS->status("//root/path/to/file").getError());
1462   EXPECT_FALSE(FS->status("//root/path/to").getError());
1463   EXPECT_FALSE(FS->status("//root/path").getError());
1464   EXPECT_FALSE(FS->status("//root/").getError());
1465 
1466   // at the start
1467   FS = getFromYAMLString(
1468       "{ 'roots': [\n"
1469       "  { 'type': 'directory', 'name': '//root/path/to',\n"
1470       "    'contents': [ { 'type': 'file', 'name': 'file',\n"
1471       "                    'external-contents': '//root/other' }]}]\n"
1472       "}",
1473       Lower);
1474   ASSERT_TRUE(nullptr != FS.get());
1475   EXPECT_FALSE(FS->status("//root/path/to/file").getError());
1476   EXPECT_FALSE(FS->status("//root/path/to").getError());
1477   EXPECT_FALSE(FS->status("//root/path").getError());
1478   EXPECT_FALSE(FS->status("//root/").getError());
1479 
1480   // at the end
1481   FS = getFromYAMLString(
1482       "{ 'roots': [\n"
1483       "  { 'type': 'directory', 'name': '//root/',\n"
1484       "    'contents': [ { 'type': 'file', 'name': 'path/to/file',\n"
1485       "                    'external-contents': '//root/other' }]}]\n"
1486       "}",
1487       Lower);
1488   ASSERT_TRUE(nullptr != FS.get());
1489   EXPECT_FALSE(FS->status("//root/path/to/file").getError());
1490   EXPECT_FALSE(FS->status("//root/path/to").getError());
1491   EXPECT_FALSE(FS->status("//root/path").getError());
1492   EXPECT_FALSE(FS->status("//root/").getError());
1493 }
1494 
1495 TEST_F(VFSFromYAMLTest, TrailingSlashes) {
1496   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
1497   Lower->addRegularFile("//root/other");
1498 
1499   // file in roots
1500   IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
1501       "{ 'roots': [\n"
1502       "  { 'type': 'directory', 'name': '//root/path/to////',\n"
1503       "    'contents': [ { 'type': 'file', 'name': '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 
1514 TEST_F(VFSFromYAMLTest, DirectoryIteration) {
1515   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
1516   Lower->addDirectory("//root/");
1517   Lower->addDirectory("//root/foo");
1518   Lower->addDirectory("//root/foo/bar");
1519   Lower->addRegularFile("//root/foo/bar/a");
1520   Lower->addRegularFile("//root/foo/bar/b");
1521   Lower->addRegularFile("//root/file3");
1522   IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
1523       "{ 'use-external-names': false,\n"
1524       "  'roots': [\n"
1525       "{\n"
1526       "  'type': 'directory',\n"
1527       "  'name': '//root/',\n"
1528       "  'contents': [ {\n"
1529       "                  'type': 'file',\n"
1530       "                  'name': 'file1',\n"
1531       "                  'external-contents': '//root/foo/bar/a'\n"
1532       "                },\n"
1533       "                {\n"
1534       "                  'type': 'file',\n"
1535       "                  'name': 'file2',\n"
1536       "                  'external-contents': '//root/foo/bar/b'\n"
1537       "                }\n"
1538       "              ]\n"
1539       "}\n"
1540       "]\n"
1541       "}",
1542       Lower);
1543   ASSERT_TRUE(FS.get() != nullptr);
1544 
1545   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
1546       new vfs::OverlayFileSystem(Lower));
1547   O->pushOverlay(FS);
1548 
1549   std::error_code EC;
1550   checkContents(O->dir_begin("//root/", EC),
1551                 {"//root/file1", "//root/file2", "//root/file3", "//root/foo"});
1552 
1553   checkContents(O->dir_begin("//root/foo/bar", EC),
1554                 {"//root/foo/bar/a", "//root/foo/bar/b"});
1555 }
1556 
1557 TEST_F(VFSFromYAMLTest, DirectoryIterationSameDirMultipleEntries) {
1558   // https://llvm.org/bugs/show_bug.cgi?id=27725
1559   if (!supportsSameDirMultipleYAMLEntries())
1560     return;
1561 
1562   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
1563   Lower->addDirectory("//root/zab");
1564   Lower->addDirectory("//root/baz");
1565   Lower->addRegularFile("//root/zab/a");
1566   Lower->addRegularFile("//root/zab/b");
1567   IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
1568       "{ 'use-external-names': false,\n"
1569       "  'roots': [\n"
1570       "{\n"
1571       "  'type': 'directory',\n"
1572       "  'name': '//root/baz/',\n"
1573       "  'contents': [ {\n"
1574       "                  'type': 'file',\n"
1575       "                  'name': 'x',\n"
1576       "                  'external-contents': '//root/zab/a'\n"
1577       "                }\n"
1578       "              ]\n"
1579       "},\n"
1580       "{\n"
1581       "  'type': 'directory',\n"
1582       "  'name': '//root/baz/',\n"
1583       "  'contents': [ {\n"
1584       "                  'type': 'file',\n"
1585       "                  'name': 'y',\n"
1586       "                  'external-contents': '//root/zab/b'\n"
1587       "                }\n"
1588       "              ]\n"
1589       "}\n"
1590       "]\n"
1591       "}",
1592       Lower);
1593   ASSERT_TRUE(FS.get() != nullptr);
1594 
1595   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
1596       new vfs::OverlayFileSystem(Lower));
1597   O->pushOverlay(FS);
1598 
1599   std::error_code EC;
1600 
1601   checkContents(O->dir_begin("//root/baz/", EC),
1602                 {"//root/baz/x", "//root/baz/y"});
1603 }
1604 
1605 TEST_F(VFSFromYAMLTest, RecursiveDirectoryIterationLevel) {
1606 
1607   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
1608   Lower->addDirectory("//root/a");
1609   Lower->addDirectory("//root/a/b");
1610   Lower->addDirectory("//root/a/b/c");
1611   Lower->addRegularFile("//root/a/b/c/file");
1612   IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
1613       "{ 'use-external-names': false,\n"
1614       "  'roots': [\n"
1615       "{\n"
1616       "  'type': 'directory',\n"
1617       "  'name': '//root/a/b/c/',\n"
1618       "  'contents': [ {\n"
1619       "                  'type': 'file',\n"
1620       "                  'name': 'file',\n"
1621       "                  'external-contents': '//root/a/b/c/file'\n"
1622       "                }\n"
1623       "              ]\n"
1624       "},\n"
1625       "]\n"
1626       "}",
1627       Lower);
1628   ASSERT_TRUE(FS.get() != nullptr);
1629 
1630   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
1631       new vfs::OverlayFileSystem(Lower));
1632   O->pushOverlay(FS);
1633 
1634   std::error_code EC;
1635 
1636   // Test recursive_directory_iterator level()
1637   vfs::recursive_directory_iterator I = vfs::recursive_directory_iterator(
1638                                         *O, "//root", EC),
1639                                     E;
1640   ASSERT_FALSE(EC);
1641   for (int l = 0; I != E; I.increment(EC), ++l) {
1642     ASSERT_FALSE(EC);
1643     EXPECT_EQ(I.level(), l);
1644   }
1645   EXPECT_EQ(I, E);
1646 }
1647 
1648 TEST_F(VFSFromYAMLTest, RelativePaths) {
1649   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
1650   // Filename at root level without a parent directory.
1651   IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
1652       "{ 'roots': [\n"
1653       "  { 'type': 'file', 'name': 'file-not-in-directory.h',\n"
1654       "    'external-contents': '//root/external/file'\n"
1655       "  }\n"
1656       "] }",
1657       Lower);
1658   EXPECT_EQ(nullptr, FS.get());
1659 
1660   // Relative file path.
1661   FS = getFromYAMLString("{ 'roots': [\n"
1662                          "  { 'type': 'file', 'name': 'relative/file/path.h',\n"
1663                          "    'external-contents': '//root/external/file'\n"
1664                          "  }\n"
1665                          "] }",
1666                          Lower);
1667   EXPECT_EQ(nullptr, FS.get());
1668 
1669   // Relative directory path.
1670   FS = getFromYAMLString(
1671       "{ 'roots': [\n"
1672       "  { 'type': 'directory', 'name': 'relative/directory/path.h',\n"
1673       "    'contents': []\n"
1674       "  }\n"
1675       "] }",
1676       Lower);
1677   EXPECT_EQ(nullptr, FS.get());
1678 
1679   EXPECT_EQ(3, NumDiagnostics);
1680 }
1681 
1682 TEST_F(VFSFromYAMLTest, NonFallthroughDirectoryIteration) {
1683   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
1684   Lower->addDirectory("//root/");
1685   Lower->addRegularFile("//root/a");
1686   Lower->addRegularFile("//root/b");
1687   IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
1688       "{ 'use-external-names': false,\n"
1689       "  'fallthrough': false,\n"
1690       "  'roots': [\n"
1691       "{\n"
1692       "  'type': 'directory',\n"
1693       "  'name': '//root/',\n"
1694       "  'contents': [ {\n"
1695       "                  'type': 'file',\n"
1696       "                  'name': 'c',\n"
1697       "                  'external-contents': '//root/a'\n"
1698       "                }\n"
1699       "              ]\n"
1700       "}\n"
1701       "]\n"
1702       "}",
1703       Lower);
1704   ASSERT_TRUE(FS.get() != nullptr);
1705 
1706   std::error_code EC;
1707   checkContents(FS->dir_begin("//root/", EC),
1708                 {"//root/c"});
1709 }
1710 
1711 TEST_F(VFSFromYAMLTest, DirectoryIterationWithDuplicates) {
1712   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
1713   Lower->addDirectory("//root/");
1714   Lower->addRegularFile("//root/a");
1715   Lower->addRegularFile("//root/b");
1716   IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
1717       "{ 'use-external-names': false,\n"
1718       "  'roots': [\n"
1719       "{\n"
1720       "  'type': 'directory',\n"
1721       "  'name': '//root/',\n"
1722       "  'contents': [ {\n"
1723       "                  'type': 'file',\n"
1724       "                  'name': 'a',\n"
1725       "                  'external-contents': '//root/a'\n"
1726       "                }\n"
1727       "              ]\n"
1728       "}\n"
1729       "]\n"
1730       "}",
1731 	  Lower);
1732   ASSERT_TRUE(FS.get() != nullptr);
1733 
1734   std::error_code EC;
1735   checkContents(FS->dir_begin("//root/", EC),
1736                 {"//root/a", "//root/b"});
1737 }
1738 
1739 TEST_F(VFSFromYAMLTest, DirectoryIterationErrorInVFSLayer) {
1740   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
1741   Lower->addDirectory("//root/");
1742   Lower->addDirectory("//root/foo");
1743   Lower->addRegularFile("//root/foo/a");
1744   Lower->addRegularFile("//root/foo/b");
1745   IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
1746       "{ 'use-external-names': false,\n"
1747       "  'roots': [\n"
1748       "{\n"
1749       "  'type': 'directory',\n"
1750       "  'name': '//root/',\n"
1751       "  'contents': [ {\n"
1752       "                  'type': 'file',\n"
1753       "                  'name': 'bar/a',\n"
1754       "                  'external-contents': '//root/foo/a'\n"
1755       "                }\n"
1756       "              ]\n"
1757       "}\n"
1758       "]\n"
1759       "}",
1760       Lower);
1761   ASSERT_TRUE(FS.get() != nullptr);
1762 
1763   std::error_code EC;
1764   checkContents(FS->dir_begin("//root/foo", EC),
1765                 {"//root/foo/a", "//root/foo/b"});
1766 }
1767