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