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