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