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