• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //===- unittests/Basic/VirtualFileSystem.cpp ---------------- VFS tests ---===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 
10 #include "clang/Basic/VirtualFileSystem.h"
11 #include "llvm/ADT/Triple.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 "gtest/gtest.h"
18 #include <map>
19 
20 using namespace clang;
21 using namespace llvm;
22 using llvm::sys::fs::UniqueID;
23 
24 namespace {
25 struct DummyFile : public vfs::File {
26   vfs::Status S;
DummyFile__anon73ed79de0111::DummyFile27   explicit DummyFile(vfs::Status S) : S(S) {}
status__anon73ed79de0111::DummyFile28   llvm::ErrorOr<vfs::Status> status() override { return S; }
29   llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
getBuffer__anon73ed79de0111::DummyFile30   getBuffer(const Twine &Name, int64_t FileSize, bool RequiresNullTerminator,
31             bool IsVolatile) override {
32     llvm_unreachable("unimplemented");
33   }
close__anon73ed79de0111::DummyFile34   std::error_code close() override { return std::error_code(); }
35 };
36 
37 class DummyFileSystem : public vfs::FileSystem {
38   int FSID;   // used to produce UniqueIDs
39   int FileID; // used to produce UniqueIDs
40   std::map<std::string, vfs::Status> FilesAndDirs;
41 
getNextFSID()42   static int getNextFSID() {
43     static int Count = 0;
44     return Count++;
45   }
46 
47 public:
DummyFileSystem()48   DummyFileSystem() : FSID(getNextFSID()), FileID(0) {}
49 
status(const Twine & Path)50   ErrorOr<vfs::Status> status(const Twine &Path) override {
51     std::map<std::string, vfs::Status>::iterator I =
52         FilesAndDirs.find(Path.str());
53     if (I == FilesAndDirs.end())
54       return make_error_code(llvm::errc::no_such_file_or_directory);
55     return I->second;
56   }
57   ErrorOr<std::unique_ptr<vfs::File>>
openFileForRead(const Twine & Path)58   openFileForRead(const Twine &Path) override {
59     auto S = status(Path);
60     if (S)
61       return std::unique_ptr<vfs::File>(new DummyFile{*S});
62     return S.getError();
63   }
getCurrentWorkingDirectory() const64   llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const override {
65     return std::string();
66   }
setCurrentWorkingDirectory(const Twine & Path)67   std::error_code setCurrentWorkingDirectory(const Twine &Path) override {
68     return std::error_code();
69   }
70 
71   struct DirIterImpl : public clang::vfs::detail::DirIterImpl {
72     std::map<std::string, vfs::Status> &FilesAndDirs;
73     std::map<std::string, vfs::Status>::iterator I;
74     std::string Path;
isInPath__anon73ed79de0111::DummyFileSystem::DirIterImpl75     bool isInPath(StringRef S) {
76       if (Path.size() < S.size() && S.find(Path) == 0) {
77         auto LastSep = S.find_last_of('/');
78         if (LastSep == Path.size() || LastSep == Path.size()-1)
79           return true;
80       }
81       return false;
82     }
DirIterImpl__anon73ed79de0111::DummyFileSystem::DirIterImpl83     DirIterImpl(std::map<std::string, vfs::Status> &FilesAndDirs,
84                 const Twine &_Path)
85         : FilesAndDirs(FilesAndDirs), I(FilesAndDirs.begin()),
86           Path(_Path.str()) {
87       for ( ; I != FilesAndDirs.end(); ++I) {
88         if (isInPath(I->first)) {
89           CurrentEntry = I->second;
90           break;
91         }
92       }
93     }
increment__anon73ed79de0111::DummyFileSystem::DirIterImpl94     std::error_code increment() override {
95       ++I;
96       for ( ; I != FilesAndDirs.end(); ++I) {
97         if (isInPath(I->first)) {
98           CurrentEntry = I->second;
99           break;
100         }
101       }
102       if (I == FilesAndDirs.end())
103         CurrentEntry = vfs::Status();
104       return std::error_code();
105     }
106   };
107 
dir_begin(const Twine & Dir,std::error_code & EC)108   vfs::directory_iterator dir_begin(const Twine &Dir,
109                                     std::error_code &EC) override {
110     return vfs::directory_iterator(
111         std::make_shared<DirIterImpl>(FilesAndDirs, Dir));
112   }
113 
addEntry(StringRef Path,const vfs::Status & Status)114   void addEntry(StringRef Path, const vfs::Status &Status) {
115     FilesAndDirs[Path] = Status;
116   }
117 
addRegularFile(StringRef Path,sys::fs::perms Perms=sys::fs::all_all)118   void addRegularFile(StringRef Path, sys::fs::perms Perms = sys::fs::all_all) {
119     vfs::Status S(Path, UniqueID(FSID, FileID++), sys::TimeValue::now(), 0, 0,
120                   1024, sys::fs::file_type::regular_file, Perms);
121     addEntry(Path, S);
122   }
123 
addDirectory(StringRef Path,sys::fs::perms Perms=sys::fs::all_all)124   void addDirectory(StringRef Path, sys::fs::perms Perms = sys::fs::all_all) {
125     vfs::Status S(Path, UniqueID(FSID, FileID++), sys::TimeValue::now(), 0, 0,
126                   0, sys::fs::file_type::directory_file, Perms);
127     addEntry(Path, S);
128   }
129 
addSymlink(StringRef Path)130   void addSymlink(StringRef Path) {
131     vfs::Status S(Path, UniqueID(FSID, FileID++), sys::TimeValue::now(), 0, 0,
132                   0, sys::fs::file_type::symlink_file, sys::fs::all_all);
133     addEntry(Path, S);
134   }
135 };
136 } // end anonymous namespace
137 
TEST(VirtualFileSystemTest,StatusQueries)138 TEST(VirtualFileSystemTest, StatusQueries) {
139   IntrusiveRefCntPtr<DummyFileSystem> D(new DummyFileSystem());
140   ErrorOr<vfs::Status> Status((std::error_code()));
141 
142   D->addRegularFile("/foo");
143   Status = D->status("/foo");
144   ASSERT_FALSE(Status.getError());
145   EXPECT_TRUE(Status->isStatusKnown());
146   EXPECT_FALSE(Status->isDirectory());
147   EXPECT_TRUE(Status->isRegularFile());
148   EXPECT_FALSE(Status->isSymlink());
149   EXPECT_FALSE(Status->isOther());
150   EXPECT_TRUE(Status->exists());
151 
152   D->addDirectory("/bar");
153   Status = D->status("/bar");
154   ASSERT_FALSE(Status.getError());
155   EXPECT_TRUE(Status->isStatusKnown());
156   EXPECT_TRUE(Status->isDirectory());
157   EXPECT_FALSE(Status->isRegularFile());
158   EXPECT_FALSE(Status->isSymlink());
159   EXPECT_FALSE(Status->isOther());
160   EXPECT_TRUE(Status->exists());
161 
162   D->addSymlink("/baz");
163   Status = D->status("/baz");
164   ASSERT_FALSE(Status.getError());
165   EXPECT_TRUE(Status->isStatusKnown());
166   EXPECT_FALSE(Status->isDirectory());
167   EXPECT_FALSE(Status->isRegularFile());
168   EXPECT_TRUE(Status->isSymlink());
169   EXPECT_FALSE(Status->isOther());
170   EXPECT_TRUE(Status->exists());
171 
172   EXPECT_TRUE(Status->equivalent(*Status));
173   ErrorOr<vfs::Status> Status2 = D->status("/foo");
174   ASSERT_FALSE(Status2.getError());
175   EXPECT_FALSE(Status->equivalent(*Status2));
176 }
177 
TEST(VirtualFileSystemTest,BaseOnlyOverlay)178 TEST(VirtualFileSystemTest, BaseOnlyOverlay) {
179   IntrusiveRefCntPtr<DummyFileSystem> D(new DummyFileSystem());
180   ErrorOr<vfs::Status> Status((std::error_code()));
181   EXPECT_FALSE(Status = D->status("/foo"));
182 
183   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(new vfs::OverlayFileSystem(D));
184   EXPECT_FALSE(Status = O->status("/foo"));
185 
186   D->addRegularFile("/foo");
187   Status = D->status("/foo");
188   EXPECT_FALSE(Status.getError());
189 
190   ErrorOr<vfs::Status> Status2((std::error_code()));
191   Status2 = O->status("/foo");
192   EXPECT_FALSE(Status2.getError());
193   EXPECT_TRUE(Status->equivalent(*Status2));
194 }
195 
TEST(VirtualFileSystemTest,OverlayFiles)196 TEST(VirtualFileSystemTest, OverlayFiles) {
197   IntrusiveRefCntPtr<DummyFileSystem> Base(new DummyFileSystem());
198   IntrusiveRefCntPtr<DummyFileSystem> Middle(new DummyFileSystem());
199   IntrusiveRefCntPtr<DummyFileSystem> Top(new DummyFileSystem());
200   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
201       new vfs::OverlayFileSystem(Base));
202   O->pushOverlay(Middle);
203   O->pushOverlay(Top);
204 
205   ErrorOr<vfs::Status> Status1((std::error_code())),
206       Status2((std::error_code())), Status3((std::error_code())),
207       StatusB((std::error_code())), StatusM((std::error_code())),
208       StatusT((std::error_code()));
209 
210   Base->addRegularFile("/foo");
211   StatusB = Base->status("/foo");
212   ASSERT_FALSE(StatusB.getError());
213   Status1 = O->status("/foo");
214   ASSERT_FALSE(Status1.getError());
215   Middle->addRegularFile("/foo");
216   StatusM = Middle->status("/foo");
217   ASSERT_FALSE(StatusM.getError());
218   Status2 = O->status("/foo");
219   ASSERT_FALSE(Status2.getError());
220   Top->addRegularFile("/foo");
221   StatusT = Top->status("/foo");
222   ASSERT_FALSE(StatusT.getError());
223   Status3 = O->status("/foo");
224   ASSERT_FALSE(Status3.getError());
225 
226   EXPECT_TRUE(Status1->equivalent(*StatusB));
227   EXPECT_TRUE(Status2->equivalent(*StatusM));
228   EXPECT_TRUE(Status3->equivalent(*StatusT));
229 
230   EXPECT_FALSE(Status1->equivalent(*Status2));
231   EXPECT_FALSE(Status2->equivalent(*Status3));
232   EXPECT_FALSE(Status1->equivalent(*Status3));
233 }
234 
TEST(VirtualFileSystemTest,OverlayDirsNonMerged)235 TEST(VirtualFileSystemTest, OverlayDirsNonMerged) {
236   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
237   IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem());
238   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
239       new vfs::OverlayFileSystem(Lower));
240   O->pushOverlay(Upper);
241 
242   Lower->addDirectory("/lower-only");
243   Upper->addDirectory("/upper-only");
244 
245   // non-merged paths should be the same
246   ErrorOr<vfs::Status> Status1 = Lower->status("/lower-only");
247   ASSERT_FALSE(Status1.getError());
248   ErrorOr<vfs::Status> Status2 = O->status("/lower-only");
249   ASSERT_FALSE(Status2.getError());
250   EXPECT_TRUE(Status1->equivalent(*Status2));
251 
252   Status1 = Upper->status("/upper-only");
253   ASSERT_FALSE(Status1.getError());
254   Status2 = O->status("/upper-only");
255   ASSERT_FALSE(Status2.getError());
256   EXPECT_TRUE(Status1->equivalent(*Status2));
257 }
258 
TEST(VirtualFileSystemTest,MergedDirPermissions)259 TEST(VirtualFileSystemTest, MergedDirPermissions) {
260   // merged directories get the permissions of the upper dir
261   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
262   IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem());
263   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
264       new vfs::OverlayFileSystem(Lower));
265   O->pushOverlay(Upper);
266 
267   ErrorOr<vfs::Status> Status((std::error_code()));
268   Lower->addDirectory("/both", sys::fs::owner_read);
269   Upper->addDirectory("/both", sys::fs::owner_all | sys::fs::group_read);
270   Status = O->status("/both");
271   ASSERT_FALSE(Status.getError());
272   EXPECT_EQ(0740, Status->getPermissions());
273 
274   // permissions (as usual) are not recursively applied
275   Lower->addRegularFile("/both/foo", sys::fs::owner_read);
276   Upper->addRegularFile("/both/bar", sys::fs::owner_write);
277   Status = O->status("/both/foo");
278   ASSERT_FALSE( Status.getError());
279   EXPECT_EQ(0400, Status->getPermissions());
280   Status = O->status("/both/bar");
281   ASSERT_FALSE(Status.getError());
282   EXPECT_EQ(0200, Status->getPermissions());
283 }
284 
285 namespace {
286 struct ScopedDir {
287   SmallString<128> Path;
ScopedDir__anon73ed79de0211::ScopedDir288   ScopedDir(const Twine &Name, bool Unique=false) {
289     std::error_code EC;
290     if (Unique) {
291       EC =  llvm::sys::fs::createUniqueDirectory(Name, Path);
292     } else {
293       Path = Name.str();
294       EC = llvm::sys::fs::create_directory(Twine(Path));
295     }
296     if (EC)
297       Path = "";
298     EXPECT_FALSE(EC);
299   }
~ScopedDir__anon73ed79de0211::ScopedDir300   ~ScopedDir() {
301     if (Path != "")
302       EXPECT_FALSE(llvm::sys::fs::remove(Path.str()));
303   }
operator StringRef__anon73ed79de0211::ScopedDir304   operator StringRef() { return Path.str(); }
305 };
306 } // end anonymous namespace
307 
TEST(VirtualFileSystemTest,BasicRealFSIteration)308 TEST(VirtualFileSystemTest, BasicRealFSIteration) {
309   ScopedDir TestDirectory("virtual-file-system-test", /*Unique*/true);
310   IntrusiveRefCntPtr<vfs::FileSystem> FS = vfs::getRealFileSystem();
311 
312   std::error_code EC;
313   vfs::directory_iterator I = FS->dir_begin(Twine(TestDirectory), EC);
314   ASSERT_FALSE(EC);
315   EXPECT_EQ(vfs::directory_iterator(), I); // empty directory is empty
316 
317   ScopedDir _a(TestDirectory+"/a");
318   ScopedDir _ab(TestDirectory+"/a/b");
319   ScopedDir _c(TestDirectory+"/c");
320   ScopedDir _cd(TestDirectory+"/c/d");
321 
322   I = FS->dir_begin(Twine(TestDirectory), EC);
323   ASSERT_FALSE(EC);
324   ASSERT_NE(vfs::directory_iterator(), I);
325   // Check either a or c, since we can't rely on the iteration order.
326   EXPECT_TRUE(I->getName().endswith("a") || I->getName().endswith("c"));
327   I.increment(EC);
328   ASSERT_FALSE(EC);
329   ASSERT_NE(vfs::directory_iterator(), I);
330   EXPECT_TRUE(I->getName().endswith("a") || I->getName().endswith("c"));
331   I.increment(EC);
332   EXPECT_EQ(vfs::directory_iterator(), I);
333 }
334 
TEST(VirtualFileSystemTest,BasicRealFSRecursiveIteration)335 TEST(VirtualFileSystemTest, BasicRealFSRecursiveIteration) {
336   ScopedDir TestDirectory("virtual-file-system-test", /*Unique*/true);
337   IntrusiveRefCntPtr<vfs::FileSystem> FS = vfs::getRealFileSystem();
338 
339   std::error_code EC;
340   auto I = vfs::recursive_directory_iterator(*FS, Twine(TestDirectory), EC);
341   ASSERT_FALSE(EC);
342   EXPECT_EQ(vfs::recursive_directory_iterator(), I); // empty directory is empty
343 
344   ScopedDir _a(TestDirectory+"/a");
345   ScopedDir _ab(TestDirectory+"/a/b");
346   ScopedDir _c(TestDirectory+"/c");
347   ScopedDir _cd(TestDirectory+"/c/d");
348 
349   I = vfs::recursive_directory_iterator(*FS, Twine(TestDirectory), EC);
350   ASSERT_FALSE(EC);
351   ASSERT_NE(vfs::recursive_directory_iterator(), I);
352 
353   std::vector<std::string> Contents;
354   for (auto E = vfs::recursive_directory_iterator(); !EC && I != E;
355        I.increment(EC)) {
356     Contents.push_back(I->getName());
357   }
358 
359   // Check contents, which may be in any order
360   EXPECT_EQ(4U, Contents.size());
361   int Counts[4] = { 0, 0, 0, 0 };
362   for (const std::string &Name : Contents) {
363     ASSERT_FALSE(Name.empty());
364     int Index = Name[Name.size()-1] - 'a';
365     ASSERT_TRUE(Index >= 0 && Index < 4);
366     Counts[Index]++;
367   }
368   EXPECT_EQ(1, Counts[0]); // a
369   EXPECT_EQ(1, Counts[1]); // b
370   EXPECT_EQ(1, Counts[2]); // c
371   EXPECT_EQ(1, Counts[3]); // d
372 }
373 
374 template <typename DirIter>
checkContents(DirIter I,ArrayRef<StringRef> ExpectedOut)375 static void checkContents(DirIter I, ArrayRef<StringRef> ExpectedOut) {
376   std::error_code EC;
377   SmallVector<StringRef, 4> Expected(ExpectedOut.begin(), ExpectedOut.end());
378   SmallVector<std::string, 4> InputToCheck;
379 
380   // Do not rely on iteration order to check for contents, sort both
381   // content vectors before comparison.
382   for (DirIter E; !EC && I != E; I.increment(EC))
383     InputToCheck.push_back(I->getName());
384 
385   std::sort(InputToCheck.begin(), InputToCheck.end());
386   std::sort(Expected.begin(), Expected.end());
387   EXPECT_EQ(InputToCheck.size(), Expected.size());
388 
389   unsigned LastElt = std::min(InputToCheck.size(), Expected.size());
390   for (unsigned Idx = 0; Idx != LastElt; ++Idx)
391     EXPECT_EQ(StringRef(InputToCheck[Idx]), Expected[Idx]);
392 }
393 
TEST(VirtualFileSystemTest,OverlayIteration)394 TEST(VirtualFileSystemTest, OverlayIteration) {
395   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
396   IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem());
397   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
398       new vfs::OverlayFileSystem(Lower));
399   O->pushOverlay(Upper);
400 
401   std::error_code EC;
402   checkContents(O->dir_begin("/", EC), ArrayRef<StringRef>());
403 
404   Lower->addRegularFile("/file1");
405   checkContents(O->dir_begin("/", EC), ArrayRef<StringRef>("/file1"));
406 
407   Upper->addRegularFile("/file2");
408   checkContents(O->dir_begin("/", EC), {"/file2", "/file1"});
409 
410   Lower->addDirectory("/dir1");
411   Lower->addRegularFile("/dir1/foo");
412   Upper->addDirectory("/dir2");
413   Upper->addRegularFile("/dir2/foo");
414   checkContents(O->dir_begin("/dir2", EC), ArrayRef<StringRef>("/dir2/foo"));
415   checkContents(O->dir_begin("/", EC), {"/dir2", "/file2", "/dir1", "/file1"});
416 }
417 
TEST(VirtualFileSystemTest,OverlayRecursiveIteration)418 TEST(VirtualFileSystemTest, OverlayRecursiveIteration) {
419   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
420   IntrusiveRefCntPtr<DummyFileSystem> Middle(new DummyFileSystem());
421   IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem());
422   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
423       new vfs::OverlayFileSystem(Lower));
424   O->pushOverlay(Middle);
425   O->pushOverlay(Upper);
426 
427   std::error_code EC;
428   checkContents(vfs::recursive_directory_iterator(*O, "/", EC),
429                 ArrayRef<StringRef>());
430 
431   Lower->addRegularFile("/file1");
432   checkContents(vfs::recursive_directory_iterator(*O, "/", EC),
433                 ArrayRef<StringRef>("/file1"));
434 
435   Upper->addDirectory("/dir");
436   Upper->addRegularFile("/dir/file2");
437   checkContents(vfs::recursive_directory_iterator(*O, "/", EC),
438                 {"/dir", "/dir/file2", "/file1"});
439 
440   Lower->addDirectory("/dir1");
441   Lower->addRegularFile("/dir1/foo");
442   Lower->addDirectory("/dir1/a");
443   Lower->addRegularFile("/dir1/a/b");
444   Middle->addDirectory("/a");
445   Middle->addDirectory("/a/b");
446   Middle->addDirectory("/a/b/c");
447   Middle->addRegularFile("/a/b/c/d");
448   Middle->addRegularFile("/hiddenByUp");
449   Upper->addDirectory("/dir2");
450   Upper->addRegularFile("/dir2/foo");
451   Upper->addRegularFile("/hiddenByUp");
452   checkContents(vfs::recursive_directory_iterator(*O, "/dir2", EC),
453                 ArrayRef<StringRef>("/dir2/foo"));
454   checkContents(vfs::recursive_directory_iterator(*O, "/", EC),
455                 {"/dir", "/dir/file2", "/dir2", "/dir2/foo", "/hiddenByUp",
456                  "/a", "/a/b", "/a/b/c", "/a/b/c/d", "/dir1", "/dir1/a",
457                  "/dir1/a/b", "/dir1/foo", "/file1"});
458 }
459 
TEST(VirtualFileSystemTest,ThreeLevelIteration)460 TEST(VirtualFileSystemTest, ThreeLevelIteration) {
461   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
462   IntrusiveRefCntPtr<DummyFileSystem> Middle(new DummyFileSystem());
463   IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem());
464   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
465       new vfs::OverlayFileSystem(Lower));
466   O->pushOverlay(Middle);
467   O->pushOverlay(Upper);
468 
469   std::error_code EC;
470   checkContents(O->dir_begin("/", EC), ArrayRef<StringRef>());
471 
472   Middle->addRegularFile("/file2");
473   checkContents(O->dir_begin("/", EC), ArrayRef<StringRef>("/file2"));
474 
475   Lower->addRegularFile("/file1");
476   Upper->addRegularFile("/file3");
477   checkContents(O->dir_begin("/", EC), {"/file3", "/file2", "/file1"});
478 }
479 
TEST(VirtualFileSystemTest,HiddenInIteration)480 TEST(VirtualFileSystemTest, HiddenInIteration) {
481   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
482   IntrusiveRefCntPtr<DummyFileSystem> Middle(new DummyFileSystem());
483   IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem());
484   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
485       new vfs::OverlayFileSystem(Lower));
486   O->pushOverlay(Middle);
487   O->pushOverlay(Upper);
488 
489   std::error_code EC;
490   Lower->addRegularFile("/onlyInLow", sys::fs::owner_read);
491   Lower->addRegularFile("/hiddenByMid", sys::fs::owner_read);
492   Lower->addRegularFile("/hiddenByUp", sys::fs::owner_read);
493   Middle->addRegularFile("/onlyInMid", sys::fs::owner_write);
494   Middle->addRegularFile("/hiddenByMid", sys::fs::owner_write);
495   Middle->addRegularFile("/hiddenByUp", sys::fs::owner_write);
496   Upper->addRegularFile("/onlyInUp", sys::fs::owner_all);
497   Upper->addRegularFile("/hiddenByUp", sys::fs::owner_all);
498   checkContents(
499       O->dir_begin("/", EC),
500       {"/hiddenByUp", "/onlyInUp", "/hiddenByMid", "/onlyInMid", "/onlyInLow"});
501 
502   // Make sure we get the top-most entry
503   {
504     std::error_code EC;
505     vfs::directory_iterator I = O->dir_begin("/", EC), E;
506     for ( ; !EC && I != E; I.increment(EC))
507       if (I->getName() == "/hiddenByUp")
508         break;
509     ASSERT_NE(E, I);
510     EXPECT_EQ(sys::fs::owner_all, I->getPermissions());
511   }
512   {
513     std::error_code EC;
514     vfs::directory_iterator I = O->dir_begin("/", EC), E;
515     for ( ; !EC && I != E; I.increment(EC))
516       if (I->getName() == "/hiddenByMid")
517         break;
518     ASSERT_NE(E, I);
519     EXPECT_EQ(sys::fs::owner_write, I->getPermissions());
520   }
521 }
522 
523 class InMemoryFileSystemTest : public ::testing::Test {
524 protected:
525   clang::vfs::InMemoryFileSystem FS;
526   clang::vfs::InMemoryFileSystem NormalizedFS;
527 
InMemoryFileSystemTest()528   InMemoryFileSystemTest()
529       : FS(/*UseNormalizedPaths=*/false),
530         NormalizedFS(/*UseNormalizedPaths=*/true) {}
531 };
532 
TEST_F(InMemoryFileSystemTest,IsEmpty)533 TEST_F(InMemoryFileSystemTest, IsEmpty) {
534   auto Stat = FS.status("/a");
535   ASSERT_EQ(Stat.getError(),errc::no_such_file_or_directory) << FS.toString();
536   Stat = FS.status("/");
537   ASSERT_EQ(Stat.getError(), errc::no_such_file_or_directory) << FS.toString();
538 }
539 
TEST_F(InMemoryFileSystemTest,WindowsPath)540 TEST_F(InMemoryFileSystemTest, WindowsPath) {
541   FS.addFile("c:/windows/system128/foo.cpp", 0, MemoryBuffer::getMemBuffer(""));
542   auto Stat = FS.status("c:");
543 #if !defined(_WIN32)
544   ASSERT_FALSE(Stat.getError()) << Stat.getError() << FS.toString();
545 #endif
546   Stat = FS.status("c:/windows/system128/foo.cpp");
547   ASSERT_FALSE(Stat.getError()) << Stat.getError() << FS.toString();
548   FS.addFile("d:/windows/foo.cpp", 0, MemoryBuffer::getMemBuffer(""));
549   Stat = FS.status("d:/windows/foo.cpp");
550   ASSERT_FALSE(Stat.getError()) << Stat.getError() << FS.toString();
551 }
552 
TEST_F(InMemoryFileSystemTest,OverlayFile)553 TEST_F(InMemoryFileSystemTest, OverlayFile) {
554   FS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a"));
555   NormalizedFS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a"));
556   auto Stat = FS.status("/");
557   ASSERT_FALSE(Stat.getError()) << Stat.getError() << FS.toString();
558   Stat = FS.status("/.");
559   ASSERT_FALSE(Stat);
560   Stat = NormalizedFS.status("/.");
561   ASSERT_FALSE(Stat.getError()) << Stat.getError() << FS.toString();
562   Stat = FS.status("/a");
563   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
564   ASSERT_EQ("/a", Stat->getName());
565 }
566 
TEST_F(InMemoryFileSystemTest,OverlayFileNoOwn)567 TEST_F(InMemoryFileSystemTest, OverlayFileNoOwn) {
568   auto Buf = MemoryBuffer::getMemBuffer("a");
569   FS.addFileNoOwn("/a", 0, Buf.get());
570   auto Stat = FS.status("/a");
571   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
572   ASSERT_EQ("/a", Stat->getName());
573 }
574 
TEST_F(InMemoryFileSystemTest,OpenFileForRead)575 TEST_F(InMemoryFileSystemTest, OpenFileForRead) {
576   FS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a"));
577   FS.addFile("././c", 0, MemoryBuffer::getMemBuffer("c"));
578   FS.addFile("./d/../d", 0, MemoryBuffer::getMemBuffer("d"));
579   NormalizedFS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a"));
580   NormalizedFS.addFile("././c", 0, MemoryBuffer::getMemBuffer("c"));
581   NormalizedFS.addFile("./d/../d", 0, MemoryBuffer::getMemBuffer("d"));
582   auto File = FS.openFileForRead("/a");
583   ASSERT_EQ("a", (*(*File)->getBuffer("ignored"))->getBuffer());
584   File = FS.openFileForRead("/a"); // Open again.
585   ASSERT_EQ("a", (*(*File)->getBuffer("ignored"))->getBuffer());
586   File = NormalizedFS.openFileForRead("/././a"); // Open again.
587   ASSERT_EQ("a", (*(*File)->getBuffer("ignored"))->getBuffer());
588   File = FS.openFileForRead("/");
589   ASSERT_EQ(File.getError(), errc::invalid_argument) << FS.toString();
590   File = FS.openFileForRead("/b");
591   ASSERT_EQ(File.getError(), errc::no_such_file_or_directory) << FS.toString();
592   File = FS.openFileForRead("./c");
593   ASSERT_FALSE(File);
594   File = FS.openFileForRead("e/../d");
595   ASSERT_FALSE(File);
596   File = NormalizedFS.openFileForRead("./c");
597   ASSERT_EQ("c", (*(*File)->getBuffer("ignored"))->getBuffer());
598   File = NormalizedFS.openFileForRead("e/../d");
599   ASSERT_EQ("d", (*(*File)->getBuffer("ignored"))->getBuffer());
600 }
601 
TEST_F(InMemoryFileSystemTest,DuplicatedFile)602 TEST_F(InMemoryFileSystemTest, DuplicatedFile) {
603   ASSERT_TRUE(FS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a")));
604   ASSERT_FALSE(FS.addFile("/a/b", 0, MemoryBuffer::getMemBuffer("a")));
605   ASSERT_TRUE(FS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a")));
606   ASSERT_FALSE(FS.addFile("/a", 0, MemoryBuffer::getMemBuffer("b")));
607 }
608 
TEST_F(InMemoryFileSystemTest,DirectoryIteration)609 TEST_F(InMemoryFileSystemTest, DirectoryIteration) {
610   FS.addFile("/a", 0, MemoryBuffer::getMemBuffer(""));
611   FS.addFile("/b/c", 0, MemoryBuffer::getMemBuffer(""));
612 
613   std::error_code EC;
614   vfs::directory_iterator I = FS.dir_begin("/", EC);
615   ASSERT_FALSE(EC);
616   ASSERT_EQ("/a", I->getName());
617   I.increment(EC);
618   ASSERT_FALSE(EC);
619   ASSERT_EQ("/b", I->getName());
620   I.increment(EC);
621   ASSERT_FALSE(EC);
622   ASSERT_EQ(vfs::directory_iterator(), I);
623 
624   I = FS.dir_begin("/b", EC);
625   ASSERT_FALSE(EC);
626   ASSERT_EQ("/b/c", I->getName());
627   I.increment(EC);
628   ASSERT_FALSE(EC);
629   ASSERT_EQ(vfs::directory_iterator(), I);
630 }
631 
TEST_F(InMemoryFileSystemTest,WorkingDirectory)632 TEST_F(InMemoryFileSystemTest, WorkingDirectory) {
633   FS.setCurrentWorkingDirectory("/b");
634   FS.addFile("c", 0, MemoryBuffer::getMemBuffer(""));
635 
636   auto Stat = FS.status("/b/c");
637   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
638   ASSERT_EQ("c", Stat->getName());
639   ASSERT_EQ("/b", *FS.getCurrentWorkingDirectory());
640 
641   Stat = FS.status("c");
642   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
643 
644   auto ReplaceBackslashes = [](std::string S) {
645     std::replace(S.begin(), S.end(), '\\', '/');
646     return S;
647   };
648   NormalizedFS.setCurrentWorkingDirectory("/b/c");
649   NormalizedFS.setCurrentWorkingDirectory(".");
650   ASSERT_EQ("/b/c", ReplaceBackslashes(
651                         NormalizedFS.getCurrentWorkingDirectory().get()));
652   NormalizedFS.setCurrentWorkingDirectory("..");
653   ASSERT_EQ("/b", ReplaceBackslashes(
654                       NormalizedFS.getCurrentWorkingDirectory().get()));
655 }
656 
657 // NOTE: in the tests below, we use '//root/' as our root directory, since it is
658 // a legal *absolute* path on Windows as well as *nix.
659 class VFSFromYAMLTest : public ::testing::Test {
660 public:
661   int NumDiagnostics;
662 
SetUp()663   void SetUp() override { NumDiagnostics = 0; }
664 
CountingDiagHandler(const SMDiagnostic &,void * Context)665   static void CountingDiagHandler(const SMDiagnostic &, void *Context) {
666     VFSFromYAMLTest *Test = static_cast<VFSFromYAMLTest *>(Context);
667     ++Test->NumDiagnostics;
668   }
669 
670   IntrusiveRefCntPtr<vfs::FileSystem>
getFromYAMLRawString(StringRef Content,IntrusiveRefCntPtr<vfs::FileSystem> ExternalFS)671   getFromYAMLRawString(StringRef Content,
672                        IntrusiveRefCntPtr<vfs::FileSystem> ExternalFS) {
673     std::unique_ptr<MemoryBuffer> Buffer = MemoryBuffer::getMemBuffer(Content);
674     return getVFSFromYAML(std::move(Buffer), CountingDiagHandler, "", this,
675                           ExternalFS);
676   }
677 
getFromYAMLString(StringRef Content,IntrusiveRefCntPtr<vfs::FileSystem> ExternalFS=new DummyFileSystem ())678   IntrusiveRefCntPtr<vfs::FileSystem> getFromYAMLString(
679       StringRef Content,
680       IntrusiveRefCntPtr<vfs::FileSystem> ExternalFS = new DummyFileSystem()) {
681     std::string VersionPlusContent("{\n  'version':0,\n");
682     VersionPlusContent += Content.slice(Content.find('{') + 1, StringRef::npos);
683     return getFromYAMLRawString(VersionPlusContent, ExternalFS);
684   }
685 
686   // This is intended as a "XFAIL" for windows hosts.
supportsSameDirMultipleYAMLEntries()687   bool supportsSameDirMultipleYAMLEntries() {
688     Triple Host(Triple::normalize(sys::getProcessTriple()));
689     return !Host.isOSWindows();
690   }
691 };
692 
TEST_F(VFSFromYAMLTest,BasicVFSFromYAML)693 TEST_F(VFSFromYAMLTest, BasicVFSFromYAML) {
694   IntrusiveRefCntPtr<vfs::FileSystem> FS;
695   FS = getFromYAMLString("");
696   EXPECT_EQ(nullptr, FS.get());
697   FS = getFromYAMLString("[]");
698   EXPECT_EQ(nullptr, FS.get());
699   FS = getFromYAMLString("'string'");
700   EXPECT_EQ(nullptr, FS.get());
701   EXPECT_EQ(3, NumDiagnostics);
702 }
703 
TEST_F(VFSFromYAMLTest,MappedFiles)704 TEST_F(VFSFromYAMLTest, MappedFiles) {
705   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
706   Lower->addRegularFile("//root/foo/bar/a");
707   IntrusiveRefCntPtr<vfs::FileSystem> FS =
708       getFromYAMLString("{ 'roots': [\n"
709                         "{\n"
710                         "  'type': 'directory',\n"
711                         "  'name': '//root/',\n"
712                         "  'contents': [ {\n"
713                         "                  'type': 'file',\n"
714                         "                  'name': 'file1',\n"
715                         "                  'external-contents': '//root/foo/bar/a'\n"
716                         "                },\n"
717                         "                {\n"
718                         "                  'type': 'file',\n"
719                         "                  'name': 'file2',\n"
720                         "                  'external-contents': '//root/foo/b'\n"
721                         "                }\n"
722                         "              ]\n"
723                         "}\n"
724                         "]\n"
725                         "}",
726                         Lower);
727   ASSERT_TRUE(FS.get() != nullptr);
728 
729   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
730       new vfs::OverlayFileSystem(Lower));
731   O->pushOverlay(FS);
732 
733   // file
734   ErrorOr<vfs::Status> S = O->status("//root/file1");
735   ASSERT_FALSE(S.getError());
736   EXPECT_EQ("//root/foo/bar/a", S->getName());
737   EXPECT_TRUE(S->IsVFSMapped);
738 
739   ErrorOr<vfs::Status> SLower = O->status("//root/foo/bar/a");
740   EXPECT_EQ("//root/foo/bar/a", SLower->getName());
741   EXPECT_TRUE(S->equivalent(*SLower));
742   EXPECT_FALSE(SLower->IsVFSMapped);
743 
744   // file after opening
745   auto OpenedF = O->openFileForRead("//root/file1");
746   ASSERT_FALSE(OpenedF.getError());
747   auto OpenedS = (*OpenedF)->status();
748   ASSERT_FALSE(OpenedS.getError());
749   EXPECT_EQ("//root/foo/bar/a", OpenedS->getName());
750   EXPECT_TRUE(OpenedS->IsVFSMapped);
751 
752   // directory
753   S = O->status("//root/");
754   ASSERT_FALSE(S.getError());
755   EXPECT_TRUE(S->isDirectory());
756   EXPECT_TRUE(S->equivalent(*O->status("//root/"))); // non-volatile UniqueID
757 
758   // broken mapping
759   EXPECT_EQ(O->status("//root/file2").getError(),
760             llvm::errc::no_such_file_or_directory);
761   EXPECT_EQ(0, NumDiagnostics);
762 }
763 
TEST_F(VFSFromYAMLTest,CaseInsensitive)764 TEST_F(VFSFromYAMLTest, CaseInsensitive) {
765   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
766   Lower->addRegularFile("//root/foo/bar/a");
767   IntrusiveRefCntPtr<vfs::FileSystem> FS =
768       getFromYAMLString("{ 'case-sensitive': 'false',\n"
769                         "  'roots': [\n"
770                         "{\n"
771                         "  'type': 'directory',\n"
772                         "  'name': '//root/',\n"
773                         "  'contents': [ {\n"
774                         "                  'type': 'file',\n"
775                         "                  'name': 'XX',\n"
776                         "                  'external-contents': '//root/foo/bar/a'\n"
777                         "                }\n"
778                         "              ]\n"
779                         "}]}",
780                         Lower);
781   ASSERT_TRUE(FS.get() != nullptr);
782 
783   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
784       new vfs::OverlayFileSystem(Lower));
785   O->pushOverlay(FS);
786 
787   ErrorOr<vfs::Status> S = O->status("//root/XX");
788   ASSERT_FALSE(S.getError());
789 
790   ErrorOr<vfs::Status> SS = O->status("//root/xx");
791   ASSERT_FALSE(SS.getError());
792   EXPECT_TRUE(S->equivalent(*SS));
793   SS = O->status("//root/xX");
794   EXPECT_TRUE(S->equivalent(*SS));
795   SS = O->status("//root/Xx");
796   EXPECT_TRUE(S->equivalent(*SS));
797   EXPECT_EQ(0, NumDiagnostics);
798 }
799 
TEST_F(VFSFromYAMLTest,CaseSensitive)800 TEST_F(VFSFromYAMLTest, CaseSensitive) {
801   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
802   Lower->addRegularFile("//root/foo/bar/a");
803   IntrusiveRefCntPtr<vfs::FileSystem> FS =
804       getFromYAMLString("{ 'case-sensitive': 'true',\n"
805                         "  'roots': [\n"
806                         "{\n"
807                         "  'type': 'directory',\n"
808                         "  'name': '//root/',\n"
809                         "  'contents': [ {\n"
810                         "                  'type': 'file',\n"
811                         "                  'name': 'XX',\n"
812                         "                  'external-contents': '//root/foo/bar/a'\n"
813                         "                }\n"
814                         "              ]\n"
815                         "}]}",
816                         Lower);
817   ASSERT_TRUE(FS.get() != nullptr);
818 
819   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
820       new vfs::OverlayFileSystem(Lower));
821   O->pushOverlay(FS);
822 
823   ErrorOr<vfs::Status> SS = O->status("//root/xx");
824   EXPECT_EQ(SS.getError(), llvm::errc::no_such_file_or_directory);
825   SS = O->status("//root/xX");
826   EXPECT_EQ(SS.getError(), llvm::errc::no_such_file_or_directory);
827   SS = O->status("//root/Xx");
828   EXPECT_EQ(SS.getError(), llvm::errc::no_such_file_or_directory);
829   EXPECT_EQ(0, NumDiagnostics);
830 }
831 
TEST_F(VFSFromYAMLTest,IllegalVFSFile)832 TEST_F(VFSFromYAMLTest, IllegalVFSFile) {
833   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
834 
835   // invalid YAML at top-level
836   IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString("{]", Lower);
837   EXPECT_EQ(nullptr, FS.get());
838   // invalid YAML in roots
839   FS = getFromYAMLString("{ 'roots':[}", Lower);
840   // invalid YAML in directory
841   FS = getFromYAMLString(
842       "{ 'roots':[ { 'name': 'foo', 'type': 'directory', 'contents': [}",
843       Lower);
844   EXPECT_EQ(nullptr, FS.get());
845 
846   // invalid configuration
847   FS = getFromYAMLString("{ 'knobular': 'true', 'roots':[] }", Lower);
848   EXPECT_EQ(nullptr, FS.get());
849   FS = getFromYAMLString("{ 'case-sensitive': 'maybe', 'roots':[] }", Lower);
850   EXPECT_EQ(nullptr, FS.get());
851 
852   // invalid roots
853   FS = getFromYAMLString("{ 'roots':'' }", Lower);
854   EXPECT_EQ(nullptr, FS.get());
855   FS = getFromYAMLString("{ 'roots':{} }", Lower);
856   EXPECT_EQ(nullptr, FS.get());
857 
858   // invalid entries
859   FS = getFromYAMLString(
860       "{ 'roots':[ { 'type': 'other', 'name': 'me', 'contents': '' }", Lower);
861   EXPECT_EQ(nullptr, FS.get());
862   FS = getFromYAMLString("{ 'roots':[ { 'type': 'file', 'name': [], "
863                          "'external-contents': 'other' }",
864                          Lower);
865   EXPECT_EQ(nullptr, FS.get());
866   FS = getFromYAMLString(
867       "{ 'roots':[ { 'type': 'file', 'name': 'me', 'external-contents': [] }",
868       Lower);
869   EXPECT_EQ(nullptr, FS.get());
870   FS = getFromYAMLString(
871       "{ 'roots':[ { 'type': 'file', 'name': 'me', 'external-contents': {} }",
872       Lower);
873   EXPECT_EQ(nullptr, FS.get());
874   FS = getFromYAMLString(
875       "{ 'roots':[ { 'type': 'directory', 'name': 'me', 'contents': {} }",
876       Lower);
877   EXPECT_EQ(nullptr, FS.get());
878   FS = getFromYAMLString(
879       "{ 'roots':[ { 'type': 'directory', 'name': 'me', 'contents': '' }",
880       Lower);
881   EXPECT_EQ(nullptr, FS.get());
882   FS = getFromYAMLString(
883       "{ 'roots':[ { 'thingy': 'directory', 'name': 'me', 'contents': [] }",
884       Lower);
885   EXPECT_EQ(nullptr, FS.get());
886 
887   // missing mandatory fields
888   FS = getFromYAMLString("{ 'roots':[ { 'type': 'file', 'name': 'me' }", Lower);
889   EXPECT_EQ(nullptr, FS.get());
890   FS = getFromYAMLString(
891       "{ 'roots':[ { 'type': 'file', 'external-contents': 'other' }", Lower);
892   EXPECT_EQ(nullptr, FS.get());
893   FS = getFromYAMLString("{ 'roots':[ { 'name': 'me', 'contents': [] }", Lower);
894   EXPECT_EQ(nullptr, FS.get());
895 
896   // duplicate keys
897   FS = getFromYAMLString("{ 'roots':[], 'roots':[] }", Lower);
898   EXPECT_EQ(nullptr, FS.get());
899   FS = getFromYAMLString(
900       "{ 'case-sensitive':'true', 'case-sensitive':'true', 'roots':[] }",
901       Lower);
902   EXPECT_EQ(nullptr, FS.get());
903   FS =
904       getFromYAMLString("{ 'roots':[{'name':'me', 'name':'you', 'type':'file', "
905                         "'external-contents':'blah' } ] }",
906                         Lower);
907   EXPECT_EQ(nullptr, FS.get());
908 
909   // missing version
910   FS = getFromYAMLRawString("{ 'roots':[] }", Lower);
911   EXPECT_EQ(nullptr, FS.get());
912 
913   // bad version number
914   FS = getFromYAMLRawString("{ 'version':'foo', 'roots':[] }", Lower);
915   EXPECT_EQ(nullptr, FS.get());
916   FS = getFromYAMLRawString("{ 'version':-1, 'roots':[] }", Lower);
917   EXPECT_EQ(nullptr, FS.get());
918   FS = getFromYAMLRawString("{ 'version':100000, 'roots':[] }", Lower);
919   EXPECT_EQ(nullptr, FS.get());
920   EXPECT_EQ(24, NumDiagnostics);
921 }
922 
TEST_F(VFSFromYAMLTest,UseExternalName)923 TEST_F(VFSFromYAMLTest, UseExternalName) {
924   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
925   Lower->addRegularFile("//root/external/file");
926 
927   IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
928       "{ 'roots': [\n"
929       "  { 'type': 'file', 'name': '//root/A',\n"
930       "    'external-contents': '//root/external/file'\n"
931       "  },\n"
932       "  { 'type': 'file', 'name': '//root/B',\n"
933       "    'use-external-name': true,\n"
934       "    'external-contents': '//root/external/file'\n"
935       "  },\n"
936       "  { 'type': 'file', 'name': '//root/C',\n"
937       "    'use-external-name': false,\n"
938       "    'external-contents': '//root/external/file'\n"
939       "  }\n"
940       "] }", Lower);
941   ASSERT_TRUE(nullptr != FS.get());
942 
943   // default true
944   EXPECT_EQ("//root/external/file", FS->status("//root/A")->getName());
945   // explicit
946   EXPECT_EQ("//root/external/file", FS->status("//root/B")->getName());
947   EXPECT_EQ("//root/C", FS->status("//root/C")->getName());
948 
949   // global configuration
950   FS = getFromYAMLString(
951       "{ 'use-external-names': false,\n"
952       "  'roots': [\n"
953       "  { 'type': 'file', 'name': '//root/A',\n"
954       "    'external-contents': '//root/external/file'\n"
955       "  },\n"
956       "  { 'type': 'file', 'name': '//root/B',\n"
957       "    'use-external-name': true,\n"
958       "    'external-contents': '//root/external/file'\n"
959       "  },\n"
960       "  { 'type': 'file', 'name': '//root/C',\n"
961       "    'use-external-name': false,\n"
962       "    'external-contents': '//root/external/file'\n"
963       "  }\n"
964       "] }", Lower);
965   ASSERT_TRUE(nullptr != FS.get());
966 
967   // default
968   EXPECT_EQ("//root/A", FS->status("//root/A")->getName());
969   // explicit
970   EXPECT_EQ("//root/external/file", FS->status("//root/B")->getName());
971   EXPECT_EQ("//root/C", FS->status("//root/C")->getName());
972 }
973 
TEST_F(VFSFromYAMLTest,MultiComponentPath)974 TEST_F(VFSFromYAMLTest, MultiComponentPath) {
975   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
976   Lower->addRegularFile("//root/other");
977 
978   // file in roots
979   IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
980       "{ 'roots': [\n"
981       "  { 'type': 'file', 'name': '//root/path/to/file',\n"
982       "    'external-contents': '//root/other' }]\n"
983       "}", Lower);
984   ASSERT_TRUE(nullptr != FS.get());
985   EXPECT_FALSE(FS->status("//root/path/to/file").getError());
986   EXPECT_FALSE(FS->status("//root/path/to").getError());
987   EXPECT_FALSE(FS->status("//root/path").getError());
988   EXPECT_FALSE(FS->status("//root/").getError());
989 
990   // at the start
991   FS = getFromYAMLString(
992       "{ 'roots': [\n"
993       "  { 'type': 'directory', 'name': '//root/path/to',\n"
994       "    'contents': [ { 'type': 'file', 'name': 'file',\n"
995       "                    'external-contents': '//root/other' }]}]\n"
996       "}", Lower);
997   ASSERT_TRUE(nullptr != FS.get());
998   EXPECT_FALSE(FS->status("//root/path/to/file").getError());
999   EXPECT_FALSE(FS->status("//root/path/to").getError());
1000   EXPECT_FALSE(FS->status("//root/path").getError());
1001   EXPECT_FALSE(FS->status("//root/").getError());
1002 
1003   // at the end
1004   FS = getFromYAMLString(
1005       "{ 'roots': [\n"
1006       "  { 'type': 'directory', 'name': '//root/',\n"
1007       "    'contents': [ { 'type': 'file', 'name': 'path/to/file',\n"
1008       "                    'external-contents': '//root/other' }]}]\n"
1009       "}", Lower);
1010   ASSERT_TRUE(nullptr != FS.get());
1011   EXPECT_FALSE(FS->status("//root/path/to/file").getError());
1012   EXPECT_FALSE(FS->status("//root/path/to").getError());
1013   EXPECT_FALSE(FS->status("//root/path").getError());
1014   EXPECT_FALSE(FS->status("//root/").getError());
1015 }
1016 
TEST_F(VFSFromYAMLTest,TrailingSlashes)1017 TEST_F(VFSFromYAMLTest, TrailingSlashes) {
1018   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
1019   Lower->addRegularFile("//root/other");
1020 
1021   // file in roots
1022   IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
1023       "{ 'roots': [\n"
1024       "  { 'type': 'directory', 'name': '//root/path/to////',\n"
1025       "    'contents': [ { 'type': 'file', 'name': 'file',\n"
1026       "                    'external-contents': '//root/other' }]}]\n"
1027       "}", Lower);
1028   ASSERT_TRUE(nullptr != FS.get());
1029   EXPECT_FALSE(FS->status("//root/path/to/file").getError());
1030   EXPECT_FALSE(FS->status("//root/path/to").getError());
1031   EXPECT_FALSE(FS->status("//root/path").getError());
1032   EXPECT_FALSE(FS->status("//root/").getError());
1033 }
1034 
TEST_F(VFSFromYAMLTest,DirectoryIteration)1035 TEST_F(VFSFromYAMLTest, DirectoryIteration) {
1036   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
1037   Lower->addDirectory("//root/");
1038   Lower->addDirectory("//root/foo");
1039   Lower->addDirectory("//root/foo/bar");
1040   Lower->addRegularFile("//root/foo/bar/a");
1041   Lower->addRegularFile("//root/foo/bar/b");
1042   Lower->addRegularFile("//root/file3");
1043   IntrusiveRefCntPtr<vfs::FileSystem> FS =
1044   getFromYAMLString("{ 'use-external-names': false,\n"
1045                     "  'roots': [\n"
1046                     "{\n"
1047                     "  'type': 'directory',\n"
1048                     "  'name': '//root/',\n"
1049                     "  'contents': [ {\n"
1050                     "                  'type': 'file',\n"
1051                     "                  'name': 'file1',\n"
1052                     "                  'external-contents': '//root/foo/bar/a'\n"
1053                     "                },\n"
1054                     "                {\n"
1055                     "                  'type': 'file',\n"
1056                     "                  'name': 'file2',\n"
1057                     "                  'external-contents': '//root/foo/bar/b'\n"
1058                     "                }\n"
1059                     "              ]\n"
1060                     "}\n"
1061                     "]\n"
1062                     "}",
1063                     Lower);
1064   ASSERT_TRUE(FS.get() != nullptr);
1065 
1066   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
1067       new vfs::OverlayFileSystem(Lower));
1068   O->pushOverlay(FS);
1069 
1070   std::error_code EC;
1071   checkContents(O->dir_begin("//root/", EC),
1072                 {"//root/file1", "//root/file2", "//root/file3", "//root/foo"});
1073 
1074   checkContents(O->dir_begin("//root/foo/bar", EC),
1075                 {"//root/foo/bar/a", "//root/foo/bar/b"});
1076 }
1077 
TEST_F(VFSFromYAMLTest,DirectoryIterationSameDirMultipleEntries)1078 TEST_F(VFSFromYAMLTest, DirectoryIterationSameDirMultipleEntries) {
1079   // https://llvm.org/bugs/show_bug.cgi?id=27725
1080   if (!supportsSameDirMultipleYAMLEntries())
1081     return;
1082 
1083   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
1084   Lower->addDirectory("//root/zab");
1085   Lower->addDirectory("//root/baz");
1086   Lower->addRegularFile("//root/zab/a");
1087   Lower->addRegularFile("//root/zab/b");
1088   IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
1089       "{ 'use-external-names': false,\n"
1090       "  'roots': [\n"
1091       "{\n"
1092       "  'type': 'directory',\n"
1093       "  'name': '//root/baz/',\n"
1094       "  'contents': [ {\n"
1095       "                  'type': 'file',\n"
1096       "                  'name': 'x',\n"
1097       "                  'external-contents': '//root/zab/a'\n"
1098       "                }\n"
1099       "              ]\n"
1100       "},\n"
1101       "{\n"
1102       "  'type': 'directory',\n"
1103       "  'name': '//root/baz/',\n"
1104       "  'contents': [ {\n"
1105       "                  'type': 'file',\n"
1106       "                  'name': 'y',\n"
1107       "                  'external-contents': '//root/zab/b'\n"
1108       "                }\n"
1109       "              ]\n"
1110       "}\n"
1111       "]\n"
1112       "}",
1113       Lower);
1114   ASSERT_TRUE(FS.get() != nullptr);
1115 
1116   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
1117       new vfs::OverlayFileSystem(Lower));
1118   O->pushOverlay(FS);
1119 
1120   std::error_code EC;
1121 
1122   checkContents(O->dir_begin("//root/baz/", EC),
1123                 {"//root/baz/x", "//root/baz/y"});
1124 }
1125 
TEST_F(VFSFromYAMLTest,RecursiveDirectoryIterationLevel)1126 TEST_F(VFSFromYAMLTest, RecursiveDirectoryIterationLevel) {
1127 
1128   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
1129   Lower->addDirectory("//root/a");
1130   Lower->addDirectory("//root/a/b");
1131   Lower->addDirectory("//root/a/b/c");
1132   Lower->addRegularFile("//root/a/b/c/file");
1133   IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
1134       "{ 'use-external-names': false,\n"
1135       "  'roots': [\n"
1136       "{\n"
1137       "  'type': 'directory',\n"
1138       "  'name': '//root/a/b/c/',\n"
1139       "  'contents': [ {\n"
1140       "                  'type': 'file',\n"
1141       "                  'name': 'file',\n"
1142       "                  'external-contents': '//root/a/b/c/file'\n"
1143       "                }\n"
1144       "              ]\n"
1145       "},\n"
1146       "]\n"
1147       "}",
1148       Lower);
1149   ASSERT_TRUE(FS.get() != nullptr);
1150 
1151   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
1152       new vfs::OverlayFileSystem(Lower));
1153   O->pushOverlay(FS);
1154 
1155   std::error_code EC;
1156 
1157   // Test recursive_directory_iterator level()
1158   vfs::recursive_directory_iterator I = vfs::recursive_directory_iterator(
1159                                         *O, "//root", EC), E;
1160   ASSERT_FALSE(EC);
1161   for (int l = 0; I != E; I.increment(EC), ++l) {
1162     ASSERT_FALSE(EC);
1163     EXPECT_EQ(I.level(), l);
1164   }
1165   EXPECT_EQ(I, E);
1166 }
1167