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/Config/llvm-config.h"
13 #include "llvm/Support/Errc.h"
14 #include "llvm/Support/Host.h"
15 #include "llvm/Support/MemoryBuffer.h"
16 #include "llvm/Support/SourceMgr.h"
17 #include "gtest/gtest.h"
18 #include <map>
19 
20 using namespace clang;
21 using namespace llvm;
22 using llvm::sys::fs::UniqueID;
23 
24 namespace {
25 struct DummyFile : public vfs::File {
26   vfs::Status S;
DummyFile__anon43ce3cdf0111::DummyFile27   explicit DummyFile(vfs::Status S) : S(S) {}
status__anon43ce3cdf0111::DummyFile28   llvm::ErrorOr<vfs::Status> status() override { return S; }
29   llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
getBuffer__anon43ce3cdf0111::DummyFile30   getBuffer(const Twine &Name, int64_t FileSize, bool RequiresNullTerminator,
31             bool IsVolatile) override {
32     llvm_unreachable("unimplemented");
33   }
close__anon43ce3cdf0111::DummyFile34   std::error_code close() override { return std::error_code(); }
35 };
36 
37 class DummyFileSystem : public vfs::FileSystem {
38   int FSID;   // used to produce UniqueIDs
39   int FileID; // used to produce UniqueIDs
40   std::map<std::string, vfs::Status> FilesAndDirs;
41 
getNextFSID()42   static int getNextFSID() {
43     static int Count = 0;
44     return Count++;
45   }
46 
47 public:
DummyFileSystem()48   DummyFileSystem() : FSID(getNextFSID()), FileID(0) {}
49 
status(const Twine & Path)50   ErrorOr<vfs::Status> status(const Twine &Path) override {
51     std::map<std::string, vfs::Status>::iterator I =
52         FilesAndDirs.find(Path.str());
53     if (I == FilesAndDirs.end())
54       return make_error_code(llvm::errc::no_such_file_or_directory);
55     return I->second;
56   }
57   ErrorOr<std::unique_ptr<vfs::File>>
openFileForRead(const Twine & Path)58   openFileForRead(const Twine &Path) override {
59     auto S = status(Path);
60     if (S)
61       return std::unique_ptr<vfs::File>(new DummyFile{*S});
62     return S.getError();
63   }
getCurrentWorkingDirectory() const64   llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const override {
65     return std::string();
66   }
setCurrentWorkingDirectory(const Twine & Path)67   std::error_code setCurrentWorkingDirectory(const Twine &Path) override {
68     return std::error_code();
69   }
70   // Map any symlink to "/symlink".
getRealPath(const Twine & Path,SmallVectorImpl<char> & Output) const71   std::error_code getRealPath(const Twine &Path,
72                               SmallVectorImpl<char> &Output) const override {
73     auto I = FilesAndDirs.find(Path.str());
74     if (I == FilesAndDirs.end())
75       return make_error_code(llvm::errc::no_such_file_or_directory);
76     if (I->second.isSymlink()) {
77       Output.clear();
78       Twine("/symlink").toVector(Output);
79       return std::error_code();
80     }
81     Output.clear();
82     Path.toVector(Output);
83     return std::error_code();
84   }
85 
86   struct DirIterImpl : public clang::vfs::detail::DirIterImpl {
87     std::map<std::string, vfs::Status> &FilesAndDirs;
88     std::map<std::string, vfs::Status>::iterator I;
89     std::string Path;
isInPath__anon43ce3cdf0111::DummyFileSystem::DirIterImpl90     bool isInPath(StringRef S) {
91       if (Path.size() < S.size() && S.find(Path) == 0) {
92         auto LastSep = S.find_last_of('/');
93         if (LastSep == Path.size() || LastSep == Path.size()-1)
94           return true;
95       }
96       return false;
97     }
DirIterImpl__anon43ce3cdf0111::DummyFileSystem::DirIterImpl98     DirIterImpl(std::map<std::string, vfs::Status> &FilesAndDirs,
99                 const Twine &_Path)
100         : FilesAndDirs(FilesAndDirs), I(FilesAndDirs.begin()),
101           Path(_Path.str()) {
102       for ( ; I != FilesAndDirs.end(); ++I) {
103         if (isInPath(I->first)) {
104           CurrentEntry = I->second;
105           break;
106         }
107       }
108     }
increment__anon43ce3cdf0111::DummyFileSystem::DirIterImpl109     std::error_code increment() override {
110       ++I;
111       for ( ; I != FilesAndDirs.end(); ++I) {
112         if (isInPath(I->first)) {
113           CurrentEntry = I->second;
114           break;
115         }
116       }
117       if (I == FilesAndDirs.end())
118         CurrentEntry = vfs::Status();
119       return std::error_code();
120     }
121   };
122 
dir_begin(const Twine & Dir,std::error_code & EC)123   vfs::directory_iterator dir_begin(const Twine &Dir,
124                                     std::error_code &EC) override {
125     return vfs::directory_iterator(
126         std::make_shared<DirIterImpl>(FilesAndDirs, Dir));
127   }
128 
addEntry(StringRef Path,const vfs::Status & Status)129   void addEntry(StringRef Path, const vfs::Status &Status) {
130     FilesAndDirs[Path] = Status;
131   }
132 
addRegularFile(StringRef Path,sys::fs::perms Perms=sys::fs::all_all)133   void addRegularFile(StringRef Path, sys::fs::perms Perms = sys::fs::all_all) {
134     vfs::Status S(Path, UniqueID(FSID, FileID++),
135                   std::chrono::system_clock::now(), 0, 0, 1024,
136                   sys::fs::file_type::regular_file, Perms);
137     addEntry(Path, S);
138   }
139 
addDirectory(StringRef Path,sys::fs::perms Perms=sys::fs::all_all)140   void addDirectory(StringRef Path, sys::fs::perms Perms = sys::fs::all_all) {
141     vfs::Status S(Path, UniqueID(FSID, FileID++),
142                   std::chrono::system_clock::now(), 0, 0, 0,
143                   sys::fs::file_type::directory_file, Perms);
144     addEntry(Path, S);
145   }
146 
addSymlink(StringRef Path)147   void addSymlink(StringRef Path) {
148     vfs::Status S(Path, UniqueID(FSID, FileID++),
149                   std::chrono::system_clock::now(), 0, 0, 0,
150                   sys::fs::file_type::symlink_file, sys::fs::all_all);
151     addEntry(Path, S);
152   }
153 };
154 } // end anonymous namespace
155 
TEST(VirtualFileSystemTest,StatusQueries)156 TEST(VirtualFileSystemTest, StatusQueries) {
157   IntrusiveRefCntPtr<DummyFileSystem> D(new DummyFileSystem());
158   ErrorOr<vfs::Status> Status((std::error_code()));
159 
160   D->addRegularFile("/foo");
161   Status = D->status("/foo");
162   ASSERT_FALSE(Status.getError());
163   EXPECT_TRUE(Status->isStatusKnown());
164   EXPECT_FALSE(Status->isDirectory());
165   EXPECT_TRUE(Status->isRegularFile());
166   EXPECT_FALSE(Status->isSymlink());
167   EXPECT_FALSE(Status->isOther());
168   EXPECT_TRUE(Status->exists());
169 
170   D->addDirectory("/bar");
171   Status = D->status("/bar");
172   ASSERT_FALSE(Status.getError());
173   EXPECT_TRUE(Status->isStatusKnown());
174   EXPECT_TRUE(Status->isDirectory());
175   EXPECT_FALSE(Status->isRegularFile());
176   EXPECT_FALSE(Status->isSymlink());
177   EXPECT_FALSE(Status->isOther());
178   EXPECT_TRUE(Status->exists());
179 
180   D->addSymlink("/baz");
181   Status = D->status("/baz");
182   ASSERT_FALSE(Status.getError());
183   EXPECT_TRUE(Status->isStatusKnown());
184   EXPECT_FALSE(Status->isDirectory());
185   EXPECT_FALSE(Status->isRegularFile());
186   EXPECT_TRUE(Status->isSymlink());
187   EXPECT_FALSE(Status->isOther());
188   EXPECT_TRUE(Status->exists());
189 
190   EXPECT_TRUE(Status->equivalent(*Status));
191   ErrorOr<vfs::Status> Status2 = D->status("/foo");
192   ASSERT_FALSE(Status2.getError());
193   EXPECT_FALSE(Status->equivalent(*Status2));
194 }
195 
TEST(VirtualFileSystemTest,BaseOnlyOverlay)196 TEST(VirtualFileSystemTest, BaseOnlyOverlay) {
197   IntrusiveRefCntPtr<DummyFileSystem> D(new DummyFileSystem());
198   ErrorOr<vfs::Status> Status((std::error_code()));
199   EXPECT_FALSE(Status = D->status("/foo"));
200 
201   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(new vfs::OverlayFileSystem(D));
202   EXPECT_FALSE(Status = O->status("/foo"));
203 
204   D->addRegularFile("/foo");
205   Status = D->status("/foo");
206   EXPECT_FALSE(Status.getError());
207 
208   ErrorOr<vfs::Status> Status2((std::error_code()));
209   Status2 = O->status("/foo");
210   EXPECT_FALSE(Status2.getError());
211   EXPECT_TRUE(Status->equivalent(*Status2));
212 }
213 
TEST(VirtualFileSystemTest,GetRealPathInOverlay)214 TEST(VirtualFileSystemTest, GetRealPathInOverlay) {
215   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
216   Lower->addRegularFile("/foo");
217   Lower->addSymlink("/lower_link");
218   IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem());
219 
220   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
221       new vfs::OverlayFileSystem(Lower));
222   O->pushOverlay(Upper);
223 
224   // Regular file.
225   SmallString<16> RealPath;
226   EXPECT_FALSE(O->getRealPath("/foo", RealPath));
227   EXPECT_EQ(RealPath.str(), "/foo");
228 
229   // Expect no error getting real path for symlink in lower overlay.
230   EXPECT_FALSE(O->getRealPath("/lower_link", RealPath));
231   EXPECT_EQ(RealPath.str(), "/symlink");
232 
233   // Try a non-existing link.
234   EXPECT_EQ(O->getRealPath("/upper_link", RealPath),
235             errc::no_such_file_or_directory);
236 
237   // Add a new symlink in upper.
238   Upper->addSymlink("/upper_link");
239   EXPECT_FALSE(O->getRealPath("/upper_link", RealPath));
240   EXPECT_EQ(RealPath.str(), "/symlink");
241 }
242 
TEST(VirtualFileSystemTest,OverlayFiles)243 TEST(VirtualFileSystemTest, OverlayFiles) {
244   IntrusiveRefCntPtr<DummyFileSystem> Base(new DummyFileSystem());
245   IntrusiveRefCntPtr<DummyFileSystem> Middle(new DummyFileSystem());
246   IntrusiveRefCntPtr<DummyFileSystem> Top(new DummyFileSystem());
247   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
248       new vfs::OverlayFileSystem(Base));
249   O->pushOverlay(Middle);
250   O->pushOverlay(Top);
251 
252   ErrorOr<vfs::Status> Status1((std::error_code())),
253       Status2((std::error_code())), Status3((std::error_code())),
254       StatusB((std::error_code())), StatusM((std::error_code())),
255       StatusT((std::error_code()));
256 
257   Base->addRegularFile("/foo");
258   StatusB = Base->status("/foo");
259   ASSERT_FALSE(StatusB.getError());
260   Status1 = O->status("/foo");
261   ASSERT_FALSE(Status1.getError());
262   Middle->addRegularFile("/foo");
263   StatusM = Middle->status("/foo");
264   ASSERT_FALSE(StatusM.getError());
265   Status2 = O->status("/foo");
266   ASSERT_FALSE(Status2.getError());
267   Top->addRegularFile("/foo");
268   StatusT = Top->status("/foo");
269   ASSERT_FALSE(StatusT.getError());
270   Status3 = O->status("/foo");
271   ASSERT_FALSE(Status3.getError());
272 
273   EXPECT_TRUE(Status1->equivalent(*StatusB));
274   EXPECT_TRUE(Status2->equivalent(*StatusM));
275   EXPECT_TRUE(Status3->equivalent(*StatusT));
276 
277   EXPECT_FALSE(Status1->equivalent(*Status2));
278   EXPECT_FALSE(Status2->equivalent(*Status3));
279   EXPECT_FALSE(Status1->equivalent(*Status3));
280 }
281 
TEST(VirtualFileSystemTest,OverlayDirsNonMerged)282 TEST(VirtualFileSystemTest, OverlayDirsNonMerged) {
283   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
284   IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem());
285   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
286       new vfs::OverlayFileSystem(Lower));
287   O->pushOverlay(Upper);
288 
289   Lower->addDirectory("/lower-only");
290   Upper->addDirectory("/upper-only");
291 
292   // non-merged paths should be the same
293   ErrorOr<vfs::Status> Status1 = Lower->status("/lower-only");
294   ASSERT_FALSE(Status1.getError());
295   ErrorOr<vfs::Status> Status2 = O->status("/lower-only");
296   ASSERT_FALSE(Status2.getError());
297   EXPECT_TRUE(Status1->equivalent(*Status2));
298 
299   Status1 = Upper->status("/upper-only");
300   ASSERT_FALSE(Status1.getError());
301   Status2 = O->status("/upper-only");
302   ASSERT_FALSE(Status2.getError());
303   EXPECT_TRUE(Status1->equivalent(*Status2));
304 }
305 
TEST(VirtualFileSystemTest,MergedDirPermissions)306 TEST(VirtualFileSystemTest, MergedDirPermissions) {
307   // merged directories get the permissions of the upper dir
308   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
309   IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem());
310   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
311       new vfs::OverlayFileSystem(Lower));
312   O->pushOverlay(Upper);
313 
314   ErrorOr<vfs::Status> Status((std::error_code()));
315   Lower->addDirectory("/both", sys::fs::owner_read);
316   Upper->addDirectory("/both", sys::fs::owner_all | sys::fs::group_read);
317   Status = O->status("/both");
318   ASSERT_FALSE(Status.getError());
319   EXPECT_EQ(0740, Status->getPermissions());
320 
321   // permissions (as usual) are not recursively applied
322   Lower->addRegularFile("/both/foo", sys::fs::owner_read);
323   Upper->addRegularFile("/both/bar", sys::fs::owner_write);
324   Status = O->status("/both/foo");
325   ASSERT_FALSE( Status.getError());
326   EXPECT_EQ(0400, Status->getPermissions());
327   Status = O->status("/both/bar");
328   ASSERT_FALSE(Status.getError());
329   EXPECT_EQ(0200, Status->getPermissions());
330 }
331 
332 namespace {
333 struct ScopedDir {
334   SmallString<128> Path;
ScopedDir__anon43ce3cdf0211::ScopedDir335   ScopedDir(const Twine &Name, bool Unique=false) {
336     std::error_code EC;
337     if (Unique) {
338       EC =  llvm::sys::fs::createUniqueDirectory(Name, Path);
339     } else {
340       Path = Name.str();
341       EC = llvm::sys::fs::create_directory(Twine(Path));
342     }
343     if (EC)
344       Path = "";
345     EXPECT_FALSE(EC);
346   }
~ScopedDir__anon43ce3cdf0211::ScopedDir347   ~ScopedDir() {
348     if (Path != "") {
349       EXPECT_FALSE(llvm::sys::fs::remove(Path.str()));
350     }
351   }
operator StringRef__anon43ce3cdf0211::ScopedDir352   operator StringRef() { return Path.str(); }
353 };
354 
355 struct ScopedLink {
356   SmallString<128> Path;
ScopedLink__anon43ce3cdf0211::ScopedLink357   ScopedLink(const Twine &To, const Twine &From) {
358     Path = From.str();
359     std::error_code EC = sys::fs::create_link(To, From);
360     if (EC)
361       Path = "";
362     EXPECT_FALSE(EC);
363   }
~ScopedLink__anon43ce3cdf0211::ScopedLink364   ~ScopedLink() {
365     if (Path != "") {
366       EXPECT_FALSE(llvm::sys::fs::remove(Path.str()));
367     }
368   }
operator StringRef__anon43ce3cdf0211::ScopedLink369   operator StringRef() { return Path.str(); }
370 };
371 } // end anonymous namespace
372 
TEST(VirtualFileSystemTest,BasicRealFSIteration)373 TEST(VirtualFileSystemTest, BasicRealFSIteration) {
374   ScopedDir TestDirectory("virtual-file-system-test", /*Unique*/true);
375   IntrusiveRefCntPtr<vfs::FileSystem> FS = vfs::getRealFileSystem();
376 
377   std::error_code EC;
378   vfs::directory_iterator I = FS->dir_begin(Twine(TestDirectory), EC);
379   ASSERT_FALSE(EC);
380   EXPECT_EQ(vfs::directory_iterator(), I); // empty directory is empty
381 
382   ScopedDir _a(TestDirectory+"/a");
383   ScopedDir _ab(TestDirectory+"/a/b");
384   ScopedDir _c(TestDirectory+"/c");
385   ScopedDir _cd(TestDirectory+"/c/d");
386 
387   I = FS->dir_begin(Twine(TestDirectory), EC);
388   ASSERT_FALSE(EC);
389   ASSERT_NE(vfs::directory_iterator(), I);
390   // Check either a or c, since we can't rely on the iteration order.
391   EXPECT_TRUE(I->getName().endswith("a") || I->getName().endswith("c"));
392   I.increment(EC);
393   ASSERT_FALSE(EC);
394   ASSERT_NE(vfs::directory_iterator(), I);
395   EXPECT_TRUE(I->getName().endswith("a") || I->getName().endswith("c"));
396   I.increment(EC);
397   EXPECT_EQ(vfs::directory_iterator(), I);
398 }
399 
400 #ifdef LLVM_ON_UNIX
TEST(VirtualFileSystemTest,BrokenSymlinkRealFSIteration)401 TEST(VirtualFileSystemTest, BrokenSymlinkRealFSIteration) {
402   ScopedDir TestDirectory("virtual-file-system-test", /*Unique*/ true);
403   IntrusiveRefCntPtr<vfs::FileSystem> FS = vfs::getRealFileSystem();
404 
405   ScopedLink _a("no_such_file", TestDirectory + "/a");
406   ScopedDir _b(TestDirectory + "/b");
407   ScopedLink _c("no_such_file", TestDirectory + "/c");
408 
409   std::error_code EC;
410   for (vfs::directory_iterator I = FS->dir_begin(Twine(TestDirectory), EC), E;
411        I != E; I.increment(EC)) {
412     // Skip broken symlinks.
413     auto EC2 = std::make_error_code(std::errc::no_such_file_or_directory);
414     if (EC == EC2) {
415       EC.clear();
416       continue;
417     }
418     // For bot debugging.
419     if (EC) {
420       outs() << "Error code found:\n"
421              << "EC value: " << EC.value() << "\n"
422              << "EC category: " << EC.category().name()
423              << "EC message: " << EC.message() << "\n";
424 
425       outs() << "Error code tested for:\n"
426              << "EC value: " << EC2.value() << "\n"
427              << "EC category: " << EC2.category().name()
428              << "EC message: " << EC2.message() << "\n";
429     }
430     ASSERT_FALSE(EC);
431     EXPECT_TRUE(I->getName() == _b);
432   }
433 }
434 #endif
435 
TEST(VirtualFileSystemTest,BasicRealFSRecursiveIteration)436 TEST(VirtualFileSystemTest, BasicRealFSRecursiveIteration) {
437   ScopedDir TestDirectory("virtual-file-system-test", /*Unique*/true);
438   IntrusiveRefCntPtr<vfs::FileSystem> FS = vfs::getRealFileSystem();
439 
440   std::error_code EC;
441   auto I = vfs::recursive_directory_iterator(*FS, Twine(TestDirectory), EC);
442   ASSERT_FALSE(EC);
443   EXPECT_EQ(vfs::recursive_directory_iterator(), I); // empty directory is empty
444 
445   ScopedDir _a(TestDirectory+"/a");
446   ScopedDir _ab(TestDirectory+"/a/b");
447   ScopedDir _c(TestDirectory+"/c");
448   ScopedDir _cd(TestDirectory+"/c/d");
449 
450   I = vfs::recursive_directory_iterator(*FS, Twine(TestDirectory), EC);
451   ASSERT_FALSE(EC);
452   ASSERT_NE(vfs::recursive_directory_iterator(), I);
453 
454   std::vector<std::string> Contents;
455   for (auto E = vfs::recursive_directory_iterator(); !EC && I != E;
456        I.increment(EC)) {
457     Contents.push_back(I->getName());
458   }
459 
460   // Check contents, which may be in any order
461   EXPECT_EQ(4U, Contents.size());
462   int Counts[4] = { 0, 0, 0, 0 };
463   for (const std::string &Name : Contents) {
464     ASSERT_FALSE(Name.empty());
465     int Index = Name[Name.size()-1] - 'a';
466     ASSERT_TRUE(Index >= 0 && Index < 4);
467     Counts[Index]++;
468   }
469   EXPECT_EQ(1, Counts[0]); // a
470   EXPECT_EQ(1, Counts[1]); // b
471   EXPECT_EQ(1, Counts[2]); // c
472   EXPECT_EQ(1, Counts[3]); // d
473 }
474 
475 #ifdef LLVM_ON_UNIX
TEST(VirtualFileSystemTest,BrokenSymlinkRealFSRecursiveIteration)476 TEST(VirtualFileSystemTest, BrokenSymlinkRealFSRecursiveIteration) {
477   ScopedDir TestDirectory("virtual-file-system-test", /*Unique*/ true);
478   IntrusiveRefCntPtr<vfs::FileSystem> FS = vfs::getRealFileSystem();
479 
480   ScopedLink _a("no_such_file", TestDirectory + "/a");
481   ScopedDir _b(TestDirectory + "/b");
482   ScopedLink _ba("no_such_file", TestDirectory + "/b/a");
483   ScopedDir _bb(TestDirectory + "/b/b");
484   ScopedLink _bc("no_such_file", TestDirectory + "/b/c");
485   ScopedLink _c("no_such_file", TestDirectory + "/c");
486   ScopedDir _d(TestDirectory + "/d");
487   ScopedDir _dd(TestDirectory + "/d/d");
488   ScopedDir _ddd(TestDirectory + "/d/d/d");
489   ScopedLink _e("no_such_file", TestDirectory + "/e");
490 
491   std::vector<StringRef> ExpectedBrokenSymlinks = {_a, _ba, _bc, _c, _e};
492   std::vector<StringRef> ExpectedNonBrokenSymlinks = {_b, _bb, _d, _dd, _ddd};
493   std::vector<std::string> VisitedBrokenSymlinks;
494   std::vector<std::string> VisitedNonBrokenSymlinks;
495   std::error_code EC;
496   for (vfs::recursive_directory_iterator I(*FS, Twine(TestDirectory), EC), E;
497        I != E; I.increment(EC)) {
498     auto EC2 = std::make_error_code(std::errc::no_such_file_or_directory);
499     if (EC == EC2) {
500       VisitedBrokenSymlinks.push_back(I->getName());
501       continue;
502     }
503     // For bot debugging.
504     if (EC) {
505       outs() << "Error code found:\n"
506              << "EC value: " << EC.value() << "\n"
507              << "EC category: " << EC.category().name()
508              << "EC message: " << EC.message() << "\n";
509 
510       outs() << "Error code tested for:\n"
511              << "EC value: " << EC2.value() << "\n"
512              << "EC category: " << EC2.category().name()
513              << "EC message: " << EC2.message() << "\n";
514     }
515     ASSERT_FALSE(EC);
516     VisitedNonBrokenSymlinks.push_back(I->getName());
517   }
518 
519   // Check visited file names.
520   std::sort(VisitedBrokenSymlinks.begin(), VisitedBrokenSymlinks.end());
521   std::sort(VisitedNonBrokenSymlinks.begin(), VisitedNonBrokenSymlinks.end());
522   EXPECT_EQ(ExpectedBrokenSymlinks.size(), VisitedBrokenSymlinks.size());
523   EXPECT_TRUE(std::equal(VisitedBrokenSymlinks.begin(),
524                          VisitedBrokenSymlinks.end(),
525                          ExpectedBrokenSymlinks.begin()));
526   EXPECT_EQ(ExpectedNonBrokenSymlinks.size(), VisitedNonBrokenSymlinks.size());
527   EXPECT_TRUE(std::equal(VisitedNonBrokenSymlinks.begin(),
528                          VisitedNonBrokenSymlinks.end(),
529                          ExpectedNonBrokenSymlinks.begin()));
530 }
531 #endif
532 
533 template <typename DirIter>
checkContents(DirIter I,ArrayRef<StringRef> ExpectedOut)534 static void checkContents(DirIter I, ArrayRef<StringRef> ExpectedOut) {
535   std::error_code EC;
536   SmallVector<StringRef, 4> Expected(ExpectedOut.begin(), ExpectedOut.end());
537   SmallVector<std::string, 4> InputToCheck;
538 
539   // Do not rely on iteration order to check for contents, sort both
540   // content vectors before comparison.
541   for (DirIter E; !EC && I != E; I.increment(EC))
542     InputToCheck.push_back(I->getName());
543 
544   llvm::sort(InputToCheck.begin(), InputToCheck.end());
545   llvm::sort(Expected.begin(), Expected.end());
546   EXPECT_EQ(InputToCheck.size(), Expected.size());
547 
548   unsigned LastElt = std::min(InputToCheck.size(), Expected.size());
549   for (unsigned Idx = 0; Idx != LastElt; ++Idx)
550     EXPECT_EQ(StringRef(InputToCheck[Idx]), Expected[Idx]);
551 }
552 
TEST(VirtualFileSystemTest,OverlayIteration)553 TEST(VirtualFileSystemTest, OverlayIteration) {
554   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
555   IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem());
556   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
557       new vfs::OverlayFileSystem(Lower));
558   O->pushOverlay(Upper);
559 
560   std::error_code EC;
561   checkContents(O->dir_begin("/", EC), ArrayRef<StringRef>());
562 
563   Lower->addRegularFile("/file1");
564   checkContents(O->dir_begin("/", EC), ArrayRef<StringRef>("/file1"));
565 
566   Upper->addRegularFile("/file2");
567   checkContents(O->dir_begin("/", EC), {"/file2", "/file1"});
568 
569   Lower->addDirectory("/dir1");
570   Lower->addRegularFile("/dir1/foo");
571   Upper->addDirectory("/dir2");
572   Upper->addRegularFile("/dir2/foo");
573   checkContents(O->dir_begin("/dir2", EC), ArrayRef<StringRef>("/dir2/foo"));
574   checkContents(O->dir_begin("/", EC), {"/dir2", "/file2", "/dir1", "/file1"});
575 }
576 
TEST(VirtualFileSystemTest,OverlayRecursiveIteration)577 TEST(VirtualFileSystemTest, OverlayRecursiveIteration) {
578   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
579   IntrusiveRefCntPtr<DummyFileSystem> Middle(new DummyFileSystem());
580   IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem());
581   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
582       new vfs::OverlayFileSystem(Lower));
583   O->pushOverlay(Middle);
584   O->pushOverlay(Upper);
585 
586   std::error_code EC;
587   checkContents(vfs::recursive_directory_iterator(*O, "/", EC),
588                 ArrayRef<StringRef>());
589 
590   Lower->addRegularFile("/file1");
591   checkContents(vfs::recursive_directory_iterator(*O, "/", EC),
592                 ArrayRef<StringRef>("/file1"));
593 
594   Upper->addDirectory("/dir");
595   Upper->addRegularFile("/dir/file2");
596   checkContents(vfs::recursive_directory_iterator(*O, "/", EC),
597                 {"/dir", "/dir/file2", "/file1"});
598 
599   Lower->addDirectory("/dir1");
600   Lower->addRegularFile("/dir1/foo");
601   Lower->addDirectory("/dir1/a");
602   Lower->addRegularFile("/dir1/a/b");
603   Middle->addDirectory("/a");
604   Middle->addDirectory("/a/b");
605   Middle->addDirectory("/a/b/c");
606   Middle->addRegularFile("/a/b/c/d");
607   Middle->addRegularFile("/hiddenByUp");
608   Upper->addDirectory("/dir2");
609   Upper->addRegularFile("/dir2/foo");
610   Upper->addRegularFile("/hiddenByUp");
611   checkContents(vfs::recursive_directory_iterator(*O, "/dir2", EC),
612                 ArrayRef<StringRef>("/dir2/foo"));
613   checkContents(vfs::recursive_directory_iterator(*O, "/", EC),
614                 {"/dir", "/dir/file2", "/dir2", "/dir2/foo", "/hiddenByUp",
615                  "/a", "/a/b", "/a/b/c", "/a/b/c/d", "/dir1", "/dir1/a",
616                  "/dir1/a/b", "/dir1/foo", "/file1"});
617 }
618 
TEST(VirtualFileSystemTest,ThreeLevelIteration)619 TEST(VirtualFileSystemTest, ThreeLevelIteration) {
620   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
621   IntrusiveRefCntPtr<DummyFileSystem> Middle(new DummyFileSystem());
622   IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem());
623   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
624       new vfs::OverlayFileSystem(Lower));
625   O->pushOverlay(Middle);
626   O->pushOverlay(Upper);
627 
628   std::error_code EC;
629   checkContents(O->dir_begin("/", EC), ArrayRef<StringRef>());
630 
631   Middle->addRegularFile("/file2");
632   checkContents(O->dir_begin("/", EC), ArrayRef<StringRef>("/file2"));
633 
634   Lower->addRegularFile("/file1");
635   Upper->addRegularFile("/file3");
636   checkContents(O->dir_begin("/", EC), {"/file3", "/file2", "/file1"});
637 }
638 
TEST(VirtualFileSystemTest,HiddenInIteration)639 TEST(VirtualFileSystemTest, HiddenInIteration) {
640   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
641   IntrusiveRefCntPtr<DummyFileSystem> Middle(new DummyFileSystem());
642   IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem());
643   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
644       new vfs::OverlayFileSystem(Lower));
645   O->pushOverlay(Middle);
646   O->pushOverlay(Upper);
647 
648   std::error_code EC;
649   Lower->addRegularFile("/onlyInLow", sys::fs::owner_read);
650   Lower->addRegularFile("/hiddenByMid", sys::fs::owner_read);
651   Lower->addRegularFile("/hiddenByUp", sys::fs::owner_read);
652   Middle->addRegularFile("/onlyInMid", sys::fs::owner_write);
653   Middle->addRegularFile("/hiddenByMid", sys::fs::owner_write);
654   Middle->addRegularFile("/hiddenByUp", sys::fs::owner_write);
655   Upper->addRegularFile("/onlyInUp", sys::fs::owner_all);
656   Upper->addRegularFile("/hiddenByUp", sys::fs::owner_all);
657   checkContents(
658       O->dir_begin("/", EC),
659       {"/hiddenByUp", "/onlyInUp", "/hiddenByMid", "/onlyInMid", "/onlyInLow"});
660 
661   // Make sure we get the top-most entry
662   {
663     std::error_code EC;
664     vfs::directory_iterator I = O->dir_begin("/", EC), E;
665     for ( ; !EC && I != E; I.increment(EC))
666       if (I->getName() == "/hiddenByUp")
667         break;
668     ASSERT_NE(E, I);
669     EXPECT_EQ(sys::fs::owner_all, I->getPermissions());
670   }
671   {
672     std::error_code EC;
673     vfs::directory_iterator I = O->dir_begin("/", EC), E;
674     for ( ; !EC && I != E; I.increment(EC))
675       if (I->getName() == "/hiddenByMid")
676         break;
677     ASSERT_NE(E, I);
678     EXPECT_EQ(sys::fs::owner_write, I->getPermissions());
679   }
680 }
681 
682 class InMemoryFileSystemTest : public ::testing::Test {
683 protected:
684   clang::vfs::InMemoryFileSystem FS;
685   clang::vfs::InMemoryFileSystem NormalizedFS;
686 
InMemoryFileSystemTest()687   InMemoryFileSystemTest()
688       : FS(/*UseNormalizedPaths=*/false),
689         NormalizedFS(/*UseNormalizedPaths=*/true) {}
690 };
691 
TEST_F(InMemoryFileSystemTest,IsEmpty)692 TEST_F(InMemoryFileSystemTest, IsEmpty) {
693   auto Stat = FS.status("/a");
694   ASSERT_EQ(Stat.getError(),errc::no_such_file_or_directory) << FS.toString();
695   Stat = FS.status("/");
696   ASSERT_EQ(Stat.getError(), errc::no_such_file_or_directory) << FS.toString();
697 }
698 
TEST_F(InMemoryFileSystemTest,WindowsPath)699 TEST_F(InMemoryFileSystemTest, WindowsPath) {
700   FS.addFile("c:/windows/system128/foo.cpp", 0, MemoryBuffer::getMemBuffer(""));
701   auto Stat = FS.status("c:");
702 #if !defined(_WIN32)
703   ASSERT_FALSE(Stat.getError()) << Stat.getError() << FS.toString();
704 #endif
705   Stat = FS.status("c:/windows/system128/foo.cpp");
706   ASSERT_FALSE(Stat.getError()) << Stat.getError() << FS.toString();
707   FS.addFile("d:/windows/foo.cpp", 0, MemoryBuffer::getMemBuffer(""));
708   Stat = FS.status("d:/windows/foo.cpp");
709   ASSERT_FALSE(Stat.getError()) << Stat.getError() << FS.toString();
710 }
711 
TEST_F(InMemoryFileSystemTest,OverlayFile)712 TEST_F(InMemoryFileSystemTest, OverlayFile) {
713   FS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a"));
714   NormalizedFS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a"));
715   auto Stat = FS.status("/");
716   ASSERT_FALSE(Stat.getError()) << Stat.getError() << FS.toString();
717   Stat = FS.status("/.");
718   ASSERT_FALSE(Stat);
719   Stat = NormalizedFS.status("/.");
720   ASSERT_FALSE(Stat.getError()) << Stat.getError() << FS.toString();
721   Stat = FS.status("/a");
722   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
723   ASSERT_EQ("/a", Stat->getName());
724 }
725 
TEST_F(InMemoryFileSystemTest,OverlayFileNoOwn)726 TEST_F(InMemoryFileSystemTest, OverlayFileNoOwn) {
727   auto Buf = MemoryBuffer::getMemBuffer("a");
728   FS.addFileNoOwn("/a", 0, Buf.get());
729   auto Stat = FS.status("/a");
730   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
731   ASSERT_EQ("/a", Stat->getName());
732 }
733 
TEST_F(InMemoryFileSystemTest,OpenFileForRead)734 TEST_F(InMemoryFileSystemTest, OpenFileForRead) {
735   FS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a"));
736   FS.addFile("././c", 0, MemoryBuffer::getMemBuffer("c"));
737   FS.addFile("./d/../d", 0, MemoryBuffer::getMemBuffer("d"));
738   NormalizedFS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a"));
739   NormalizedFS.addFile("././c", 0, MemoryBuffer::getMemBuffer("c"));
740   NormalizedFS.addFile("./d/../d", 0, MemoryBuffer::getMemBuffer("d"));
741   auto File = FS.openFileForRead("/a");
742   ASSERT_EQ("a", (*(*File)->getBuffer("ignored"))->getBuffer());
743   File = FS.openFileForRead("/a"); // Open again.
744   ASSERT_EQ("a", (*(*File)->getBuffer("ignored"))->getBuffer());
745   File = NormalizedFS.openFileForRead("/././a"); // Open again.
746   ASSERT_EQ("a", (*(*File)->getBuffer("ignored"))->getBuffer());
747   File = FS.openFileForRead("/");
748   ASSERT_EQ(File.getError(), errc::invalid_argument) << FS.toString();
749   File = FS.openFileForRead("/b");
750   ASSERT_EQ(File.getError(), errc::no_such_file_or_directory) << FS.toString();
751   File = FS.openFileForRead("./c");
752   ASSERT_FALSE(File);
753   File = FS.openFileForRead("e/../d");
754   ASSERT_FALSE(File);
755   File = NormalizedFS.openFileForRead("./c");
756   ASSERT_EQ("c", (*(*File)->getBuffer("ignored"))->getBuffer());
757   File = NormalizedFS.openFileForRead("e/../d");
758   ASSERT_EQ("d", (*(*File)->getBuffer("ignored"))->getBuffer());
759 }
760 
TEST_F(InMemoryFileSystemTest,DuplicatedFile)761 TEST_F(InMemoryFileSystemTest, DuplicatedFile) {
762   ASSERT_TRUE(FS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a")));
763   ASSERT_FALSE(FS.addFile("/a/b", 0, MemoryBuffer::getMemBuffer("a")));
764   ASSERT_TRUE(FS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a")));
765   ASSERT_FALSE(FS.addFile("/a", 0, MemoryBuffer::getMemBuffer("b")));
766 }
767 
TEST_F(InMemoryFileSystemTest,DirectoryIteration)768 TEST_F(InMemoryFileSystemTest, DirectoryIteration) {
769   FS.addFile("/a", 0, MemoryBuffer::getMemBuffer(""));
770   FS.addFile("/b/c", 0, MemoryBuffer::getMemBuffer(""));
771 
772   std::error_code EC;
773   vfs::directory_iterator I = FS.dir_begin("/", EC);
774   ASSERT_FALSE(EC);
775   ASSERT_EQ("/a", I->getName());
776   I.increment(EC);
777   ASSERT_FALSE(EC);
778   ASSERT_EQ("/b", I->getName());
779   I.increment(EC);
780   ASSERT_FALSE(EC);
781   ASSERT_EQ(vfs::directory_iterator(), I);
782 
783   I = FS.dir_begin("/b", EC);
784   ASSERT_FALSE(EC);
785   ASSERT_EQ("/b/c", I->getName());
786   I.increment(EC);
787   ASSERT_FALSE(EC);
788   ASSERT_EQ(vfs::directory_iterator(), I);
789 }
790 
TEST_F(InMemoryFileSystemTest,WorkingDirectory)791 TEST_F(InMemoryFileSystemTest, WorkingDirectory) {
792   FS.setCurrentWorkingDirectory("/b");
793   FS.addFile("c", 0, MemoryBuffer::getMemBuffer(""));
794 
795   auto Stat = FS.status("/b/c");
796   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
797   ASSERT_EQ("c", Stat->getName());
798   ASSERT_EQ("/b", *FS.getCurrentWorkingDirectory());
799 
800   Stat = FS.status("c");
801   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
802 
803   auto ReplaceBackslashes = [](std::string S) {
804     std::replace(S.begin(), S.end(), '\\', '/');
805     return S;
806   };
807   NormalizedFS.setCurrentWorkingDirectory("/b/c");
808   NormalizedFS.setCurrentWorkingDirectory(".");
809   ASSERT_EQ("/b/c", ReplaceBackslashes(
810                         NormalizedFS.getCurrentWorkingDirectory().get()));
811   NormalizedFS.setCurrentWorkingDirectory("..");
812   ASSERT_EQ("/b", ReplaceBackslashes(
813                       NormalizedFS.getCurrentWorkingDirectory().get()));
814 }
815 
816 #if !defined(_WIN32)
TEST_F(InMemoryFileSystemTest,GetRealPath)817 TEST_F(InMemoryFileSystemTest, GetRealPath) {
818   SmallString<16> Path;
819   EXPECT_EQ(FS.getRealPath("b", Path), errc::operation_not_permitted);
820 
821   auto GetRealPath = [this](StringRef P) {
822     SmallString<16> Output;
823     auto EC = FS.getRealPath(P, Output);
824     EXPECT_FALSE(EC);
825     return Output.str().str();
826   };
827 
828   FS.setCurrentWorkingDirectory("a");
829   EXPECT_EQ(GetRealPath("b"), "a/b");
830   EXPECT_EQ(GetRealPath("../b"), "b");
831   EXPECT_EQ(GetRealPath("b/./c"), "a/b/c");
832 
833   FS.setCurrentWorkingDirectory("/a");
834   EXPECT_EQ(GetRealPath("b"), "/a/b");
835   EXPECT_EQ(GetRealPath("../b"), "/b");
836   EXPECT_EQ(GetRealPath("b/./c"), "/a/b/c");
837 }
838 #endif // _WIN32
839 
TEST_F(InMemoryFileSystemTest,AddFileWithUser)840 TEST_F(InMemoryFileSystemTest, AddFileWithUser) {
841   FS.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"), 0xFEEDFACE);
842   auto Stat = FS.status("/a");
843   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
844   ASSERT_TRUE(Stat->isDirectory());
845   ASSERT_EQ(0xFEEDFACE, Stat->getUser());
846   Stat = FS.status("/a/b");
847   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
848   ASSERT_TRUE(Stat->isDirectory());
849   ASSERT_EQ(0xFEEDFACE, Stat->getUser());
850   Stat = FS.status("/a/b/c");
851   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
852   ASSERT_TRUE(Stat->isRegularFile());
853   ASSERT_EQ(sys::fs::perms::all_all, Stat->getPermissions());
854   ASSERT_EQ(0xFEEDFACE, Stat->getUser());
855 }
856 
TEST_F(InMemoryFileSystemTest,AddFileWithGroup)857 TEST_F(InMemoryFileSystemTest, AddFileWithGroup) {
858   FS.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"), None, 0xDABBAD00);
859   auto Stat = FS.status("/a");
860   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
861   ASSERT_TRUE(Stat->isDirectory());
862   ASSERT_EQ(0xDABBAD00, Stat->getGroup());
863   Stat = FS.status("/a/b");
864   ASSERT_TRUE(Stat->isDirectory());
865   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
866   ASSERT_EQ(0xDABBAD00, Stat->getGroup());
867   Stat = FS.status("/a/b/c");
868   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
869   ASSERT_TRUE(Stat->isRegularFile());
870   ASSERT_EQ(sys::fs::perms::all_all, Stat->getPermissions());
871   ASSERT_EQ(0xDABBAD00, Stat->getGroup());
872 }
873 
TEST_F(InMemoryFileSystemTest,AddFileWithFileType)874 TEST_F(InMemoryFileSystemTest, AddFileWithFileType) {
875   FS.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"), None, None,
876              sys::fs::file_type::socket_file);
877   auto Stat = FS.status("/a");
878   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
879   ASSERT_TRUE(Stat->isDirectory());
880   Stat = FS.status("/a/b");
881   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
882   ASSERT_TRUE(Stat->isDirectory());
883   Stat = FS.status("/a/b/c");
884   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
885   ASSERT_EQ(sys::fs::file_type::socket_file, Stat->getType());
886   ASSERT_EQ(sys::fs::perms::all_all, Stat->getPermissions());
887 }
888 
TEST_F(InMemoryFileSystemTest,AddFileWithPerms)889 TEST_F(InMemoryFileSystemTest, AddFileWithPerms) {
890   FS.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"), None, None,
891              None, sys::fs::perms::owner_read | sys::fs::perms::owner_write);
892   auto Stat = FS.status("/a");
893   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
894   ASSERT_TRUE(Stat->isDirectory());
895   ASSERT_EQ(sys::fs::perms::owner_read | sys::fs::perms::owner_write |
896             sys::fs::perms::owner_exe, Stat->getPermissions());
897   Stat = FS.status("/a/b");
898   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
899   ASSERT_TRUE(Stat->isDirectory());
900   ASSERT_EQ(sys::fs::perms::owner_read | sys::fs::perms::owner_write |
901             sys::fs::perms::owner_exe, Stat->getPermissions());
902   Stat = FS.status("/a/b/c");
903   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
904   ASSERT_TRUE(Stat->isRegularFile());
905   ASSERT_EQ(sys::fs::perms::owner_read | sys::fs::perms::owner_write,
906             Stat->getPermissions());
907 }
908 
TEST_F(InMemoryFileSystemTest,AddDirectoryThenAddChild)909 TEST_F(InMemoryFileSystemTest, AddDirectoryThenAddChild) {
910   FS.addFile("/a", 0, MemoryBuffer::getMemBuffer(""), /*User=*/None,
911              /*Group=*/None, sys::fs::file_type::directory_file);
912   FS.addFile("/a/b", 0, MemoryBuffer::getMemBuffer("abc"), /*User=*/None,
913              /*Group=*/None, sys::fs::file_type::regular_file);
914   auto Stat = FS.status("/a");
915   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
916   ASSERT_TRUE(Stat->isDirectory());
917   Stat = FS.status("/a/b");
918   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
919   ASSERT_TRUE(Stat->isRegularFile());
920 }
921 
922 // NOTE: in the tests below, we use '//root/' as our root directory, since it is
923 // a legal *absolute* path on Windows as well as *nix.
924 class VFSFromYAMLTest : public ::testing::Test {
925 public:
926   int NumDiagnostics;
927 
SetUp()928   void SetUp() override { NumDiagnostics = 0; }
929 
CountingDiagHandler(const SMDiagnostic &,void * Context)930   static void CountingDiagHandler(const SMDiagnostic &, void *Context) {
931     VFSFromYAMLTest *Test = static_cast<VFSFromYAMLTest *>(Context);
932     ++Test->NumDiagnostics;
933   }
934 
935   IntrusiveRefCntPtr<vfs::FileSystem>
getFromYAMLRawString(StringRef Content,IntrusiveRefCntPtr<vfs::FileSystem> ExternalFS)936   getFromYAMLRawString(StringRef Content,
937                        IntrusiveRefCntPtr<vfs::FileSystem> ExternalFS) {
938     std::unique_ptr<MemoryBuffer> Buffer = MemoryBuffer::getMemBuffer(Content);
939     return getVFSFromYAML(std::move(Buffer), CountingDiagHandler, "", this,
940                           ExternalFS);
941   }
942 
getFromYAMLString(StringRef Content,IntrusiveRefCntPtr<vfs::FileSystem> ExternalFS=new DummyFileSystem ())943   IntrusiveRefCntPtr<vfs::FileSystem> getFromYAMLString(
944       StringRef Content,
945       IntrusiveRefCntPtr<vfs::FileSystem> ExternalFS = new DummyFileSystem()) {
946     std::string VersionPlusContent("{\n  'version':0,\n");
947     VersionPlusContent += Content.slice(Content.find('{') + 1, StringRef::npos);
948     return getFromYAMLRawString(VersionPlusContent, ExternalFS);
949   }
950 
951   // This is intended as a "XFAIL" for windows hosts.
supportsSameDirMultipleYAMLEntries()952   bool supportsSameDirMultipleYAMLEntries() {
953     Triple Host(Triple::normalize(sys::getProcessTriple()));
954     return !Host.isOSWindows();
955   }
956 };
957 
TEST_F(VFSFromYAMLTest,BasicVFSFromYAML)958 TEST_F(VFSFromYAMLTest, BasicVFSFromYAML) {
959   IntrusiveRefCntPtr<vfs::FileSystem> FS;
960   FS = getFromYAMLString("");
961   EXPECT_EQ(nullptr, FS.get());
962   FS = getFromYAMLString("[]");
963   EXPECT_EQ(nullptr, FS.get());
964   FS = getFromYAMLString("'string'");
965   EXPECT_EQ(nullptr, FS.get());
966   EXPECT_EQ(3, NumDiagnostics);
967 }
968 
TEST_F(VFSFromYAMLTest,MappedFiles)969 TEST_F(VFSFromYAMLTest, MappedFiles) {
970   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
971   Lower->addRegularFile("//root/foo/bar/a");
972   IntrusiveRefCntPtr<vfs::FileSystem> FS =
973       getFromYAMLString("{ 'roots': [\n"
974                         "{\n"
975                         "  'type': 'directory',\n"
976                         "  'name': '//root/',\n"
977                         "  'contents': [ {\n"
978                         "                  'type': 'file',\n"
979                         "                  'name': 'file1',\n"
980                         "                  'external-contents': '//root/foo/bar/a'\n"
981                         "                },\n"
982                         "                {\n"
983                         "                  'type': 'file',\n"
984                         "                  'name': 'file2',\n"
985                         "                  'external-contents': '//root/foo/b'\n"
986                         "                }\n"
987                         "              ]\n"
988                         "}\n"
989                         "]\n"
990                         "}",
991                         Lower);
992   ASSERT_TRUE(FS.get() != nullptr);
993 
994   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
995       new vfs::OverlayFileSystem(Lower));
996   O->pushOverlay(FS);
997 
998   // file
999   ErrorOr<vfs::Status> S = O->status("//root/file1");
1000   ASSERT_FALSE(S.getError());
1001   EXPECT_EQ("//root/foo/bar/a", S->getName());
1002   EXPECT_TRUE(S->IsVFSMapped);
1003 
1004   ErrorOr<vfs::Status> SLower = O->status("//root/foo/bar/a");
1005   EXPECT_EQ("//root/foo/bar/a", SLower->getName());
1006   EXPECT_TRUE(S->equivalent(*SLower));
1007   EXPECT_FALSE(SLower->IsVFSMapped);
1008 
1009   // file after opening
1010   auto OpenedF = O->openFileForRead("//root/file1");
1011   ASSERT_FALSE(OpenedF.getError());
1012   auto OpenedS = (*OpenedF)->status();
1013   ASSERT_FALSE(OpenedS.getError());
1014   EXPECT_EQ("//root/foo/bar/a", OpenedS->getName());
1015   EXPECT_TRUE(OpenedS->IsVFSMapped);
1016 
1017   // directory
1018   S = O->status("//root/");
1019   ASSERT_FALSE(S.getError());
1020   EXPECT_TRUE(S->isDirectory());
1021   EXPECT_TRUE(S->equivalent(*O->status("//root/"))); // non-volatile UniqueID
1022 
1023   // broken mapping
1024   EXPECT_EQ(O->status("//root/file2").getError(),
1025             llvm::errc::no_such_file_or_directory);
1026   EXPECT_EQ(0, NumDiagnostics);
1027 }
1028 
TEST_F(VFSFromYAMLTest,CaseInsensitive)1029 TEST_F(VFSFromYAMLTest, CaseInsensitive) {
1030   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
1031   Lower->addRegularFile("//root/foo/bar/a");
1032   IntrusiveRefCntPtr<vfs::FileSystem> FS =
1033       getFromYAMLString("{ 'case-sensitive': 'false',\n"
1034                         "  'roots': [\n"
1035                         "{\n"
1036                         "  'type': 'directory',\n"
1037                         "  'name': '//root/',\n"
1038                         "  'contents': [ {\n"
1039                         "                  'type': 'file',\n"
1040                         "                  'name': 'XX',\n"
1041                         "                  'external-contents': '//root/foo/bar/a'\n"
1042                         "                }\n"
1043                         "              ]\n"
1044                         "}]}",
1045                         Lower);
1046   ASSERT_TRUE(FS.get() != nullptr);
1047 
1048   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
1049       new vfs::OverlayFileSystem(Lower));
1050   O->pushOverlay(FS);
1051 
1052   ErrorOr<vfs::Status> S = O->status("//root/XX");
1053   ASSERT_FALSE(S.getError());
1054 
1055   ErrorOr<vfs::Status> SS = O->status("//root/xx");
1056   ASSERT_FALSE(SS.getError());
1057   EXPECT_TRUE(S->equivalent(*SS));
1058   SS = O->status("//root/xX");
1059   EXPECT_TRUE(S->equivalent(*SS));
1060   SS = O->status("//root/Xx");
1061   EXPECT_TRUE(S->equivalent(*SS));
1062   EXPECT_EQ(0, NumDiagnostics);
1063 }
1064 
TEST_F(VFSFromYAMLTest,CaseSensitive)1065 TEST_F(VFSFromYAMLTest, CaseSensitive) {
1066   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
1067   Lower->addRegularFile("//root/foo/bar/a");
1068   IntrusiveRefCntPtr<vfs::FileSystem> FS =
1069       getFromYAMLString("{ 'case-sensitive': 'true',\n"
1070                         "  'roots': [\n"
1071                         "{\n"
1072                         "  'type': 'directory',\n"
1073                         "  'name': '//root/',\n"
1074                         "  'contents': [ {\n"
1075                         "                  'type': 'file',\n"
1076                         "                  'name': 'XX',\n"
1077                         "                  'external-contents': '//root/foo/bar/a'\n"
1078                         "                }\n"
1079                         "              ]\n"
1080                         "}]}",
1081                         Lower);
1082   ASSERT_TRUE(FS.get() != nullptr);
1083 
1084   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
1085       new vfs::OverlayFileSystem(Lower));
1086   O->pushOverlay(FS);
1087 
1088   ErrorOr<vfs::Status> SS = O->status("//root/xx");
1089   EXPECT_EQ(SS.getError(), llvm::errc::no_such_file_or_directory);
1090   SS = O->status("//root/xX");
1091   EXPECT_EQ(SS.getError(), llvm::errc::no_such_file_or_directory);
1092   SS = O->status("//root/Xx");
1093   EXPECT_EQ(SS.getError(), llvm::errc::no_such_file_or_directory);
1094   EXPECT_EQ(0, NumDiagnostics);
1095 }
1096 
TEST_F(VFSFromYAMLTest,IllegalVFSFile)1097 TEST_F(VFSFromYAMLTest, IllegalVFSFile) {
1098   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
1099 
1100   // invalid YAML at top-level
1101   IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString("{]", Lower);
1102   EXPECT_EQ(nullptr, FS.get());
1103   // invalid YAML in roots
1104   FS = getFromYAMLString("{ 'roots':[}", Lower);
1105   // invalid YAML in directory
1106   FS = getFromYAMLString(
1107       "{ 'roots':[ { 'name': 'foo', 'type': 'directory', 'contents': [}",
1108       Lower);
1109   EXPECT_EQ(nullptr, FS.get());
1110 
1111   // invalid configuration
1112   FS = getFromYAMLString("{ 'knobular': 'true', 'roots':[] }", Lower);
1113   EXPECT_EQ(nullptr, FS.get());
1114   FS = getFromYAMLString("{ 'case-sensitive': 'maybe', 'roots':[] }", Lower);
1115   EXPECT_EQ(nullptr, FS.get());
1116 
1117   // invalid roots
1118   FS = getFromYAMLString("{ 'roots':'' }", Lower);
1119   EXPECT_EQ(nullptr, FS.get());
1120   FS = getFromYAMLString("{ 'roots':{} }", Lower);
1121   EXPECT_EQ(nullptr, FS.get());
1122 
1123   // invalid entries
1124   FS = getFromYAMLString(
1125       "{ 'roots':[ { 'type': 'other', 'name': 'me', 'contents': '' }", Lower);
1126   EXPECT_EQ(nullptr, FS.get());
1127   FS = getFromYAMLString("{ 'roots':[ { 'type': 'file', 'name': [], "
1128                          "'external-contents': 'other' }",
1129                          Lower);
1130   EXPECT_EQ(nullptr, FS.get());
1131   FS = getFromYAMLString(
1132       "{ 'roots':[ { 'type': 'file', 'name': 'me', 'external-contents': [] }",
1133       Lower);
1134   EXPECT_EQ(nullptr, FS.get());
1135   FS = getFromYAMLString(
1136       "{ 'roots':[ { 'type': 'file', 'name': 'me', 'external-contents': {} }",
1137       Lower);
1138   EXPECT_EQ(nullptr, FS.get());
1139   FS = getFromYAMLString(
1140       "{ 'roots':[ { 'type': 'directory', 'name': 'me', 'contents': {} }",
1141       Lower);
1142   EXPECT_EQ(nullptr, FS.get());
1143   FS = getFromYAMLString(
1144       "{ 'roots':[ { 'type': 'directory', 'name': 'me', 'contents': '' }",
1145       Lower);
1146   EXPECT_EQ(nullptr, FS.get());
1147   FS = getFromYAMLString(
1148       "{ 'roots':[ { 'thingy': 'directory', 'name': 'me', 'contents': [] }",
1149       Lower);
1150   EXPECT_EQ(nullptr, FS.get());
1151 
1152   // missing mandatory fields
1153   FS = getFromYAMLString("{ 'roots':[ { 'type': 'file', 'name': 'me' }", Lower);
1154   EXPECT_EQ(nullptr, FS.get());
1155   FS = getFromYAMLString(
1156       "{ 'roots':[ { 'type': 'file', 'external-contents': 'other' }", Lower);
1157   EXPECT_EQ(nullptr, FS.get());
1158   FS = getFromYAMLString("{ 'roots':[ { 'name': 'me', 'contents': [] }", Lower);
1159   EXPECT_EQ(nullptr, FS.get());
1160 
1161   // duplicate keys
1162   FS = getFromYAMLString("{ 'roots':[], 'roots':[] }", Lower);
1163   EXPECT_EQ(nullptr, FS.get());
1164   FS = getFromYAMLString(
1165       "{ 'case-sensitive':'true', 'case-sensitive':'true', 'roots':[] }",
1166       Lower);
1167   EXPECT_EQ(nullptr, FS.get());
1168   FS =
1169       getFromYAMLString("{ 'roots':[{'name':'me', 'name':'you', 'type':'file', "
1170                         "'external-contents':'blah' } ] }",
1171                         Lower);
1172   EXPECT_EQ(nullptr, FS.get());
1173 
1174   // missing version
1175   FS = getFromYAMLRawString("{ 'roots':[] }", Lower);
1176   EXPECT_EQ(nullptr, FS.get());
1177 
1178   // bad version number
1179   FS = getFromYAMLRawString("{ 'version':'foo', 'roots':[] }", Lower);
1180   EXPECT_EQ(nullptr, FS.get());
1181   FS = getFromYAMLRawString("{ 'version':-1, 'roots':[] }", Lower);
1182   EXPECT_EQ(nullptr, FS.get());
1183   FS = getFromYAMLRawString("{ 'version':100000, 'roots':[] }", Lower);
1184   EXPECT_EQ(nullptr, FS.get());
1185   EXPECT_EQ(24, NumDiagnostics);
1186 }
1187 
TEST_F(VFSFromYAMLTest,UseExternalName)1188 TEST_F(VFSFromYAMLTest, UseExternalName) {
1189   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
1190   Lower->addRegularFile("//root/external/file");
1191 
1192   IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
1193       "{ 'roots': [\n"
1194       "  { 'type': 'file', 'name': '//root/A',\n"
1195       "    'external-contents': '//root/external/file'\n"
1196       "  },\n"
1197       "  { 'type': 'file', 'name': '//root/B',\n"
1198       "    'use-external-name': true,\n"
1199       "    'external-contents': '//root/external/file'\n"
1200       "  },\n"
1201       "  { 'type': 'file', 'name': '//root/C',\n"
1202       "    'use-external-name': false,\n"
1203       "    'external-contents': '//root/external/file'\n"
1204       "  }\n"
1205       "] }", Lower);
1206   ASSERT_TRUE(nullptr != FS.get());
1207 
1208   // default true
1209   EXPECT_EQ("//root/external/file", FS->status("//root/A")->getName());
1210   // explicit
1211   EXPECT_EQ("//root/external/file", FS->status("//root/B")->getName());
1212   EXPECT_EQ("//root/C", FS->status("//root/C")->getName());
1213 
1214   // global configuration
1215   FS = getFromYAMLString(
1216       "{ 'use-external-names': false,\n"
1217       "  'roots': [\n"
1218       "  { 'type': 'file', 'name': '//root/A',\n"
1219       "    'external-contents': '//root/external/file'\n"
1220       "  },\n"
1221       "  { 'type': 'file', 'name': '//root/B',\n"
1222       "    'use-external-name': true,\n"
1223       "    'external-contents': '//root/external/file'\n"
1224       "  },\n"
1225       "  { 'type': 'file', 'name': '//root/C',\n"
1226       "    'use-external-name': false,\n"
1227       "    'external-contents': '//root/external/file'\n"
1228       "  }\n"
1229       "] }", Lower);
1230   ASSERT_TRUE(nullptr != FS.get());
1231 
1232   // default
1233   EXPECT_EQ("//root/A", FS->status("//root/A")->getName());
1234   // explicit
1235   EXPECT_EQ("//root/external/file", FS->status("//root/B")->getName());
1236   EXPECT_EQ("//root/C", FS->status("//root/C")->getName());
1237 }
1238 
TEST_F(VFSFromYAMLTest,MultiComponentPath)1239 TEST_F(VFSFromYAMLTest, MultiComponentPath) {
1240   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
1241   Lower->addRegularFile("//root/other");
1242 
1243   // file in roots
1244   IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
1245       "{ 'roots': [\n"
1246       "  { 'type': 'file', 'name': '//root/path/to/file',\n"
1247       "    'external-contents': '//root/other' }]\n"
1248       "}", Lower);
1249   ASSERT_TRUE(nullptr != FS.get());
1250   EXPECT_FALSE(FS->status("//root/path/to/file").getError());
1251   EXPECT_FALSE(FS->status("//root/path/to").getError());
1252   EXPECT_FALSE(FS->status("//root/path").getError());
1253   EXPECT_FALSE(FS->status("//root/").getError());
1254 
1255   // at the start
1256   FS = getFromYAMLString(
1257       "{ 'roots': [\n"
1258       "  { 'type': 'directory', 'name': '//root/path/to',\n"
1259       "    'contents': [ { 'type': 'file', 'name': 'file',\n"
1260       "                    'external-contents': '//root/other' }]}]\n"
1261       "}", Lower);
1262   ASSERT_TRUE(nullptr != FS.get());
1263   EXPECT_FALSE(FS->status("//root/path/to/file").getError());
1264   EXPECT_FALSE(FS->status("//root/path/to").getError());
1265   EXPECT_FALSE(FS->status("//root/path").getError());
1266   EXPECT_FALSE(FS->status("//root/").getError());
1267 
1268   // at the end
1269   FS = getFromYAMLString(
1270       "{ 'roots': [\n"
1271       "  { 'type': 'directory', 'name': '//root/',\n"
1272       "    'contents': [ { 'type': 'file', 'name': 'path/to/file',\n"
1273       "                    'external-contents': '//root/other' }]}]\n"
1274       "}", Lower);
1275   ASSERT_TRUE(nullptr != FS.get());
1276   EXPECT_FALSE(FS->status("//root/path/to/file").getError());
1277   EXPECT_FALSE(FS->status("//root/path/to").getError());
1278   EXPECT_FALSE(FS->status("//root/path").getError());
1279   EXPECT_FALSE(FS->status("//root/").getError());
1280 }
1281 
TEST_F(VFSFromYAMLTest,TrailingSlashes)1282 TEST_F(VFSFromYAMLTest, TrailingSlashes) {
1283   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
1284   Lower->addRegularFile("//root/other");
1285 
1286   // file in roots
1287   IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
1288       "{ 'roots': [\n"
1289       "  { 'type': 'directory', 'name': '//root/path/to////',\n"
1290       "    'contents': [ { 'type': 'file', 'name': 'file',\n"
1291       "                    'external-contents': '//root/other' }]}]\n"
1292       "}", Lower);
1293   ASSERT_TRUE(nullptr != FS.get());
1294   EXPECT_FALSE(FS->status("//root/path/to/file").getError());
1295   EXPECT_FALSE(FS->status("//root/path/to").getError());
1296   EXPECT_FALSE(FS->status("//root/path").getError());
1297   EXPECT_FALSE(FS->status("//root/").getError());
1298 }
1299 
TEST_F(VFSFromYAMLTest,DirectoryIteration)1300 TEST_F(VFSFromYAMLTest, DirectoryIteration) {
1301   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
1302   Lower->addDirectory("//root/");
1303   Lower->addDirectory("//root/foo");
1304   Lower->addDirectory("//root/foo/bar");
1305   Lower->addRegularFile("//root/foo/bar/a");
1306   Lower->addRegularFile("//root/foo/bar/b");
1307   Lower->addRegularFile("//root/file3");
1308   IntrusiveRefCntPtr<vfs::FileSystem> FS =
1309   getFromYAMLString("{ 'use-external-names': false,\n"
1310                     "  'roots': [\n"
1311                     "{\n"
1312                     "  'type': 'directory',\n"
1313                     "  'name': '//root/',\n"
1314                     "  'contents': [ {\n"
1315                     "                  'type': 'file',\n"
1316                     "                  'name': 'file1',\n"
1317                     "                  'external-contents': '//root/foo/bar/a'\n"
1318                     "                },\n"
1319                     "                {\n"
1320                     "                  'type': 'file',\n"
1321                     "                  'name': 'file2',\n"
1322                     "                  'external-contents': '//root/foo/bar/b'\n"
1323                     "                }\n"
1324                     "              ]\n"
1325                     "}\n"
1326                     "]\n"
1327                     "}",
1328                     Lower);
1329   ASSERT_TRUE(FS.get() != nullptr);
1330 
1331   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
1332       new vfs::OverlayFileSystem(Lower));
1333   O->pushOverlay(FS);
1334 
1335   std::error_code EC;
1336   checkContents(O->dir_begin("//root/", EC),
1337                 {"//root/file1", "//root/file2", "//root/file3", "//root/foo"});
1338 
1339   checkContents(O->dir_begin("//root/foo/bar", EC),
1340                 {"//root/foo/bar/a", "//root/foo/bar/b"});
1341 }
1342 
TEST_F(VFSFromYAMLTest,DirectoryIterationSameDirMultipleEntries)1343 TEST_F(VFSFromYAMLTest, DirectoryIterationSameDirMultipleEntries) {
1344   // https://llvm.org/bugs/show_bug.cgi?id=27725
1345   if (!supportsSameDirMultipleYAMLEntries())
1346     return;
1347 
1348   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
1349   Lower->addDirectory("//root/zab");
1350   Lower->addDirectory("//root/baz");
1351   Lower->addRegularFile("//root/zab/a");
1352   Lower->addRegularFile("//root/zab/b");
1353   IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
1354       "{ 'use-external-names': false,\n"
1355       "  'roots': [\n"
1356       "{\n"
1357       "  'type': 'directory',\n"
1358       "  'name': '//root/baz/',\n"
1359       "  'contents': [ {\n"
1360       "                  'type': 'file',\n"
1361       "                  'name': 'x',\n"
1362       "                  'external-contents': '//root/zab/a'\n"
1363       "                }\n"
1364       "              ]\n"
1365       "},\n"
1366       "{\n"
1367       "  'type': 'directory',\n"
1368       "  'name': '//root/baz/',\n"
1369       "  'contents': [ {\n"
1370       "                  'type': 'file',\n"
1371       "                  'name': 'y',\n"
1372       "                  'external-contents': '//root/zab/b'\n"
1373       "                }\n"
1374       "              ]\n"
1375       "}\n"
1376       "]\n"
1377       "}",
1378       Lower);
1379   ASSERT_TRUE(FS.get() != nullptr);
1380 
1381   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
1382       new vfs::OverlayFileSystem(Lower));
1383   O->pushOverlay(FS);
1384 
1385   std::error_code EC;
1386 
1387   checkContents(O->dir_begin("//root/baz/", EC),
1388                 {"//root/baz/x", "//root/baz/y"});
1389 }
1390 
TEST_F(VFSFromYAMLTest,RecursiveDirectoryIterationLevel)1391 TEST_F(VFSFromYAMLTest, RecursiveDirectoryIterationLevel) {
1392 
1393   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
1394   Lower->addDirectory("//root/a");
1395   Lower->addDirectory("//root/a/b");
1396   Lower->addDirectory("//root/a/b/c");
1397   Lower->addRegularFile("//root/a/b/c/file");
1398   IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
1399       "{ 'use-external-names': false,\n"
1400       "  'roots': [\n"
1401       "{\n"
1402       "  'type': 'directory',\n"
1403       "  'name': '//root/a/b/c/',\n"
1404       "  'contents': [ {\n"
1405       "                  'type': 'file',\n"
1406       "                  'name': 'file',\n"
1407       "                  'external-contents': '//root/a/b/c/file'\n"
1408       "                }\n"
1409       "              ]\n"
1410       "},\n"
1411       "]\n"
1412       "}",
1413       Lower);
1414   ASSERT_TRUE(FS.get() != nullptr);
1415 
1416   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
1417       new vfs::OverlayFileSystem(Lower));
1418   O->pushOverlay(FS);
1419 
1420   std::error_code EC;
1421 
1422   // Test recursive_directory_iterator level()
1423   vfs::recursive_directory_iterator I = vfs::recursive_directory_iterator(
1424                                         *O, "//root", EC), E;
1425   ASSERT_FALSE(EC);
1426   for (int l = 0; I != E; I.increment(EC), ++l) {
1427     ASSERT_FALSE(EC);
1428     EXPECT_EQ(I.level(), l);
1429   }
1430   EXPECT_EQ(I, E);
1431 }
1432