• 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/Support/Errc.h"
12 #include "llvm/Support/MemoryBuffer.h"
13 #include "llvm/Support/Path.h"
14 #include "llvm/Support/SourceMgr.h"
15 #include "gtest/gtest.h"
16 #include <map>
17 using namespace clang;
18 using namespace llvm;
19 using llvm::sys::fs::UniqueID;
20 
21 namespace {
22 class DummyFileSystem : public vfs::FileSystem {
23   int FSID;   // used to produce UniqueIDs
24   int FileID; // used to produce UniqueIDs
25   std::map<std::string, vfs::Status> FilesAndDirs;
26 
getNextFSID()27   static int getNextFSID() {
28     static int Count = 0;
29     return Count++;
30   }
31 
32 public:
DummyFileSystem()33   DummyFileSystem() : FSID(getNextFSID()), FileID(0) {}
34 
status(const Twine & Path)35   ErrorOr<vfs::Status> status(const Twine &Path) {
36     std::map<std::string, vfs::Status>::iterator I =
37         FilesAndDirs.find(Path.str());
38     if (I == FilesAndDirs.end())
39       return make_error_code(llvm::errc::no_such_file_or_directory);
40     return I->second;
41   }
openFileForRead(const Twine & Path,std::unique_ptr<vfs::File> & Result)42   std::error_code openFileForRead(const Twine &Path,
43                                   std::unique_ptr<vfs::File> &Result) {
44     llvm_unreachable("unimplemented");
45   }
getBufferForFile(const Twine & Name,std::unique_ptr<MemoryBuffer> & Result,int64_t FileSize=-1,bool RequiresNullTerminator=true)46   std::error_code getBufferForFile(const Twine &Name,
47                                    std::unique_ptr<MemoryBuffer> &Result,
48                                    int64_t FileSize = -1,
49                                    bool RequiresNullTerminator = true) {
50     llvm_unreachable("unimplemented");
51   }
52 
53   struct DirIterImpl : public clang::vfs::detail::DirIterImpl {
54     std::map<std::string, vfs::Status> &FilesAndDirs;
55     std::map<std::string, vfs::Status>::iterator I;
56     std::string Path;
isInPath__anon0a6ea1590111::DummyFileSystem::DirIterImpl57     bool isInPath(StringRef S) {
58       if (Path.size() < S.size() && S.find(Path) == 0) {
59         auto LastSep = S.find_last_of('/');
60         if (LastSep == Path.size() || LastSep == Path.size()-1)
61           return true;
62       }
63       return false;
64     }
DirIterImpl__anon0a6ea1590111::DummyFileSystem::DirIterImpl65     DirIterImpl(std::map<std::string, vfs::Status> &FilesAndDirs,
66                 const Twine &_Path)
67         : FilesAndDirs(FilesAndDirs), I(FilesAndDirs.begin()),
68           Path(_Path.str()) {
69       for ( ; I != FilesAndDirs.end(); ++I) {
70         if (isInPath(I->first)) {
71           CurrentEntry = I->second;
72           break;
73         }
74       }
75     }
increment__anon0a6ea1590111::DummyFileSystem::DirIterImpl76     std::error_code increment() override {
77       ++I;
78       for ( ; I != FilesAndDirs.end(); ++I) {
79         if (isInPath(I->first)) {
80           CurrentEntry = I->second;
81           break;
82         }
83       }
84       if (I == FilesAndDirs.end())
85         CurrentEntry = vfs::Status();
86       return std::error_code();
87     }
88   };
89 
dir_begin(const Twine & Dir,std::error_code & EC)90   vfs::directory_iterator dir_begin(const Twine &Dir,
91                                     std::error_code &EC) override {
92     return vfs::directory_iterator(
93         std::make_shared<DirIterImpl>(FilesAndDirs, Dir));
94   }
95 
addEntry(StringRef Path,const vfs::Status & Status)96   void addEntry(StringRef Path, const vfs::Status &Status) {
97     FilesAndDirs[Path] = Status;
98   }
99 
addRegularFile(StringRef Path,sys::fs::perms Perms=sys::fs::all_all)100   void addRegularFile(StringRef Path, sys::fs::perms Perms = sys::fs::all_all) {
101     vfs::Status S(Path, Path, UniqueID(FSID, FileID++), sys::TimeValue::now(),
102                   0, 0, 1024, sys::fs::file_type::regular_file, Perms);
103     addEntry(Path, S);
104   }
105 
addDirectory(StringRef Path,sys::fs::perms Perms=sys::fs::all_all)106   void addDirectory(StringRef Path, sys::fs::perms Perms = sys::fs::all_all) {
107     vfs::Status S(Path, Path, UniqueID(FSID, FileID++), sys::TimeValue::now(),
108                   0, 0, 0, sys::fs::file_type::directory_file, Perms);
109     addEntry(Path, S);
110   }
111 
addSymlink(StringRef Path)112   void addSymlink(StringRef Path) {
113     vfs::Status S(Path, Path, UniqueID(FSID, FileID++), sys::TimeValue::now(),
114                   0, 0, 0, sys::fs::file_type::symlink_file, sys::fs::all_all);
115     addEntry(Path, S);
116   }
117 };
118 } // end anonymous namespace
119 
TEST(VirtualFileSystemTest,StatusQueries)120 TEST(VirtualFileSystemTest, StatusQueries) {
121   IntrusiveRefCntPtr<DummyFileSystem> D(new DummyFileSystem());
122   ErrorOr<vfs::Status> Status((std::error_code()));
123 
124   D->addRegularFile("/foo");
125   Status = D->status("/foo");
126   ASSERT_FALSE(Status.getError());
127   EXPECT_TRUE(Status->isStatusKnown());
128   EXPECT_FALSE(Status->isDirectory());
129   EXPECT_TRUE(Status->isRegularFile());
130   EXPECT_FALSE(Status->isSymlink());
131   EXPECT_FALSE(Status->isOther());
132   EXPECT_TRUE(Status->exists());
133 
134   D->addDirectory("/bar");
135   Status = D->status("/bar");
136   ASSERT_FALSE(Status.getError());
137   EXPECT_TRUE(Status->isStatusKnown());
138   EXPECT_TRUE(Status->isDirectory());
139   EXPECT_FALSE(Status->isRegularFile());
140   EXPECT_FALSE(Status->isSymlink());
141   EXPECT_FALSE(Status->isOther());
142   EXPECT_TRUE(Status->exists());
143 
144   D->addSymlink("/baz");
145   Status = D->status("/baz");
146   ASSERT_FALSE(Status.getError());
147   EXPECT_TRUE(Status->isStatusKnown());
148   EXPECT_FALSE(Status->isDirectory());
149   EXPECT_FALSE(Status->isRegularFile());
150   EXPECT_TRUE(Status->isSymlink());
151   EXPECT_FALSE(Status->isOther());
152   EXPECT_TRUE(Status->exists());
153 
154   EXPECT_TRUE(Status->equivalent(*Status));
155   ErrorOr<vfs::Status> Status2 = D->status("/foo");
156   ASSERT_FALSE(Status2.getError());
157   EXPECT_FALSE(Status->equivalent(*Status2));
158 }
159 
TEST(VirtualFileSystemTest,BaseOnlyOverlay)160 TEST(VirtualFileSystemTest, BaseOnlyOverlay) {
161   IntrusiveRefCntPtr<DummyFileSystem> D(new DummyFileSystem());
162   ErrorOr<vfs::Status> Status((std::error_code()));
163   EXPECT_FALSE(Status = D->status("/foo"));
164 
165   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(new vfs::OverlayFileSystem(D));
166   EXPECT_FALSE(Status = O->status("/foo"));
167 
168   D->addRegularFile("/foo");
169   Status = D->status("/foo");
170   EXPECT_FALSE(Status.getError());
171 
172   ErrorOr<vfs::Status> Status2((std::error_code()));
173   Status2 = O->status("/foo");
174   EXPECT_FALSE(Status2.getError());
175   EXPECT_TRUE(Status->equivalent(*Status2));
176 }
177 
TEST(VirtualFileSystemTest,OverlayFiles)178 TEST(VirtualFileSystemTest, OverlayFiles) {
179   IntrusiveRefCntPtr<DummyFileSystem> Base(new DummyFileSystem());
180   IntrusiveRefCntPtr<DummyFileSystem> Middle(new DummyFileSystem());
181   IntrusiveRefCntPtr<DummyFileSystem> Top(new DummyFileSystem());
182   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
183       new vfs::OverlayFileSystem(Base));
184   O->pushOverlay(Middle);
185   O->pushOverlay(Top);
186 
187   ErrorOr<vfs::Status> Status1((std::error_code())),
188       Status2((std::error_code())), Status3((std::error_code())),
189       StatusB((std::error_code())), StatusM((std::error_code())),
190       StatusT((std::error_code()));
191 
192   Base->addRegularFile("/foo");
193   StatusB = Base->status("/foo");
194   ASSERT_FALSE(StatusB.getError());
195   Status1 = O->status("/foo");
196   ASSERT_FALSE(Status1.getError());
197   Middle->addRegularFile("/foo");
198   StatusM = Middle->status("/foo");
199   ASSERT_FALSE(StatusM.getError());
200   Status2 = O->status("/foo");
201   ASSERT_FALSE(Status2.getError());
202   Top->addRegularFile("/foo");
203   StatusT = Top->status("/foo");
204   ASSERT_FALSE(StatusT.getError());
205   Status3 = O->status("/foo");
206   ASSERT_FALSE(Status3.getError());
207 
208   EXPECT_TRUE(Status1->equivalent(*StatusB));
209   EXPECT_TRUE(Status2->equivalent(*StatusM));
210   EXPECT_TRUE(Status3->equivalent(*StatusT));
211 
212   EXPECT_FALSE(Status1->equivalent(*Status2));
213   EXPECT_FALSE(Status2->equivalent(*Status3));
214   EXPECT_FALSE(Status1->equivalent(*Status3));
215 }
216 
TEST(VirtualFileSystemTest,OverlayDirsNonMerged)217 TEST(VirtualFileSystemTest, OverlayDirsNonMerged) {
218   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
219   IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem());
220   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
221       new vfs::OverlayFileSystem(Lower));
222   O->pushOverlay(Upper);
223 
224   Lower->addDirectory("/lower-only");
225   Upper->addDirectory("/upper-only");
226 
227   // non-merged paths should be the same
228   ErrorOr<vfs::Status> Status1 = Lower->status("/lower-only");
229   ASSERT_FALSE(Status1.getError());
230   ErrorOr<vfs::Status> Status2 = O->status("/lower-only");
231   ASSERT_FALSE(Status2.getError());
232   EXPECT_TRUE(Status1->equivalent(*Status2));
233 
234   Status1 = Upper->status("/upper-only");
235   ASSERT_FALSE(Status1.getError());
236   Status2 = O->status("/upper-only");
237   ASSERT_FALSE(Status2.getError());
238   EXPECT_TRUE(Status1->equivalent(*Status2));
239 }
240 
TEST(VirtualFileSystemTest,MergedDirPermissions)241 TEST(VirtualFileSystemTest, MergedDirPermissions) {
242   // merged directories get the permissions of the upper dir
243   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
244   IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem());
245   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
246       new vfs::OverlayFileSystem(Lower));
247   O->pushOverlay(Upper);
248 
249   ErrorOr<vfs::Status> Status((std::error_code()));
250   Lower->addDirectory("/both", sys::fs::owner_read);
251   Upper->addDirectory("/both", sys::fs::owner_all | sys::fs::group_read);
252   Status = O->status("/both");
253   ASSERT_FALSE(Status.getError());
254   EXPECT_EQ(0740, Status->getPermissions());
255 
256   // permissions (as usual) are not recursively applied
257   Lower->addRegularFile("/both/foo", sys::fs::owner_read);
258   Upper->addRegularFile("/both/bar", sys::fs::owner_write);
259   Status = O->status("/both/foo");
260   ASSERT_FALSE( Status.getError());
261   EXPECT_EQ(0400, Status->getPermissions());
262   Status = O->status("/both/bar");
263   ASSERT_FALSE(Status.getError());
264   EXPECT_EQ(0200, Status->getPermissions());
265 }
266 
267 namespace {
268 struct ScopedDir {
269   SmallString<128> Path;
ScopedDir__anon0a6ea1590211::ScopedDir270   ScopedDir(const Twine &Name, bool Unique=false) {
271     std::error_code EC;
272     if (Unique) {
273       EC =  llvm::sys::fs::createUniqueDirectory(Name, Path);
274     } else {
275       Path = Name.str();
276       EC = llvm::sys::fs::create_directory(Twine(Path));
277     }
278     if (EC)
279       Path = "";
280     EXPECT_FALSE(EC);
281   }
~ScopedDir__anon0a6ea1590211::ScopedDir282   ~ScopedDir() {
283     if (Path != "")
284       EXPECT_FALSE(llvm::sys::fs::remove(Path.str()));
285   }
operator StringRef__anon0a6ea1590211::ScopedDir286   operator StringRef() { return Path.str(); }
287 };
288 }
289 
TEST(VirtualFileSystemTest,BasicRealFSIteration)290 TEST(VirtualFileSystemTest, BasicRealFSIteration) {
291   ScopedDir TestDirectory("virtual-file-system-test", /*Unique*/true);
292   IntrusiveRefCntPtr<vfs::FileSystem> FS = vfs::getRealFileSystem();
293 
294   std::error_code EC;
295   vfs::directory_iterator I = FS->dir_begin(Twine(TestDirectory), EC);
296   ASSERT_FALSE(EC);
297   EXPECT_EQ(vfs::directory_iterator(), I); // empty directory is empty
298 
299   ScopedDir _a(TestDirectory+"/a");
300   ScopedDir _ab(TestDirectory+"/a/b");
301   ScopedDir _c(TestDirectory+"/c");
302   ScopedDir _cd(TestDirectory+"/c/d");
303 
304   I = FS->dir_begin(Twine(TestDirectory), EC);
305   ASSERT_FALSE(EC);
306   ASSERT_NE(vfs::directory_iterator(), I);
307   // Check either a or c, since we can't rely on the iteration order.
308   EXPECT_TRUE(I->getName().endswith("a") || I->getName().endswith("c"));
309   I.increment(EC);
310   ASSERT_FALSE(EC);
311   ASSERT_NE(vfs::directory_iterator(), I);
312   EXPECT_TRUE(I->getName().endswith("a") || I->getName().endswith("c"));
313   I.increment(EC);
314   EXPECT_EQ(vfs::directory_iterator(), I);
315 }
316 
TEST(VirtualFileSystemTest,BasicRealFSRecursiveIteration)317 TEST(VirtualFileSystemTest, BasicRealFSRecursiveIteration) {
318   ScopedDir TestDirectory("virtual-file-system-test", /*Unique*/true);
319   IntrusiveRefCntPtr<vfs::FileSystem> FS = vfs::getRealFileSystem();
320 
321   std::error_code EC;
322   auto I = vfs::recursive_directory_iterator(*FS, Twine(TestDirectory), EC);
323   ASSERT_FALSE(EC);
324   EXPECT_EQ(vfs::recursive_directory_iterator(), I); // empty directory is empty
325 
326   ScopedDir _a(TestDirectory+"/a");
327   ScopedDir _ab(TestDirectory+"/a/b");
328   ScopedDir _c(TestDirectory+"/c");
329   ScopedDir _cd(TestDirectory+"/c/d");
330 
331   I = vfs::recursive_directory_iterator(*FS, Twine(TestDirectory), EC);
332   ASSERT_FALSE(EC);
333   ASSERT_NE(vfs::recursive_directory_iterator(), I);
334 
335 
336   std::vector<std::string> Contents;
337   for (auto E = vfs::recursive_directory_iterator(); !EC && I != E;
338        I.increment(EC)) {
339     Contents.push_back(I->getName());
340   }
341 
342   // Check contents, which may be in any order
343   EXPECT_EQ(4U, Contents.size());
344   int Counts[4] = { 0, 0, 0, 0 };
345   for (const std::string &Name : Contents) {
346     ASSERT_FALSE(Name.empty());
347     int Index = Name[Name.size()-1] - 'a';
348     ASSERT_TRUE(Index >= 0 && Index < 4);
349     Counts[Index]++;
350   }
351   EXPECT_EQ(1, Counts[0]); // a
352   EXPECT_EQ(1, Counts[1]); // b
353   EXPECT_EQ(1, Counts[2]); // c
354   EXPECT_EQ(1, Counts[3]); // d
355 }
356 
357 template <typename T, size_t N>
makeStringRefVector(const T (& Arr)[N])358 std::vector<StringRef> makeStringRefVector(const T (&Arr)[N]) {
359   std::vector<StringRef> Vec;
360   for (size_t i = 0; i != N; ++i)
361     Vec.push_back(Arr[i]);
362   return Vec;
363 }
364 
365 template <typename DirIter>
checkContents(DirIter I,ArrayRef<StringRef> Expected)366 static void checkContents(DirIter I, ArrayRef<StringRef> Expected) {
367   std::error_code EC;
368   auto ExpectedIter = Expected.begin(), ExpectedEnd = Expected.end();
369   for (DirIter E;
370        !EC && I != E && ExpectedIter != ExpectedEnd;
371        I.increment(EC), ++ExpectedIter)
372     EXPECT_EQ(*ExpectedIter, I->getName());
373 
374   EXPECT_EQ(ExpectedEnd, ExpectedIter);
375   EXPECT_EQ(DirIter(), I);
376 }
377 
TEST(VirtualFileSystemTest,OverlayIteration)378 TEST(VirtualFileSystemTest, OverlayIteration) {
379   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
380   IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem());
381   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
382       new vfs::OverlayFileSystem(Lower));
383   O->pushOverlay(Upper);
384 
385   std::error_code EC;
386   checkContents(O->dir_begin("/", EC), ArrayRef<StringRef>());
387 
388   Lower->addRegularFile("/file1");
389   checkContents(O->dir_begin("/", EC), ArrayRef<StringRef>("/file1"));
390 
391   Upper->addRegularFile("/file2");
392   {
393     const char *Contents[] = {"/file2", "/file1"};
394     checkContents(O->dir_begin("/", EC), makeStringRefVector(Contents));
395   }
396 
397   Lower->addDirectory("/dir1");
398   Lower->addRegularFile("/dir1/foo");
399   Upper->addDirectory("/dir2");
400   Upper->addRegularFile("/dir2/foo");
401   checkContents(O->dir_begin("/dir2", EC), ArrayRef<StringRef>("/dir2/foo"));
402   {
403     const char *Contents[] = {"/dir2", "/file2", "/dir1", "/file1"};
404     checkContents(O->dir_begin("/", EC), makeStringRefVector(Contents));
405   }
406 }
407 
TEST(VirtualFileSystemTest,OverlayRecursiveIteration)408 TEST(VirtualFileSystemTest, OverlayRecursiveIteration) {
409   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
410   IntrusiveRefCntPtr<DummyFileSystem> Middle(new DummyFileSystem());
411   IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem());
412   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
413       new vfs::OverlayFileSystem(Lower));
414   O->pushOverlay(Middle);
415   O->pushOverlay(Upper);
416 
417   std::error_code EC;
418   checkContents(vfs::recursive_directory_iterator(*O, "/", EC),
419                 ArrayRef<StringRef>());
420 
421   Lower->addRegularFile("/file1");
422   checkContents(vfs::recursive_directory_iterator(*O, "/", EC),
423                 ArrayRef<StringRef>("/file1"));
424 
425   Upper->addDirectory("/dir");
426   Upper->addRegularFile("/dir/file2");
427   {
428     const char *Contents[] = {"/dir", "/dir/file2", "/file1"};
429     checkContents(vfs::recursive_directory_iterator(*O, "/", EC),
430                   makeStringRefVector(Contents));
431   }
432 
433   Lower->addDirectory("/dir1");
434   Lower->addRegularFile("/dir1/foo");
435   Lower->addDirectory("/dir1/a");
436   Lower->addRegularFile("/dir1/a/b");
437   Middle->addDirectory("/a");
438   Middle->addDirectory("/a/b");
439   Middle->addDirectory("/a/b/c");
440   Middle->addRegularFile("/a/b/c/d");
441   Middle->addRegularFile("/hiddenByUp");
442   Upper->addDirectory("/dir2");
443   Upper->addRegularFile("/dir2/foo");
444   Upper->addRegularFile("/hiddenByUp");
445   checkContents(vfs::recursive_directory_iterator(*O, "/dir2", EC),
446                 ArrayRef<StringRef>("/dir2/foo"));
447   {
448     const char *Contents[] = { "/dir", "/dir/file2", "/dir2", "/dir2/foo",
449         "/hiddenByUp", "/a", "/a/b", "/a/b/c", "/a/b/c/d", "/dir1", "/dir1/a",
450         "/dir1/a/b", "/dir1/foo", "/file1" };
451     checkContents(vfs::recursive_directory_iterator(*O, "/", EC),
452                   makeStringRefVector(Contents));
453   }
454 }
455 
TEST(VirtualFileSystemTest,ThreeLevelIteration)456 TEST(VirtualFileSystemTest, ThreeLevelIteration) {
457   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
458   IntrusiveRefCntPtr<DummyFileSystem> Middle(new DummyFileSystem());
459   IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem());
460   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
461       new vfs::OverlayFileSystem(Lower));
462   O->pushOverlay(Middle);
463   O->pushOverlay(Upper);
464 
465   std::error_code EC;
466   checkContents(O->dir_begin("/", EC), ArrayRef<StringRef>());
467 
468   Middle->addRegularFile("/file2");
469   checkContents(O->dir_begin("/", EC), ArrayRef<StringRef>("/file2"));
470 
471   Lower->addRegularFile("/file1");
472   Upper->addRegularFile("/file3");
473   {
474     const char *Contents[] = {"/file3", "/file2", "/file1"};
475     checkContents(O->dir_begin("/", EC), makeStringRefVector(Contents));
476   }
477 }
478 
TEST(VirtualFileSystemTest,HiddenInIteration)479 TEST(VirtualFileSystemTest, HiddenInIteration) {
480   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
481   IntrusiveRefCntPtr<DummyFileSystem> Middle(new DummyFileSystem());
482   IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem());
483   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
484       new vfs::OverlayFileSystem(Lower));
485   O->pushOverlay(Middle);
486   O->pushOverlay(Upper);
487 
488   std::error_code EC;
489   Lower->addRegularFile("/onlyInLow", sys::fs::owner_read);
490   Lower->addRegularFile("/hiddenByMid", sys::fs::owner_read);
491   Lower->addRegularFile("/hiddenByUp", sys::fs::owner_read);
492   Middle->addRegularFile("/onlyInMid", sys::fs::owner_write);
493   Middle->addRegularFile("/hiddenByMid", sys::fs::owner_write);
494   Middle->addRegularFile("/hiddenByUp", sys::fs::owner_write);
495   Upper->addRegularFile("/onlyInUp", sys::fs::owner_all);
496   Upper->addRegularFile("/hiddenByUp", sys::fs::owner_all);
497   {
498     const char *Contents[] = {"/hiddenByUp", "/onlyInUp", "/hiddenByMid",
499                               "/onlyInMid", "/onlyInLow"};
500     checkContents(O->dir_begin("/", EC), makeStringRefVector(Contents));
501   }
502 
503   // Make sure we get the top-most entry
504   {
505     std::error_code EC;
506     vfs::directory_iterator I = O->dir_begin("/", EC), E;
507     for ( ; !EC && I != E; I.increment(EC))
508       if (I->getName() == "/hiddenByUp")
509         break;
510     ASSERT_NE(E, I);
511     EXPECT_EQ(sys::fs::owner_all, I->getPermissions());
512   }
513   {
514     std::error_code EC;
515     vfs::directory_iterator I = O->dir_begin("/", EC), E;
516     for ( ; !EC && I != E; I.increment(EC))
517       if (I->getName() == "/hiddenByMid")
518         break;
519     ASSERT_NE(E, I);
520     EXPECT_EQ(sys::fs::owner_write, I->getPermissions());
521   }
522 }
523 
524 // NOTE: in the tests below, we use '//root/' as our root directory, since it is
525 // a legal *absolute* path on Windows as well as *nix.
526 class VFSFromYAMLTest : public ::testing::Test {
527 public:
528   int NumDiagnostics;
529 
SetUp()530   void SetUp() {
531     NumDiagnostics = 0;
532   }
533 
CountingDiagHandler(const SMDiagnostic &,void * Context)534   static void CountingDiagHandler(const SMDiagnostic &, void *Context) {
535     VFSFromYAMLTest *Test = static_cast<VFSFromYAMLTest *>(Context);
536     ++Test->NumDiagnostics;
537   }
538 
539   IntrusiveRefCntPtr<vfs::FileSystem>
getFromYAMLRawString(StringRef Content,IntrusiveRefCntPtr<vfs::FileSystem> ExternalFS)540   getFromYAMLRawString(StringRef Content,
541                        IntrusiveRefCntPtr<vfs::FileSystem> ExternalFS) {
542     MemoryBuffer *Buffer = MemoryBuffer::getMemBuffer(Content);
543     return getVFSFromYAML(Buffer, CountingDiagHandler, this, ExternalFS);
544   }
545 
getFromYAMLString(StringRef Content,IntrusiveRefCntPtr<vfs::FileSystem> ExternalFS=new DummyFileSystem ())546   IntrusiveRefCntPtr<vfs::FileSystem> getFromYAMLString(
547       StringRef Content,
548       IntrusiveRefCntPtr<vfs::FileSystem> ExternalFS = new DummyFileSystem()) {
549     std::string VersionPlusContent("{\n  'version':0,\n");
550     VersionPlusContent += Content.slice(Content.find('{') + 1, StringRef::npos);
551     return getFromYAMLRawString(VersionPlusContent, ExternalFS);
552   }
553 };
554 
TEST_F(VFSFromYAMLTest,BasicVFSFromYAML)555 TEST_F(VFSFromYAMLTest, BasicVFSFromYAML) {
556   IntrusiveRefCntPtr<vfs::FileSystem> FS;
557   FS = getFromYAMLString("");
558   EXPECT_EQ(nullptr, FS.get());
559   FS = getFromYAMLString("[]");
560   EXPECT_EQ(nullptr, FS.get());
561   FS = getFromYAMLString("'string'");
562   EXPECT_EQ(nullptr, FS.get());
563   EXPECT_EQ(3, NumDiagnostics);
564 }
565 
TEST_F(VFSFromYAMLTest,MappedFiles)566 TEST_F(VFSFromYAMLTest, MappedFiles) {
567   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
568   Lower->addRegularFile("//root/foo/bar/a");
569   IntrusiveRefCntPtr<vfs::FileSystem> FS =
570       getFromYAMLString("{ 'roots': [\n"
571                         "{\n"
572                         "  'type': 'directory',\n"
573                         "  'name': '//root/',\n"
574                         "  'contents': [ {\n"
575                         "                  'type': 'file',\n"
576                         "                  'name': 'file1',\n"
577                         "                  'external-contents': '//root/foo/bar/a'\n"
578                         "                },\n"
579                         "                {\n"
580                         "                  'type': 'file',\n"
581                         "                  'name': 'file2',\n"
582                         "                  'external-contents': '//root/foo/b'\n"
583                         "                }\n"
584                         "              ]\n"
585                         "}\n"
586                         "]\n"
587                         "}",
588                         Lower);
589   ASSERT_TRUE(FS.get() != nullptr);
590 
591   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
592       new vfs::OverlayFileSystem(Lower));
593   O->pushOverlay(FS);
594 
595   // file
596   ErrorOr<vfs::Status> S = O->status("//root/file1");
597   ASSERT_FALSE(S.getError());
598   EXPECT_EQ("//root/foo/bar/a", S->getName());
599 
600   ErrorOr<vfs::Status> SLower = O->status("//root/foo/bar/a");
601   EXPECT_EQ("//root/foo/bar/a", SLower->getName());
602   EXPECT_TRUE(S->equivalent(*SLower));
603 
604   // directory
605   S = O->status("//root/");
606   ASSERT_FALSE(S.getError());
607   EXPECT_TRUE(S->isDirectory());
608   EXPECT_TRUE(S->equivalent(*O->status("//root/"))); // non-volatile UniqueID
609 
610   // broken mapping
611   EXPECT_EQ(O->status("//root/file2").getError(),
612             llvm::errc::no_such_file_or_directory);
613   EXPECT_EQ(0, NumDiagnostics);
614 }
615 
TEST_F(VFSFromYAMLTest,CaseInsensitive)616 TEST_F(VFSFromYAMLTest, CaseInsensitive) {
617   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
618   Lower->addRegularFile("//root/foo/bar/a");
619   IntrusiveRefCntPtr<vfs::FileSystem> FS =
620       getFromYAMLString("{ 'case-sensitive': 'false',\n"
621                         "  'roots': [\n"
622                         "{\n"
623                         "  'type': 'directory',\n"
624                         "  'name': '//root/',\n"
625                         "  'contents': [ {\n"
626                         "                  'type': 'file',\n"
627                         "                  'name': 'XX',\n"
628                         "                  'external-contents': '//root/foo/bar/a'\n"
629                         "                }\n"
630                         "              ]\n"
631                         "}]}",
632                         Lower);
633   ASSERT_TRUE(FS.get() != nullptr);
634 
635   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
636       new vfs::OverlayFileSystem(Lower));
637   O->pushOverlay(FS);
638 
639   ErrorOr<vfs::Status> S = O->status("//root/XX");
640   ASSERT_FALSE(S.getError());
641 
642   ErrorOr<vfs::Status> SS = O->status("//root/xx");
643   ASSERT_FALSE(SS.getError());
644   EXPECT_TRUE(S->equivalent(*SS));
645   SS = O->status("//root/xX");
646   EXPECT_TRUE(S->equivalent(*SS));
647   SS = O->status("//root/Xx");
648   EXPECT_TRUE(S->equivalent(*SS));
649   EXPECT_EQ(0, NumDiagnostics);
650 }
651 
TEST_F(VFSFromYAMLTest,CaseSensitive)652 TEST_F(VFSFromYAMLTest, CaseSensitive) {
653   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
654   Lower->addRegularFile("//root/foo/bar/a");
655   IntrusiveRefCntPtr<vfs::FileSystem> FS =
656       getFromYAMLString("{ 'case-sensitive': 'true',\n"
657                         "  'roots': [\n"
658                         "{\n"
659                         "  'type': 'directory',\n"
660                         "  'name': '//root/',\n"
661                         "  'contents': [ {\n"
662                         "                  'type': 'file',\n"
663                         "                  'name': 'XX',\n"
664                         "                  'external-contents': '//root/foo/bar/a'\n"
665                         "                }\n"
666                         "              ]\n"
667                         "}]}",
668                         Lower);
669   ASSERT_TRUE(FS.get() != nullptr);
670 
671   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
672       new vfs::OverlayFileSystem(Lower));
673   O->pushOverlay(FS);
674 
675   ErrorOr<vfs::Status> SS = O->status("//root/xx");
676   EXPECT_EQ(SS.getError(), llvm::errc::no_such_file_or_directory);
677   SS = O->status("//root/xX");
678   EXPECT_EQ(SS.getError(), llvm::errc::no_such_file_or_directory);
679   SS = O->status("//root/Xx");
680   EXPECT_EQ(SS.getError(), llvm::errc::no_such_file_or_directory);
681   EXPECT_EQ(0, NumDiagnostics);
682 }
683 
TEST_F(VFSFromYAMLTest,IllegalVFSFile)684 TEST_F(VFSFromYAMLTest, IllegalVFSFile) {
685   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
686 
687   // invalid YAML at top-level
688   IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString("{]", Lower);
689   EXPECT_EQ(nullptr, FS.get());
690   // invalid YAML in roots
691   FS = getFromYAMLString("{ 'roots':[}", Lower);
692   // invalid YAML in directory
693   FS = getFromYAMLString(
694       "{ 'roots':[ { 'name': 'foo', 'type': 'directory', 'contents': [}",
695       Lower);
696   EXPECT_EQ(nullptr, FS.get());
697 
698   // invalid configuration
699   FS = getFromYAMLString("{ 'knobular': 'true', 'roots':[] }", Lower);
700   EXPECT_EQ(nullptr, FS.get());
701   FS = getFromYAMLString("{ 'case-sensitive': 'maybe', 'roots':[] }", Lower);
702   EXPECT_EQ(nullptr, FS.get());
703 
704   // invalid roots
705   FS = getFromYAMLString("{ 'roots':'' }", Lower);
706   EXPECT_EQ(nullptr, FS.get());
707   FS = getFromYAMLString("{ 'roots':{} }", Lower);
708   EXPECT_EQ(nullptr, FS.get());
709 
710   // invalid entries
711   FS = getFromYAMLString(
712       "{ 'roots':[ { 'type': 'other', 'name': 'me', 'contents': '' }", Lower);
713   EXPECT_EQ(nullptr, FS.get());
714   FS = getFromYAMLString("{ 'roots':[ { 'type': 'file', 'name': [], "
715                          "'external-contents': 'other' }",
716                          Lower);
717   EXPECT_EQ(nullptr, FS.get());
718   FS = getFromYAMLString(
719       "{ 'roots':[ { 'type': 'file', 'name': 'me', 'external-contents': [] }",
720       Lower);
721   EXPECT_EQ(nullptr, FS.get());
722   FS = getFromYAMLString(
723       "{ 'roots':[ { 'type': 'file', 'name': 'me', 'external-contents': {} }",
724       Lower);
725   EXPECT_EQ(nullptr, FS.get());
726   FS = getFromYAMLString(
727       "{ 'roots':[ { 'type': 'directory', 'name': 'me', 'contents': {} }",
728       Lower);
729   EXPECT_EQ(nullptr, FS.get());
730   FS = getFromYAMLString(
731       "{ 'roots':[ { 'type': 'directory', 'name': 'me', 'contents': '' }",
732       Lower);
733   EXPECT_EQ(nullptr, FS.get());
734   FS = getFromYAMLString(
735       "{ 'roots':[ { 'thingy': 'directory', 'name': 'me', 'contents': [] }",
736       Lower);
737   EXPECT_EQ(nullptr, FS.get());
738 
739   // missing mandatory fields
740   FS = getFromYAMLString("{ 'roots':[ { 'type': 'file', 'name': 'me' }", Lower);
741   EXPECT_EQ(nullptr, FS.get());
742   FS = getFromYAMLString(
743       "{ 'roots':[ { 'type': 'file', 'external-contents': 'other' }", Lower);
744   EXPECT_EQ(nullptr, FS.get());
745   FS = getFromYAMLString("{ 'roots':[ { 'name': 'me', 'contents': [] }", Lower);
746   EXPECT_EQ(nullptr, FS.get());
747 
748   // duplicate keys
749   FS = getFromYAMLString("{ 'roots':[], 'roots':[] }", Lower);
750   EXPECT_EQ(nullptr, FS.get());
751   FS = getFromYAMLString(
752       "{ 'case-sensitive':'true', 'case-sensitive':'true', 'roots':[] }",
753       Lower);
754   EXPECT_EQ(nullptr, FS.get());
755   FS =
756       getFromYAMLString("{ 'roots':[{'name':'me', 'name':'you', 'type':'file', "
757                         "'external-contents':'blah' } ] }",
758                         Lower);
759   EXPECT_EQ(nullptr, FS.get());
760 
761   // missing version
762   FS = getFromYAMLRawString("{ 'roots':[] }", Lower);
763   EXPECT_EQ(nullptr, FS.get());
764 
765   // bad version number
766   FS = getFromYAMLRawString("{ 'version':'foo', 'roots':[] }", Lower);
767   EXPECT_EQ(nullptr, FS.get());
768   FS = getFromYAMLRawString("{ 'version':-1, 'roots':[] }", Lower);
769   EXPECT_EQ(nullptr, FS.get());
770   FS = getFromYAMLRawString("{ 'version':100000, 'roots':[] }", Lower);
771   EXPECT_EQ(nullptr, FS.get());
772   EXPECT_EQ(24, NumDiagnostics);
773 }
774 
TEST_F(VFSFromYAMLTest,UseExternalName)775 TEST_F(VFSFromYAMLTest, UseExternalName) {
776   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
777   Lower->addRegularFile("//root/external/file");
778 
779   IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
780       "{ 'roots': [\n"
781       "  { 'type': 'file', 'name': '//root/A',\n"
782       "    'external-contents': '//root/external/file'\n"
783       "  },\n"
784       "  { 'type': 'file', 'name': '//root/B',\n"
785       "    'use-external-name': true,\n"
786       "    'external-contents': '//root/external/file'\n"
787       "  },\n"
788       "  { 'type': 'file', 'name': '//root/C',\n"
789       "    'use-external-name': false,\n"
790       "    'external-contents': '//root/external/file'\n"
791       "  }\n"
792       "] }", Lower);
793   ASSERT_TRUE(nullptr != FS.get());
794 
795   // default true
796   EXPECT_EQ("//root/external/file", FS->status("//root/A")->getName());
797   // explicit
798   EXPECT_EQ("//root/external/file", FS->status("//root/B")->getName());
799   EXPECT_EQ("//root/C", FS->status("//root/C")->getName());
800 
801   // global configuration
802   FS = getFromYAMLString(
803       "{ 'use-external-names': false,\n"
804       "  'roots': [\n"
805       "  { 'type': 'file', 'name': '//root/A',\n"
806       "    'external-contents': '//root/external/file'\n"
807       "  },\n"
808       "  { 'type': 'file', 'name': '//root/B',\n"
809       "    'use-external-name': true,\n"
810       "    'external-contents': '//root/external/file'\n"
811       "  },\n"
812       "  { 'type': 'file', 'name': '//root/C',\n"
813       "    'use-external-name': false,\n"
814       "    'external-contents': '//root/external/file'\n"
815       "  }\n"
816       "] }", Lower);
817   ASSERT_TRUE(nullptr != FS.get());
818 
819   // default
820   EXPECT_EQ("//root/A", FS->status("//root/A")->getName());
821   // explicit
822   EXPECT_EQ("//root/external/file", FS->status("//root/B")->getName());
823   EXPECT_EQ("//root/C", FS->status("//root/C")->getName());
824 }
825 
TEST_F(VFSFromYAMLTest,MultiComponentPath)826 TEST_F(VFSFromYAMLTest, MultiComponentPath) {
827   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
828   Lower->addRegularFile("//root/other");
829 
830   // file in roots
831   IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
832       "{ 'roots': [\n"
833       "  { 'type': 'file', 'name': '//root/path/to/file',\n"
834       "    'external-contents': '//root/other' }]\n"
835       "}", Lower);
836   ASSERT_TRUE(nullptr != FS.get());
837   EXPECT_FALSE(FS->status("//root/path/to/file").getError());
838   EXPECT_FALSE(FS->status("//root/path/to").getError());
839   EXPECT_FALSE(FS->status("//root/path").getError());
840   EXPECT_FALSE(FS->status("//root/").getError());
841 
842   // at the start
843   FS = getFromYAMLString(
844       "{ 'roots': [\n"
845       "  { 'type': 'directory', 'name': '//root/path/to',\n"
846       "    'contents': [ { 'type': 'file', 'name': 'file',\n"
847       "                    'external-contents': '//root/other' }]}]\n"
848       "}", Lower);
849   ASSERT_TRUE(nullptr != FS.get());
850   EXPECT_FALSE(FS->status("//root/path/to/file").getError());
851   EXPECT_FALSE(FS->status("//root/path/to").getError());
852   EXPECT_FALSE(FS->status("//root/path").getError());
853   EXPECT_FALSE(FS->status("//root/").getError());
854 
855   // at the end
856   FS = getFromYAMLString(
857       "{ 'roots': [\n"
858       "  { 'type': 'directory', 'name': '//root/',\n"
859       "    'contents': [ { 'type': 'file', 'name': 'path/to/file',\n"
860       "                    'external-contents': '//root/other' }]}]\n"
861       "}", Lower);
862   ASSERT_TRUE(nullptr != FS.get());
863   EXPECT_FALSE(FS->status("//root/path/to/file").getError());
864   EXPECT_FALSE(FS->status("//root/path/to").getError());
865   EXPECT_FALSE(FS->status("//root/path").getError());
866   EXPECT_FALSE(FS->status("//root/").getError());
867 }
868 
TEST_F(VFSFromYAMLTest,TrailingSlashes)869 TEST_F(VFSFromYAMLTest, TrailingSlashes) {
870   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
871   Lower->addRegularFile("//root/other");
872 
873   // file in roots
874   IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
875       "{ 'roots': [\n"
876       "  { 'type': 'directory', 'name': '//root/path/to////',\n"
877       "    'contents': [ { 'type': 'file', 'name': 'file',\n"
878       "                    'external-contents': '//root/other' }]}]\n"
879       "}", Lower);
880   ASSERT_TRUE(nullptr != FS.get());
881   EXPECT_FALSE(FS->status("//root/path/to/file").getError());
882   EXPECT_FALSE(FS->status("//root/path/to").getError());
883   EXPECT_FALSE(FS->status("//root/path").getError());
884   EXPECT_FALSE(FS->status("//root/").getError());
885 }
886 
TEST_F(VFSFromYAMLTest,DirectoryIteration)887 TEST_F(VFSFromYAMLTest, DirectoryIteration) {
888   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
889   Lower->addDirectory("//root/");
890   Lower->addDirectory("//root/foo");
891   Lower->addDirectory("//root/foo/bar");
892   Lower->addRegularFile("//root/foo/bar/a");
893   Lower->addRegularFile("//root/foo/bar/b");
894   Lower->addRegularFile("//root/file3");
895   IntrusiveRefCntPtr<vfs::FileSystem> FS =
896   getFromYAMLString("{ 'use-external-names': false,\n"
897                     "  'roots': [\n"
898                     "{\n"
899                     "  'type': 'directory',\n"
900                     "  'name': '//root/',\n"
901                     "  'contents': [ {\n"
902                     "                  'type': 'file',\n"
903                     "                  'name': 'file1',\n"
904                     "                  'external-contents': '//root/foo/bar/a'\n"
905                     "                },\n"
906                     "                {\n"
907                     "                  'type': 'file',\n"
908                     "                  'name': 'file2',\n"
909                     "                  'external-contents': '//root/foo/bar/b'\n"
910                     "                }\n"
911                     "              ]\n"
912                     "}\n"
913                     "]\n"
914                     "}",
915                     Lower);
916   ASSERT_TRUE(FS.get() != NULL);
917 
918   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
919       new vfs::OverlayFileSystem(Lower));
920   O->pushOverlay(FS);
921 
922   std::error_code EC;
923   {
924     const char *Contents[] = {"//root/file1", "//root/file2", "//root/file3",
925                               "//root/foo"};
926     checkContents(O->dir_begin("//root/", EC), makeStringRefVector(Contents));
927   }
928 
929   {
930     const char *Contents[] = {"//root/foo/bar/a", "//root/foo/bar/b"};
931     checkContents(O->dir_begin("//root/foo/bar", EC),
932                   makeStringRefVector(Contents));
933   }
934 }
935