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