BuildBot fix, compiler complains about array decay to pointer
[llvm-core.git] / unittests / Support / VirtualFileSystemTest.cpp
blob58d928516f9d39fbc7ee2bf44eb2f6e419336628
1 //===- unittests/Support/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 //===----------------------------------------------------------------------===//
10 #include "llvm/Support/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/Path.h"
17 #include "llvm/Support/SourceMgr.h"
18 #include "gmock/gmock.h"
19 #include "gtest/gtest.h"
20 #include <map>
21 #include <string>
23 using namespace llvm;
24 using llvm::sys::fs::UniqueID;
25 using testing::ElementsAre;
26 using testing::Pair;
27 using testing::UnorderedElementsAre;
29 namespace {
30 struct DummyFile : public vfs::File {
31 vfs::Status S;
32 explicit DummyFile(vfs::Status S) : S(S) {}
33 llvm::ErrorOr<vfs::Status> status() override { return S; }
34 llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
35 getBuffer(const Twine &Name, int64_t FileSize, bool RequiresNullTerminator,
36 bool IsVolatile) override {
37 llvm_unreachable("unimplemented");
39 std::error_code close() override { return std::error_code(); }
42 class DummyFileSystem : public vfs::FileSystem {
43 int FSID; // used to produce UniqueIDs
44 int FileID; // used to produce UniqueIDs
45 std::map<std::string, vfs::Status> FilesAndDirs;
47 static int getNextFSID() {
48 static int Count = 0;
49 return Count++;
52 public:
53 DummyFileSystem() : FSID(getNextFSID()), FileID(0) {}
55 ErrorOr<vfs::Status> status(const Twine &Path) override {
56 std::map<std::string, vfs::Status>::iterator I =
57 FilesAndDirs.find(Path.str());
58 if (I == FilesAndDirs.end())
59 return make_error_code(llvm::errc::no_such_file_or_directory);
60 return I->second;
62 ErrorOr<std::unique_ptr<vfs::File>>
63 openFileForRead(const Twine &Path) override {
64 auto S = status(Path);
65 if (S)
66 return std::unique_ptr<vfs::File>(new DummyFile{*S});
67 return S.getError();
69 llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const override {
70 return std::string();
72 std::error_code setCurrentWorkingDirectory(const Twine &Path) override {
73 return std::error_code();
75 // Map any symlink to "/symlink".
76 std::error_code getRealPath(const Twine &Path,
77 SmallVectorImpl<char> &Output) const override {
78 auto I = FilesAndDirs.find(Path.str());
79 if (I == FilesAndDirs.end())
80 return make_error_code(llvm::errc::no_such_file_or_directory);
81 if (I->second.isSymlink()) {
82 Output.clear();
83 Twine("/symlink").toVector(Output);
84 return std::error_code();
86 Output.clear();
87 Path.toVector(Output);
88 return std::error_code();
91 struct DirIterImpl : public llvm::vfs::detail::DirIterImpl {
92 std::map<std::string, vfs::Status> &FilesAndDirs;
93 std::map<std::string, vfs::Status>::iterator I;
94 std::string Path;
95 bool isInPath(StringRef S) {
96 if (Path.size() < S.size() && S.find(Path) == 0) {
97 auto LastSep = S.find_last_of('/');
98 if (LastSep == Path.size() || LastSep == Path.size() - 1)
99 return true;
101 return false;
103 DirIterImpl(std::map<std::string, vfs::Status> &FilesAndDirs,
104 const Twine &_Path)
105 : FilesAndDirs(FilesAndDirs), I(FilesAndDirs.begin()),
106 Path(_Path.str()) {
107 for (; I != FilesAndDirs.end(); ++I) {
108 if (isInPath(I->first)) {
109 CurrentEntry =
110 vfs::directory_entry(I->second.getName(), I->second.getType());
111 break;
115 std::error_code increment() override {
116 ++I;
117 for (; I != FilesAndDirs.end(); ++I) {
118 if (isInPath(I->first)) {
119 CurrentEntry =
120 vfs::directory_entry(I->second.getName(), I->second.getType());
121 break;
124 if (I == FilesAndDirs.end())
125 CurrentEntry = vfs::directory_entry();
126 return std::error_code();
130 vfs::directory_iterator dir_begin(const Twine &Dir,
131 std::error_code &EC) override {
132 return vfs::directory_iterator(
133 std::make_shared<DirIterImpl>(FilesAndDirs, Dir));
136 void addEntry(StringRef Path, const vfs::Status &Status) {
137 FilesAndDirs[Path] = Status;
140 void addRegularFile(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, 1024,
143 sys::fs::file_type::regular_file, Perms);
144 addEntry(Path, S);
147 void addDirectory(StringRef Path, sys::fs::perms Perms = sys::fs::all_all) {
148 vfs::Status S(Path, UniqueID(FSID, FileID++),
149 std::chrono::system_clock::now(), 0, 0, 0,
150 sys::fs::file_type::directory_file, Perms);
151 addEntry(Path, S);
154 void addSymlink(StringRef Path) {
155 vfs::Status S(Path, UniqueID(FSID, FileID++),
156 std::chrono::system_clock::now(), 0, 0, 0,
157 sys::fs::file_type::symlink_file, sys::fs::all_all);
158 addEntry(Path, S);
162 /// Replace back-slashes by front-slashes.
163 std::string getPosixPath(std::string S) {
164 SmallString<128> Result;
165 llvm::sys::path::native(S, Result, llvm::sys::path::Style::posix);
166 return Result.str();
168 } // end anonymous namespace
170 TEST(VirtualFileSystemTest, StatusQueries) {
171 IntrusiveRefCntPtr<DummyFileSystem> D(new DummyFileSystem());
172 ErrorOr<vfs::Status> Status((std::error_code()));
174 D->addRegularFile("/foo");
175 Status = D->status("/foo");
176 ASSERT_FALSE(Status.getError());
177 EXPECT_TRUE(Status->isStatusKnown());
178 EXPECT_FALSE(Status->isDirectory());
179 EXPECT_TRUE(Status->isRegularFile());
180 EXPECT_FALSE(Status->isSymlink());
181 EXPECT_FALSE(Status->isOther());
182 EXPECT_TRUE(Status->exists());
184 D->addDirectory("/bar");
185 Status = D->status("/bar");
186 ASSERT_FALSE(Status.getError());
187 EXPECT_TRUE(Status->isStatusKnown());
188 EXPECT_TRUE(Status->isDirectory());
189 EXPECT_FALSE(Status->isRegularFile());
190 EXPECT_FALSE(Status->isSymlink());
191 EXPECT_FALSE(Status->isOther());
192 EXPECT_TRUE(Status->exists());
194 D->addSymlink("/baz");
195 Status = D->status("/baz");
196 ASSERT_FALSE(Status.getError());
197 EXPECT_TRUE(Status->isStatusKnown());
198 EXPECT_FALSE(Status->isDirectory());
199 EXPECT_FALSE(Status->isRegularFile());
200 EXPECT_TRUE(Status->isSymlink());
201 EXPECT_FALSE(Status->isOther());
202 EXPECT_TRUE(Status->exists());
204 EXPECT_TRUE(Status->equivalent(*Status));
205 ErrorOr<vfs::Status> Status2 = D->status("/foo");
206 ASSERT_FALSE(Status2.getError());
207 EXPECT_FALSE(Status->equivalent(*Status2));
210 TEST(VirtualFileSystemTest, BaseOnlyOverlay) {
211 IntrusiveRefCntPtr<DummyFileSystem> D(new DummyFileSystem());
212 ErrorOr<vfs::Status> Status((std::error_code()));
213 EXPECT_FALSE(Status = D->status("/foo"));
215 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(new vfs::OverlayFileSystem(D));
216 EXPECT_FALSE(Status = O->status("/foo"));
218 D->addRegularFile("/foo");
219 Status = D->status("/foo");
220 EXPECT_FALSE(Status.getError());
222 ErrorOr<vfs::Status> Status2((std::error_code()));
223 Status2 = O->status("/foo");
224 EXPECT_FALSE(Status2.getError());
225 EXPECT_TRUE(Status->equivalent(*Status2));
228 TEST(VirtualFileSystemTest, GetRealPathInOverlay) {
229 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
230 Lower->addRegularFile("/foo");
231 Lower->addSymlink("/lower_link");
232 IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem());
234 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
235 new vfs::OverlayFileSystem(Lower));
236 O->pushOverlay(Upper);
238 // Regular file.
239 SmallString<16> RealPath;
240 EXPECT_FALSE(O->getRealPath("/foo", RealPath));
241 EXPECT_EQ(RealPath.str(), "/foo");
243 // Expect no error getting real path for symlink in lower overlay.
244 EXPECT_FALSE(O->getRealPath("/lower_link", RealPath));
245 EXPECT_EQ(RealPath.str(), "/symlink");
247 // Try a non-existing link.
248 EXPECT_EQ(O->getRealPath("/upper_link", RealPath),
249 errc::no_such_file_or_directory);
251 // Add a new symlink in upper.
252 Upper->addSymlink("/upper_link");
253 EXPECT_FALSE(O->getRealPath("/upper_link", RealPath));
254 EXPECT_EQ(RealPath.str(), "/symlink");
257 TEST(VirtualFileSystemTest, OverlayFiles) {
258 IntrusiveRefCntPtr<DummyFileSystem> Base(new DummyFileSystem());
259 IntrusiveRefCntPtr<DummyFileSystem> Middle(new DummyFileSystem());
260 IntrusiveRefCntPtr<DummyFileSystem> Top(new DummyFileSystem());
261 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
262 new vfs::OverlayFileSystem(Base));
263 O->pushOverlay(Middle);
264 O->pushOverlay(Top);
266 ErrorOr<vfs::Status> Status1((std::error_code())),
267 Status2((std::error_code())), Status3((std::error_code())),
268 StatusB((std::error_code())), StatusM((std::error_code())),
269 StatusT((std::error_code()));
271 Base->addRegularFile("/foo");
272 StatusB = Base->status("/foo");
273 ASSERT_FALSE(StatusB.getError());
274 Status1 = O->status("/foo");
275 ASSERT_FALSE(Status1.getError());
276 Middle->addRegularFile("/foo");
277 StatusM = Middle->status("/foo");
278 ASSERT_FALSE(StatusM.getError());
279 Status2 = O->status("/foo");
280 ASSERT_FALSE(Status2.getError());
281 Top->addRegularFile("/foo");
282 StatusT = Top->status("/foo");
283 ASSERT_FALSE(StatusT.getError());
284 Status3 = O->status("/foo");
285 ASSERT_FALSE(Status3.getError());
287 EXPECT_TRUE(Status1->equivalent(*StatusB));
288 EXPECT_TRUE(Status2->equivalent(*StatusM));
289 EXPECT_TRUE(Status3->equivalent(*StatusT));
291 EXPECT_FALSE(Status1->equivalent(*Status2));
292 EXPECT_FALSE(Status2->equivalent(*Status3));
293 EXPECT_FALSE(Status1->equivalent(*Status3));
296 TEST(VirtualFileSystemTest, OverlayDirsNonMerged) {
297 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
298 IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem());
299 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
300 new vfs::OverlayFileSystem(Lower));
301 O->pushOverlay(Upper);
303 Lower->addDirectory("/lower-only");
304 Upper->addDirectory("/upper-only");
306 // non-merged paths should be the same
307 ErrorOr<vfs::Status> Status1 = Lower->status("/lower-only");
308 ASSERT_FALSE(Status1.getError());
309 ErrorOr<vfs::Status> Status2 = O->status("/lower-only");
310 ASSERT_FALSE(Status2.getError());
311 EXPECT_TRUE(Status1->equivalent(*Status2));
313 Status1 = Upper->status("/upper-only");
314 ASSERT_FALSE(Status1.getError());
315 Status2 = O->status("/upper-only");
316 ASSERT_FALSE(Status2.getError());
317 EXPECT_TRUE(Status1->equivalent(*Status2));
320 TEST(VirtualFileSystemTest, MergedDirPermissions) {
321 // merged directories get the permissions of the upper dir
322 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
323 IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem());
324 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
325 new vfs::OverlayFileSystem(Lower));
326 O->pushOverlay(Upper);
328 ErrorOr<vfs::Status> Status((std::error_code()));
329 Lower->addDirectory("/both", sys::fs::owner_read);
330 Upper->addDirectory("/both", sys::fs::owner_all | sys::fs::group_read);
331 Status = O->status("/both");
332 ASSERT_FALSE(Status.getError());
333 EXPECT_EQ(0740, Status->getPermissions());
335 // permissions (as usual) are not recursively applied
336 Lower->addRegularFile("/both/foo", sys::fs::owner_read);
337 Upper->addRegularFile("/both/bar", sys::fs::owner_write);
338 Status = O->status("/both/foo");
339 ASSERT_FALSE(Status.getError());
340 EXPECT_EQ(0400, Status->getPermissions());
341 Status = O->status("/both/bar");
342 ASSERT_FALSE(Status.getError());
343 EXPECT_EQ(0200, Status->getPermissions());
346 namespace {
347 struct ScopedDir {
348 SmallString<128> Path;
349 ScopedDir(const Twine &Name, bool Unique = false) {
350 std::error_code EC;
351 if (Unique) {
352 EC = llvm::sys::fs::createUniqueDirectory(Name, Path);
353 } else {
354 Path = Name.str();
355 EC = llvm::sys::fs::create_directory(Twine(Path));
357 if (EC)
358 Path = "";
359 EXPECT_FALSE(EC);
361 ~ScopedDir() {
362 if (Path != "") {
363 EXPECT_FALSE(llvm::sys::fs::remove(Path.str()));
366 operator StringRef() { return Path.str(); }
369 struct ScopedLink {
370 SmallString<128> Path;
371 ScopedLink(const Twine &To, const Twine &From) {
372 Path = From.str();
373 std::error_code EC = sys::fs::create_link(To, From);
374 if (EC)
375 Path = "";
376 EXPECT_FALSE(EC);
378 ~ScopedLink() {
379 if (Path != "") {
380 EXPECT_FALSE(llvm::sys::fs::remove(Path.str()));
383 operator StringRef() { return Path.str(); }
385 } // end anonymous namespace
387 TEST(VirtualFileSystemTest, BasicRealFSIteration) {
388 ScopedDir TestDirectory("virtual-file-system-test", /*Unique*/ true);
389 IntrusiveRefCntPtr<vfs::FileSystem> FS = vfs::getRealFileSystem();
391 std::error_code EC;
392 vfs::directory_iterator I = FS->dir_begin(Twine(TestDirectory), EC);
393 ASSERT_FALSE(EC);
394 EXPECT_EQ(vfs::directory_iterator(), I); // empty directory is empty
396 ScopedDir _a(TestDirectory + "/a");
397 ScopedDir _ab(TestDirectory + "/a/b");
398 ScopedDir _c(TestDirectory + "/c");
399 ScopedDir _cd(TestDirectory + "/c/d");
401 I = FS->dir_begin(Twine(TestDirectory), EC);
402 ASSERT_FALSE(EC);
403 ASSERT_NE(vfs::directory_iterator(), I);
404 // Check either a or c, since we can't rely on the iteration order.
405 EXPECT_TRUE(I->path().endswith("a") || I->path().endswith("c"));
406 I.increment(EC);
407 ASSERT_FALSE(EC);
408 ASSERT_NE(vfs::directory_iterator(), I);
409 EXPECT_TRUE(I->path().endswith("a") || I->path().endswith("c"));
410 I.increment(EC);
411 EXPECT_EQ(vfs::directory_iterator(), I);
414 #ifdef LLVM_ON_UNIX
415 TEST(VirtualFileSystemTest, BrokenSymlinkRealFSIteration) {
416 ScopedDir TestDirectory("virtual-file-system-test", /*Unique*/ true);
417 IntrusiveRefCntPtr<vfs::FileSystem> FS = vfs::getRealFileSystem();
419 ScopedLink _a("no_such_file", TestDirectory + "/a");
420 ScopedDir _b(TestDirectory + "/b");
421 ScopedLink _c("no_such_file", TestDirectory + "/c");
423 // Should get no iteration error, but a stat error for the broken symlinks.
424 std::map<std::string, std::error_code> StatResults;
425 std::error_code EC;
426 for (vfs::directory_iterator I = FS->dir_begin(Twine(TestDirectory), EC), E;
427 I != E; I.increment(EC)) {
428 EXPECT_FALSE(EC);
429 StatResults[sys::path::filename(I->path())] =
430 FS->status(I->path()).getError();
432 EXPECT_THAT(
433 StatResults,
434 ElementsAre(
435 Pair("a", std::make_error_code(std::errc::no_such_file_or_directory)),
436 Pair("b", std::error_code()),
437 Pair("c",
438 std::make_error_code(std::errc::no_such_file_or_directory))));
440 #endif
442 TEST(VirtualFileSystemTest, BasicRealFSRecursiveIteration) {
443 ScopedDir TestDirectory("virtual-file-system-test", /*Unique*/ true);
444 IntrusiveRefCntPtr<vfs::FileSystem> FS = vfs::getRealFileSystem();
446 std::error_code EC;
447 auto I = vfs::recursive_directory_iterator(*FS, Twine(TestDirectory), EC);
448 ASSERT_FALSE(EC);
449 EXPECT_EQ(vfs::recursive_directory_iterator(), I); // empty directory is empty
451 ScopedDir _a(TestDirectory + "/a");
452 ScopedDir _ab(TestDirectory + "/a/b");
453 ScopedDir _c(TestDirectory + "/c");
454 ScopedDir _cd(TestDirectory + "/c/d");
456 I = vfs::recursive_directory_iterator(*FS, Twine(TestDirectory), EC);
457 ASSERT_FALSE(EC);
458 ASSERT_NE(vfs::recursive_directory_iterator(), I);
460 std::vector<std::string> Contents;
461 for (auto E = vfs::recursive_directory_iterator(); !EC && I != E;
462 I.increment(EC)) {
463 Contents.push_back(I->path());
466 // Check contents, which may be in any order
467 EXPECT_EQ(4U, Contents.size());
468 int Counts[4] = {0, 0, 0, 0};
469 for (const std::string &Name : Contents) {
470 ASSERT_FALSE(Name.empty());
471 int Index = Name[Name.size() - 1] - 'a';
472 ASSERT_TRUE(Index >= 0 && Index < 4);
473 Counts[Index]++;
475 EXPECT_EQ(1, Counts[0]); // a
476 EXPECT_EQ(1, Counts[1]); // b
477 EXPECT_EQ(1, Counts[2]); // c
478 EXPECT_EQ(1, Counts[3]); // d
481 #ifdef LLVM_ON_UNIX
482 TEST(VirtualFileSystemTest, BrokenSymlinkRealFSRecursiveIteration) {
483 ScopedDir TestDirectory("virtual-file-system-test", /*Unique*/ true);
484 IntrusiveRefCntPtr<vfs::FileSystem> FS = vfs::getRealFileSystem();
486 ScopedLink _a("no_such_file", TestDirectory + "/a");
487 ScopedDir _b(TestDirectory + "/b");
488 ScopedLink _ba("no_such_file", TestDirectory + "/b/a");
489 ScopedDir _bb(TestDirectory + "/b/b");
490 ScopedLink _bc("no_such_file", TestDirectory + "/b/c");
491 ScopedLink _c("no_such_file", TestDirectory + "/c");
492 ScopedDir _d(TestDirectory + "/d");
493 ScopedDir _dd(TestDirectory + "/d/d");
494 ScopedDir _ddd(TestDirectory + "/d/d/d");
495 ScopedLink _e("no_such_file", TestDirectory + "/e");
497 std::vector<std::string> VisitedBrokenSymlinks;
498 std::vector<std::string> VisitedNonBrokenSymlinks;
499 std::error_code EC;
500 for (vfs::recursive_directory_iterator I(*FS, Twine(TestDirectory), EC), E;
501 I != E; I.increment(EC)) {
502 EXPECT_FALSE(EC);
503 (FS->status(I->path()) ? VisitedNonBrokenSymlinks : VisitedBrokenSymlinks)
504 .push_back(I->path());
507 // Check visited file names.
508 EXPECT_THAT(VisitedBrokenSymlinks,
509 UnorderedElementsAre(StringRef(_a), StringRef(_ba),
510 StringRef(_bc), StringRef(_c),
511 StringRef(_e)));
512 EXPECT_THAT(VisitedNonBrokenSymlinks,
513 UnorderedElementsAre(StringRef(_b), StringRef(_bb), StringRef(_d),
514 StringRef(_dd), StringRef(_ddd)));
516 #endif
518 template <typename DirIter>
519 static void checkContents(DirIter I, ArrayRef<StringRef> ExpectedOut) {
520 std::error_code EC;
521 SmallVector<StringRef, 4> Expected(ExpectedOut.begin(), ExpectedOut.end());
522 SmallVector<std::string, 4> InputToCheck;
524 // Do not rely on iteration order to check for contents, sort both
525 // content vectors before comparison.
526 for (DirIter E; !EC && I != E; I.increment(EC))
527 InputToCheck.push_back(I->path());
529 llvm::sort(InputToCheck);
530 llvm::sort(Expected);
531 EXPECT_EQ(InputToCheck.size(), Expected.size());
533 unsigned LastElt = std::min(InputToCheck.size(), Expected.size());
534 for (unsigned Idx = 0; Idx != LastElt; ++Idx)
535 EXPECT_EQ(StringRef(InputToCheck[Idx]), Expected[Idx]);
538 TEST(VirtualFileSystemTest, OverlayIteration) {
539 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
540 IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem());
541 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
542 new vfs::OverlayFileSystem(Lower));
543 O->pushOverlay(Upper);
545 std::error_code EC;
546 checkContents(O->dir_begin("/", EC), ArrayRef<StringRef>());
548 Lower->addRegularFile("/file1");
549 checkContents(O->dir_begin("/", EC), ArrayRef<StringRef>("/file1"));
551 Upper->addRegularFile("/file2");
552 checkContents(O->dir_begin("/", EC), {"/file2", "/file1"});
554 Lower->addDirectory("/dir1");
555 Lower->addRegularFile("/dir1/foo");
556 Upper->addDirectory("/dir2");
557 Upper->addRegularFile("/dir2/foo");
558 checkContents(O->dir_begin("/dir2", EC), ArrayRef<StringRef>("/dir2/foo"));
559 checkContents(O->dir_begin("/", EC), {"/dir2", "/file2", "/dir1", "/file1"});
562 TEST(VirtualFileSystemTest, OverlayRecursiveIteration) {
563 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
564 IntrusiveRefCntPtr<DummyFileSystem> Middle(new DummyFileSystem());
565 IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem());
566 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
567 new vfs::OverlayFileSystem(Lower));
568 O->pushOverlay(Middle);
569 O->pushOverlay(Upper);
571 std::error_code EC;
572 checkContents(vfs::recursive_directory_iterator(*O, "/", EC),
573 ArrayRef<StringRef>());
575 Lower->addRegularFile("/file1");
576 checkContents(vfs::recursive_directory_iterator(*O, "/", EC),
577 ArrayRef<StringRef>("/file1"));
579 Upper->addDirectory("/dir");
580 Upper->addRegularFile("/dir/file2");
581 checkContents(vfs::recursive_directory_iterator(*O, "/", EC),
582 {"/dir", "/dir/file2", "/file1"});
584 Lower->addDirectory("/dir1");
585 Lower->addRegularFile("/dir1/foo");
586 Lower->addDirectory("/dir1/a");
587 Lower->addRegularFile("/dir1/a/b");
588 Middle->addDirectory("/a");
589 Middle->addDirectory("/a/b");
590 Middle->addDirectory("/a/b/c");
591 Middle->addRegularFile("/a/b/c/d");
592 Middle->addRegularFile("/hiddenByUp");
593 Upper->addDirectory("/dir2");
594 Upper->addRegularFile("/dir2/foo");
595 Upper->addRegularFile("/hiddenByUp");
596 checkContents(vfs::recursive_directory_iterator(*O, "/dir2", EC),
597 ArrayRef<StringRef>("/dir2/foo"));
598 checkContents(vfs::recursive_directory_iterator(*O, "/", EC),
599 {"/dir", "/dir/file2", "/dir2", "/dir2/foo", "/hiddenByUp",
600 "/a", "/a/b", "/a/b/c", "/a/b/c/d", "/dir1", "/dir1/a",
601 "/dir1/a/b", "/dir1/foo", "/file1"});
604 TEST(VirtualFileSystemTest, ThreeLevelIteration) {
605 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
606 IntrusiveRefCntPtr<DummyFileSystem> Middle(new DummyFileSystem());
607 IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem());
608 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
609 new vfs::OverlayFileSystem(Lower));
610 O->pushOverlay(Middle);
611 O->pushOverlay(Upper);
613 std::error_code EC;
614 checkContents(O->dir_begin("/", EC), ArrayRef<StringRef>());
616 Middle->addRegularFile("/file2");
617 checkContents(O->dir_begin("/", EC), ArrayRef<StringRef>("/file2"));
619 Lower->addRegularFile("/file1");
620 Upper->addRegularFile("/file3");
621 checkContents(O->dir_begin("/", EC), {"/file3", "/file2", "/file1"});
624 TEST(VirtualFileSystemTest, HiddenInIteration) {
625 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
626 IntrusiveRefCntPtr<DummyFileSystem> Middle(new DummyFileSystem());
627 IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem());
628 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
629 new vfs::OverlayFileSystem(Lower));
630 O->pushOverlay(Middle);
631 O->pushOverlay(Upper);
633 std::error_code EC;
634 Lower->addRegularFile("/onlyInLow");
635 Lower->addDirectory("/hiddenByMid");
636 Lower->addDirectory("/hiddenByUp");
637 Middle->addRegularFile("/onlyInMid");
638 Middle->addRegularFile("/hiddenByMid");
639 Middle->addDirectory("/hiddenByUp");
640 Upper->addRegularFile("/onlyInUp");
641 Upper->addRegularFile("/hiddenByUp");
642 checkContents(
643 O->dir_begin("/", EC),
644 {"/hiddenByUp", "/onlyInUp", "/hiddenByMid", "/onlyInMid", "/onlyInLow"});
646 // Make sure we get the top-most entry
648 std::error_code EC;
649 vfs::directory_iterator I = O->dir_begin("/", EC), E;
650 for (; !EC && I != E; I.increment(EC))
651 if (I->path() == "/hiddenByUp")
652 break;
653 ASSERT_NE(E, I);
654 EXPECT_EQ(sys::fs::file_type::regular_file, I->type());
657 std::error_code EC;
658 vfs::directory_iterator I = O->dir_begin("/", EC), E;
659 for (; !EC && I != E; I.increment(EC))
660 if (I->path() == "/hiddenByMid")
661 break;
662 ASSERT_NE(E, I);
663 EXPECT_EQ(sys::fs::file_type::regular_file, I->type());
667 class InMemoryFileSystemTest : public ::testing::Test {
668 protected:
669 llvm::vfs::InMemoryFileSystem FS;
670 llvm::vfs::InMemoryFileSystem NormalizedFS;
672 InMemoryFileSystemTest()
673 : FS(/*UseNormalizedPaths=*/false),
674 NormalizedFS(/*UseNormalizedPaths=*/true) {}
677 MATCHER_P2(IsHardLinkTo, FS, Target, "") {
678 StringRef From = arg;
679 StringRef To = Target;
680 auto OpenedFrom = FS->openFileForRead(From);
681 auto OpenedTo = FS->openFileForRead(To);
682 return !OpenedFrom.getError() && !OpenedTo.getError() &&
683 (*OpenedFrom)->status()->getUniqueID() ==
684 (*OpenedTo)->status()->getUniqueID();
687 TEST_F(InMemoryFileSystemTest, IsEmpty) {
688 auto Stat = FS.status("/a");
689 ASSERT_EQ(Stat.getError(), errc::no_such_file_or_directory) << FS.toString();
690 Stat = FS.status("/");
691 ASSERT_EQ(Stat.getError(), errc::no_such_file_or_directory) << FS.toString();
694 TEST_F(InMemoryFileSystemTest, WindowsPath) {
695 FS.addFile("c:/windows/system128/foo.cpp", 0, MemoryBuffer::getMemBuffer(""));
696 auto Stat = FS.status("c:");
697 #if !defined(_WIN32)
698 ASSERT_FALSE(Stat.getError()) << Stat.getError() << FS.toString();
699 #endif
700 Stat = FS.status("c:/windows/system128/foo.cpp");
701 ASSERT_FALSE(Stat.getError()) << Stat.getError() << FS.toString();
702 FS.addFile("d:/windows/foo.cpp", 0, MemoryBuffer::getMemBuffer(""));
703 Stat = FS.status("d:/windows/foo.cpp");
704 ASSERT_FALSE(Stat.getError()) << Stat.getError() << FS.toString();
707 TEST_F(InMemoryFileSystemTest, OverlayFile) {
708 FS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a"));
709 NormalizedFS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a"));
710 auto Stat = FS.status("/");
711 ASSERT_FALSE(Stat.getError()) << Stat.getError() << FS.toString();
712 Stat = FS.status("/.");
713 ASSERT_FALSE(Stat);
714 Stat = NormalizedFS.status("/.");
715 ASSERT_FALSE(Stat.getError()) << Stat.getError() << FS.toString();
716 Stat = FS.status("/a");
717 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
718 ASSERT_EQ("/a", Stat->getName());
721 TEST_F(InMemoryFileSystemTest, OverlayFileNoOwn) {
722 auto Buf = MemoryBuffer::getMemBuffer("a");
723 FS.addFileNoOwn("/a", 0, Buf.get());
724 auto Stat = FS.status("/a");
725 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
726 ASSERT_EQ("/a", Stat->getName());
729 TEST_F(InMemoryFileSystemTest, OpenFileForRead) {
730 FS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a"));
731 FS.addFile("././c", 0, MemoryBuffer::getMemBuffer("c"));
732 FS.addFile("./d/../d", 0, MemoryBuffer::getMemBuffer("d"));
733 NormalizedFS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a"));
734 NormalizedFS.addFile("././c", 0, MemoryBuffer::getMemBuffer("c"));
735 NormalizedFS.addFile("./d/../d", 0, MemoryBuffer::getMemBuffer("d"));
736 auto File = FS.openFileForRead("/a");
737 ASSERT_EQ("a", (*(*File)->getBuffer("ignored"))->getBuffer());
738 File = FS.openFileForRead("/a"); // Open again.
739 ASSERT_EQ("a", (*(*File)->getBuffer("ignored"))->getBuffer());
740 File = NormalizedFS.openFileForRead("/././a"); // Open again.
741 ASSERT_EQ("a", (*(*File)->getBuffer("ignored"))->getBuffer());
742 File = FS.openFileForRead("/");
743 ASSERT_EQ(File.getError(), errc::invalid_argument) << FS.toString();
744 File = FS.openFileForRead("/b");
745 ASSERT_EQ(File.getError(), errc::no_such_file_or_directory) << FS.toString();
746 File = FS.openFileForRead("./c");
747 ASSERT_FALSE(File);
748 File = FS.openFileForRead("e/../d");
749 ASSERT_FALSE(File);
750 File = NormalizedFS.openFileForRead("./c");
751 ASSERT_EQ("c", (*(*File)->getBuffer("ignored"))->getBuffer());
752 File = NormalizedFS.openFileForRead("e/../d");
753 ASSERT_EQ("d", (*(*File)->getBuffer("ignored"))->getBuffer());
756 TEST_F(InMemoryFileSystemTest, DuplicatedFile) {
757 ASSERT_TRUE(FS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a")));
758 ASSERT_FALSE(FS.addFile("/a/b", 0, MemoryBuffer::getMemBuffer("a")));
759 ASSERT_TRUE(FS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a")));
760 ASSERT_FALSE(FS.addFile("/a", 0, MemoryBuffer::getMemBuffer("b")));
763 TEST_F(InMemoryFileSystemTest, DirectoryIteration) {
764 FS.addFile("/a", 0, MemoryBuffer::getMemBuffer(""));
765 FS.addFile("/b/c", 0, MemoryBuffer::getMemBuffer(""));
767 std::error_code EC;
768 vfs::directory_iterator I = FS.dir_begin("/", EC);
769 ASSERT_FALSE(EC);
770 ASSERT_EQ("/a", I->path());
771 I.increment(EC);
772 ASSERT_FALSE(EC);
773 ASSERT_EQ("/b", I->path());
774 I.increment(EC);
775 ASSERT_FALSE(EC);
776 ASSERT_EQ(vfs::directory_iterator(), I);
778 I = FS.dir_begin("/b", EC);
779 ASSERT_FALSE(EC);
780 // When on Windows, we end up with "/b\\c" as the name. Convert to Posix
781 // path for the sake of the comparison.
782 ASSERT_EQ("/b/c", getPosixPath(I->path()));
783 I.increment(EC);
784 ASSERT_FALSE(EC);
785 ASSERT_EQ(vfs::directory_iterator(), I);
788 TEST_F(InMemoryFileSystemTest, WorkingDirectory) {
789 FS.setCurrentWorkingDirectory("/b");
790 FS.addFile("c", 0, MemoryBuffer::getMemBuffer(""));
792 auto Stat = FS.status("/b/c");
793 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
794 ASSERT_EQ("/b/c", Stat->getName());
795 ASSERT_EQ("/b", *FS.getCurrentWorkingDirectory());
797 Stat = FS.status("c");
798 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
800 NormalizedFS.setCurrentWorkingDirectory("/b/c");
801 NormalizedFS.setCurrentWorkingDirectory(".");
802 ASSERT_EQ("/b/c",
803 getPosixPath(NormalizedFS.getCurrentWorkingDirectory().get()));
804 NormalizedFS.setCurrentWorkingDirectory("..");
805 ASSERT_EQ("/b",
806 getPosixPath(NormalizedFS.getCurrentWorkingDirectory().get()));
809 #if !defined(_WIN32)
810 TEST_F(InMemoryFileSystemTest, GetRealPath) {
811 SmallString<16> Path;
812 EXPECT_EQ(FS.getRealPath("b", Path), errc::operation_not_permitted);
814 auto GetRealPath = [this](StringRef P) {
815 SmallString<16> Output;
816 auto EC = FS.getRealPath(P, Output);
817 EXPECT_FALSE(EC);
818 return Output.str().str();
821 FS.setCurrentWorkingDirectory("a");
822 EXPECT_EQ(GetRealPath("b"), "a/b");
823 EXPECT_EQ(GetRealPath("../b"), "b");
824 EXPECT_EQ(GetRealPath("b/./c"), "a/b/c");
826 FS.setCurrentWorkingDirectory("/a");
827 EXPECT_EQ(GetRealPath("b"), "/a/b");
828 EXPECT_EQ(GetRealPath("../b"), "/b");
829 EXPECT_EQ(GetRealPath("b/./c"), "/a/b/c");
831 #endif // _WIN32
833 TEST_F(InMemoryFileSystemTest, AddFileWithUser) {
834 FS.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"), 0xFEEDFACE);
835 auto Stat = FS.status("/a");
836 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
837 ASSERT_TRUE(Stat->isDirectory());
838 ASSERT_EQ(0xFEEDFACE, Stat->getUser());
839 Stat = FS.status("/a/b");
840 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
841 ASSERT_TRUE(Stat->isDirectory());
842 ASSERT_EQ(0xFEEDFACE, Stat->getUser());
843 Stat = FS.status("/a/b/c");
844 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
845 ASSERT_TRUE(Stat->isRegularFile());
846 ASSERT_EQ(sys::fs::perms::all_all, Stat->getPermissions());
847 ASSERT_EQ(0xFEEDFACE, Stat->getUser());
850 TEST_F(InMemoryFileSystemTest, AddFileWithGroup) {
851 FS.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"), None, 0xDABBAD00);
852 auto Stat = FS.status("/a");
853 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
854 ASSERT_TRUE(Stat->isDirectory());
855 ASSERT_EQ(0xDABBAD00, Stat->getGroup());
856 Stat = FS.status("/a/b");
857 ASSERT_TRUE(Stat->isDirectory());
858 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
859 ASSERT_EQ(0xDABBAD00, Stat->getGroup());
860 Stat = FS.status("/a/b/c");
861 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
862 ASSERT_TRUE(Stat->isRegularFile());
863 ASSERT_EQ(sys::fs::perms::all_all, Stat->getPermissions());
864 ASSERT_EQ(0xDABBAD00, Stat->getGroup());
867 TEST_F(InMemoryFileSystemTest, AddFileWithFileType) {
868 FS.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"), None, None,
869 sys::fs::file_type::socket_file);
870 auto Stat = FS.status("/a");
871 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
872 ASSERT_TRUE(Stat->isDirectory());
873 Stat = FS.status("/a/b");
874 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
875 ASSERT_TRUE(Stat->isDirectory());
876 Stat = FS.status("/a/b/c");
877 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
878 ASSERT_EQ(sys::fs::file_type::socket_file, Stat->getType());
879 ASSERT_EQ(sys::fs::perms::all_all, Stat->getPermissions());
882 TEST_F(InMemoryFileSystemTest, AddFileWithPerms) {
883 FS.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"), None, None, None,
884 sys::fs::perms::owner_read | sys::fs::perms::owner_write);
885 auto Stat = FS.status("/a");
886 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
887 ASSERT_TRUE(Stat->isDirectory());
888 ASSERT_EQ(sys::fs::perms::owner_read | sys::fs::perms::owner_write |
889 sys::fs::perms::owner_exe,
890 Stat->getPermissions());
891 Stat = FS.status("/a/b");
892 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
893 ASSERT_TRUE(Stat->isDirectory());
894 ASSERT_EQ(sys::fs::perms::owner_read | sys::fs::perms::owner_write |
895 sys::fs::perms::owner_exe,
896 Stat->getPermissions());
897 Stat = FS.status("/a/b/c");
898 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
899 ASSERT_TRUE(Stat->isRegularFile());
900 ASSERT_EQ(sys::fs::perms::owner_read | sys::fs::perms::owner_write,
901 Stat->getPermissions());
904 TEST_F(InMemoryFileSystemTest, AddDirectoryThenAddChild) {
905 FS.addFile("/a", 0, MemoryBuffer::getMemBuffer(""), /*User=*/None,
906 /*Group=*/None, sys::fs::file_type::directory_file);
907 FS.addFile("/a/b", 0, MemoryBuffer::getMemBuffer("abc"), /*User=*/None,
908 /*Group=*/None, sys::fs::file_type::regular_file);
909 auto Stat = FS.status("/a");
910 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
911 ASSERT_TRUE(Stat->isDirectory());
912 Stat = FS.status("/a/b");
913 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
914 ASSERT_TRUE(Stat->isRegularFile());
917 // Test that the name returned by status() is in the same form as the path that
918 // was requested (to match the behavior of RealFileSystem).
919 TEST_F(InMemoryFileSystemTest, StatusName) {
920 NormalizedFS.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"),
921 /*User=*/None,
922 /*Group=*/None, sys::fs::file_type::regular_file);
923 NormalizedFS.setCurrentWorkingDirectory("/a/b");
925 // Access using InMemoryFileSystem::status.
926 auto Stat = NormalizedFS.status("../b/c");
927 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n"
928 << NormalizedFS.toString();
929 ASSERT_TRUE(Stat->isRegularFile());
930 ASSERT_EQ("../b/c", Stat->getName());
932 // Access using InMemoryFileAdaptor::status.
933 auto File = NormalizedFS.openFileForRead("../b/c");
934 ASSERT_FALSE(File.getError()) << File.getError() << "\n"
935 << NormalizedFS.toString();
936 Stat = (*File)->status();
937 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n"
938 << NormalizedFS.toString();
939 ASSERT_TRUE(Stat->isRegularFile());
940 ASSERT_EQ("../b/c", Stat->getName());
942 // Access using a directory iterator.
943 std::error_code EC;
944 llvm::vfs::directory_iterator It = NormalizedFS.dir_begin("../b", EC);
945 // When on Windows, we end up with "../b\\c" as the name. Convert to Posix
946 // path for the sake of the comparison.
947 ASSERT_EQ("../b/c", getPosixPath(It->path()));
950 TEST_F(InMemoryFileSystemTest, AddHardLinkToFile) {
951 StringRef FromLink = "/path/to/FROM/link";
952 StringRef Target = "/path/to/TO/file";
953 FS.addFile(Target, 0, MemoryBuffer::getMemBuffer("content of target"));
954 EXPECT_TRUE(FS.addHardLink(FromLink, Target));
955 EXPECT_THAT(FromLink, IsHardLinkTo(&FS, Target));
956 EXPECT_TRUE(FS.status(FromLink)->getSize() == FS.status(Target)->getSize());
957 EXPECT_TRUE(FS.getBufferForFile(FromLink)->get()->getBuffer() ==
958 FS.getBufferForFile(Target)->get()->getBuffer());
961 TEST_F(InMemoryFileSystemTest, AddHardLinkInChainPattern) {
962 StringRef Link0 = "/path/to/0/link";
963 StringRef Link1 = "/path/to/1/link";
964 StringRef Link2 = "/path/to/2/link";
965 StringRef Target = "/path/to/target";
966 FS.addFile(Target, 0, MemoryBuffer::getMemBuffer("content of target file"));
967 EXPECT_TRUE(FS.addHardLink(Link2, Target));
968 EXPECT_TRUE(FS.addHardLink(Link1, Link2));
969 EXPECT_TRUE(FS.addHardLink(Link0, Link1));
970 EXPECT_THAT(Link0, IsHardLinkTo(&FS, Target));
971 EXPECT_THAT(Link1, IsHardLinkTo(&FS, Target));
972 EXPECT_THAT(Link2, IsHardLinkTo(&FS, Target));
975 TEST_F(InMemoryFileSystemTest, AddHardLinkToAFileThatWasNotAddedBefore) {
976 EXPECT_FALSE(FS.addHardLink("/path/to/link", "/path/to/target"));
979 TEST_F(InMemoryFileSystemTest, AddHardLinkFromAFileThatWasAddedBefore) {
980 StringRef Link = "/path/to/link";
981 StringRef Target = "/path/to/target";
982 FS.addFile(Target, 0, MemoryBuffer::getMemBuffer("content of target"));
983 FS.addFile(Link, 0, MemoryBuffer::getMemBuffer("content of link"));
984 EXPECT_FALSE(FS.addHardLink(Link, Target));
987 TEST_F(InMemoryFileSystemTest, AddSameHardLinkMoreThanOnce) {
988 StringRef Link = "/path/to/link";
989 StringRef Target = "/path/to/target";
990 FS.addFile(Target, 0, MemoryBuffer::getMemBuffer("content of target"));
991 EXPECT_TRUE(FS.addHardLink(Link, Target));
992 EXPECT_FALSE(FS.addHardLink(Link, Target));
995 TEST_F(InMemoryFileSystemTest, AddFileInPlaceOfAHardLinkWithSameContent) {
996 StringRef Link = "/path/to/link";
997 StringRef Target = "/path/to/target";
998 StringRef Content = "content of target";
999 EXPECT_TRUE(FS.addFile(Target, 0, MemoryBuffer::getMemBuffer(Content)));
1000 EXPECT_TRUE(FS.addHardLink(Link, Target));
1001 EXPECT_TRUE(FS.addFile(Link, 0, MemoryBuffer::getMemBuffer(Content)));
1004 TEST_F(InMemoryFileSystemTest, AddFileInPlaceOfAHardLinkWithDifferentContent) {
1005 StringRef Link = "/path/to/link";
1006 StringRef Target = "/path/to/target";
1007 StringRef Content = "content of target";
1008 StringRef LinkContent = "different content of link";
1009 EXPECT_TRUE(FS.addFile(Target, 0, MemoryBuffer::getMemBuffer(Content)));
1010 EXPECT_TRUE(FS.addHardLink(Link, Target));
1011 EXPECT_FALSE(FS.addFile(Link, 0, MemoryBuffer::getMemBuffer(LinkContent)));
1014 TEST_F(InMemoryFileSystemTest, AddHardLinkToADirectory) {
1015 StringRef Dir = "path/to/dummy/dir";
1016 StringRef Link = "/path/to/link";
1017 StringRef File = "path/to/dummy/dir/target";
1018 StringRef Content = "content of target";
1019 EXPECT_TRUE(FS.addFile(File, 0, MemoryBuffer::getMemBuffer(Content)));
1020 EXPECT_FALSE(FS.addHardLink(Link, Dir));
1023 TEST_F(InMemoryFileSystemTest, AddHardLinkFromADirectory) {
1024 StringRef Dir = "path/to/dummy/dir";
1025 StringRef Target = "path/to/dummy/dir/target";
1026 StringRef Content = "content of target";
1027 EXPECT_TRUE(FS.addFile(Target, 0, MemoryBuffer::getMemBuffer(Content)));
1028 EXPECT_FALSE(FS.addHardLink(Dir, Target));
1031 TEST_F(InMemoryFileSystemTest, AddHardLinkUnderAFile) {
1032 StringRef CommonContent = "content string";
1033 FS.addFile("/a/b", 0, MemoryBuffer::getMemBuffer(CommonContent));
1034 FS.addFile("/c/d", 0, MemoryBuffer::getMemBuffer(CommonContent));
1035 EXPECT_FALSE(FS.addHardLink("/c/d/e", "/a/b"));
1038 TEST_F(InMemoryFileSystemTest, RecursiveIterationWithHardLink) {
1039 std::error_code EC;
1040 FS.addFile("/a/b", 0, MemoryBuffer::getMemBuffer("content string"));
1041 EXPECT_TRUE(FS.addHardLink("/c/d", "/a/b"));
1042 auto I = vfs::recursive_directory_iterator(FS, "/", EC);
1043 ASSERT_FALSE(EC);
1044 std::vector<std::string> Nodes;
1045 for (auto E = vfs::recursive_directory_iterator(); !EC && I != E;
1046 I.increment(EC)) {
1047 Nodes.push_back(getPosixPath(I->path()));
1049 EXPECT_THAT(Nodes, testing::UnorderedElementsAre("/a", "/a/b", "/c", "/c/d"));
1052 // NOTE: in the tests below, we use '//root/' as our root directory, since it is
1053 // a legal *absolute* path on Windows as well as *nix.
1054 class VFSFromYAMLTest : public ::testing::Test {
1055 public:
1056 int NumDiagnostics;
1058 void SetUp() override { NumDiagnostics = 0; }
1060 static void CountingDiagHandler(const SMDiagnostic &, void *Context) {
1061 VFSFromYAMLTest *Test = static_cast<VFSFromYAMLTest *>(Context);
1062 ++Test->NumDiagnostics;
1065 IntrusiveRefCntPtr<vfs::FileSystem>
1066 getFromYAMLRawString(StringRef Content,
1067 IntrusiveRefCntPtr<vfs::FileSystem> ExternalFS) {
1068 std::unique_ptr<MemoryBuffer> Buffer = MemoryBuffer::getMemBuffer(Content);
1069 return getVFSFromYAML(std::move(Buffer), CountingDiagHandler, "", this,
1070 ExternalFS);
1073 IntrusiveRefCntPtr<vfs::FileSystem> getFromYAMLString(
1074 StringRef Content,
1075 IntrusiveRefCntPtr<vfs::FileSystem> ExternalFS = new DummyFileSystem()) {
1076 std::string VersionPlusContent("{\n 'version':0,\n");
1077 VersionPlusContent += Content.slice(Content.find('{') + 1, StringRef::npos);
1078 return getFromYAMLRawString(VersionPlusContent, ExternalFS);
1081 // This is intended as a "XFAIL" for windows hosts.
1082 bool supportsSameDirMultipleYAMLEntries() {
1083 Triple Host(Triple::normalize(sys::getProcessTriple()));
1084 return !Host.isOSWindows();
1088 TEST_F(VFSFromYAMLTest, BasicVFSFromYAML) {
1089 IntrusiveRefCntPtr<vfs::FileSystem> FS;
1090 FS = getFromYAMLString("");
1091 EXPECT_EQ(nullptr, FS.get());
1092 FS = getFromYAMLString("[]");
1093 EXPECT_EQ(nullptr, FS.get());
1094 FS = getFromYAMLString("'string'");
1095 EXPECT_EQ(nullptr, FS.get());
1096 EXPECT_EQ(3, NumDiagnostics);
1099 TEST_F(VFSFromYAMLTest, MappedFiles) {
1100 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
1101 Lower->addRegularFile("//root/foo/bar/a");
1102 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
1103 "{ 'roots': [\n"
1104 "{\n"
1105 " 'type': 'directory',\n"
1106 " 'name': '//root/',\n"
1107 " 'contents': [ {\n"
1108 " 'type': 'file',\n"
1109 " 'name': 'file1',\n"
1110 " 'external-contents': '//root/foo/bar/a'\n"
1111 " },\n"
1112 " {\n"
1113 " 'type': 'file',\n"
1114 " 'name': 'file2',\n"
1115 " 'external-contents': '//root/foo/b'\n"
1116 " }\n"
1117 " ]\n"
1118 "}\n"
1119 "]\n"
1120 "}",
1121 Lower);
1122 ASSERT_TRUE(FS.get() != nullptr);
1124 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
1125 new vfs::OverlayFileSystem(Lower));
1126 O->pushOverlay(FS);
1128 // file
1129 ErrorOr<vfs::Status> S = O->status("//root/file1");
1130 ASSERT_FALSE(S.getError());
1131 EXPECT_EQ("//root/foo/bar/a", S->getName());
1132 EXPECT_TRUE(S->IsVFSMapped);
1134 ErrorOr<vfs::Status> SLower = O->status("//root/foo/bar/a");
1135 EXPECT_EQ("//root/foo/bar/a", SLower->getName());
1136 EXPECT_TRUE(S->equivalent(*SLower));
1137 EXPECT_FALSE(SLower->IsVFSMapped);
1139 // file after opening
1140 auto OpenedF = O->openFileForRead("//root/file1");
1141 ASSERT_FALSE(OpenedF.getError());
1142 auto OpenedS = (*OpenedF)->status();
1143 ASSERT_FALSE(OpenedS.getError());
1144 EXPECT_EQ("//root/foo/bar/a", OpenedS->getName());
1145 EXPECT_TRUE(OpenedS->IsVFSMapped);
1147 // directory
1148 S = O->status("//root/");
1149 ASSERT_FALSE(S.getError());
1150 EXPECT_TRUE(S->isDirectory());
1151 EXPECT_TRUE(S->equivalent(*O->status("//root/"))); // non-volatile UniqueID
1153 // broken mapping
1154 EXPECT_EQ(O->status("//root/file2").getError(),
1155 llvm::errc::no_such_file_or_directory);
1156 EXPECT_EQ(0, NumDiagnostics);
1159 TEST_F(VFSFromYAMLTest, CaseInsensitive) {
1160 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
1161 Lower->addRegularFile("//root/foo/bar/a");
1162 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
1163 "{ 'case-sensitive': 'false',\n"
1164 " 'roots': [\n"
1165 "{\n"
1166 " 'type': 'directory',\n"
1167 " 'name': '//root/',\n"
1168 " 'contents': [ {\n"
1169 " 'type': 'file',\n"
1170 " 'name': 'XX',\n"
1171 " 'external-contents': '//root/foo/bar/a'\n"
1172 " }\n"
1173 " ]\n"
1174 "}]}",
1175 Lower);
1176 ASSERT_TRUE(FS.get() != nullptr);
1178 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
1179 new vfs::OverlayFileSystem(Lower));
1180 O->pushOverlay(FS);
1182 ErrorOr<vfs::Status> S = O->status("//root/XX");
1183 ASSERT_FALSE(S.getError());
1185 ErrorOr<vfs::Status> SS = O->status("//root/xx");
1186 ASSERT_FALSE(SS.getError());
1187 EXPECT_TRUE(S->equivalent(*SS));
1188 SS = O->status("//root/xX");
1189 EXPECT_TRUE(S->equivalent(*SS));
1190 SS = O->status("//root/Xx");
1191 EXPECT_TRUE(S->equivalent(*SS));
1192 EXPECT_EQ(0, NumDiagnostics);
1195 TEST_F(VFSFromYAMLTest, CaseSensitive) {
1196 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
1197 Lower->addRegularFile("//root/foo/bar/a");
1198 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
1199 "{ 'case-sensitive': 'true',\n"
1200 " 'roots': [\n"
1201 "{\n"
1202 " 'type': 'directory',\n"
1203 " 'name': '//root/',\n"
1204 " 'contents': [ {\n"
1205 " 'type': 'file',\n"
1206 " 'name': 'XX',\n"
1207 " 'external-contents': '//root/foo/bar/a'\n"
1208 " }\n"
1209 " ]\n"
1210 "}]}",
1211 Lower);
1212 ASSERT_TRUE(FS.get() != nullptr);
1214 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
1215 new vfs::OverlayFileSystem(Lower));
1216 O->pushOverlay(FS);
1218 ErrorOr<vfs::Status> SS = O->status("//root/xx");
1219 EXPECT_EQ(SS.getError(), llvm::errc::no_such_file_or_directory);
1220 SS = O->status("//root/xX");
1221 EXPECT_EQ(SS.getError(), llvm::errc::no_such_file_or_directory);
1222 SS = O->status("//root/Xx");
1223 EXPECT_EQ(SS.getError(), llvm::errc::no_such_file_or_directory);
1224 EXPECT_EQ(0, NumDiagnostics);
1227 TEST_F(VFSFromYAMLTest, IllegalVFSFile) {
1228 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
1230 // invalid YAML at top-level
1231 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString("{]", Lower);
1232 EXPECT_EQ(nullptr, FS.get());
1233 // invalid YAML in roots
1234 FS = getFromYAMLString("{ 'roots':[}", Lower);
1235 // invalid YAML in directory
1236 FS = getFromYAMLString(
1237 "{ 'roots':[ { 'name': 'foo', 'type': 'directory', 'contents': [}",
1238 Lower);
1239 EXPECT_EQ(nullptr, FS.get());
1241 // invalid configuration
1242 FS = getFromYAMLString("{ 'knobular': 'true', 'roots':[] }", Lower);
1243 EXPECT_EQ(nullptr, FS.get());
1244 FS = getFromYAMLString("{ 'case-sensitive': 'maybe', 'roots':[] }", Lower);
1245 EXPECT_EQ(nullptr, FS.get());
1247 // invalid roots
1248 FS = getFromYAMLString("{ 'roots':'' }", Lower);
1249 EXPECT_EQ(nullptr, FS.get());
1250 FS = getFromYAMLString("{ 'roots':{} }", Lower);
1251 EXPECT_EQ(nullptr, FS.get());
1253 // invalid entries
1254 FS = getFromYAMLString(
1255 "{ 'roots':[ { 'type': 'other', 'name': 'me', 'contents': '' }", Lower);
1256 EXPECT_EQ(nullptr, FS.get());
1257 FS = getFromYAMLString("{ 'roots':[ { 'type': 'file', 'name': [], "
1258 "'external-contents': 'other' }",
1259 Lower);
1260 EXPECT_EQ(nullptr, FS.get());
1261 FS = getFromYAMLString(
1262 "{ 'roots':[ { 'type': 'file', 'name': 'me', 'external-contents': [] }",
1263 Lower);
1264 EXPECT_EQ(nullptr, FS.get());
1265 FS = getFromYAMLString(
1266 "{ 'roots':[ { 'type': 'file', 'name': 'me', 'external-contents': {} }",
1267 Lower);
1268 EXPECT_EQ(nullptr, FS.get());
1269 FS = getFromYAMLString(
1270 "{ 'roots':[ { 'type': 'directory', 'name': 'me', 'contents': {} }",
1271 Lower);
1272 EXPECT_EQ(nullptr, FS.get());
1273 FS = getFromYAMLString(
1274 "{ 'roots':[ { 'type': 'directory', 'name': 'me', 'contents': '' }",
1275 Lower);
1276 EXPECT_EQ(nullptr, FS.get());
1277 FS = getFromYAMLString(
1278 "{ 'roots':[ { 'thingy': 'directory', 'name': 'me', 'contents': [] }",
1279 Lower);
1280 EXPECT_EQ(nullptr, FS.get());
1282 // missing mandatory fields
1283 FS = getFromYAMLString("{ 'roots':[ { 'type': 'file', 'name': 'me' }", Lower);
1284 EXPECT_EQ(nullptr, FS.get());
1285 FS = getFromYAMLString(
1286 "{ 'roots':[ { 'type': 'file', 'external-contents': 'other' }", Lower);
1287 EXPECT_EQ(nullptr, FS.get());
1288 FS = getFromYAMLString("{ 'roots':[ { 'name': 'me', 'contents': [] }", Lower);
1289 EXPECT_EQ(nullptr, FS.get());
1291 // duplicate keys
1292 FS = getFromYAMLString("{ 'roots':[], 'roots':[] }", Lower);
1293 EXPECT_EQ(nullptr, FS.get());
1294 FS = getFromYAMLString(
1295 "{ 'case-sensitive':'true', 'case-sensitive':'true', 'roots':[] }",
1296 Lower);
1297 EXPECT_EQ(nullptr, FS.get());
1298 FS =
1299 getFromYAMLString("{ 'roots':[{'name':'me', 'name':'you', 'type':'file', "
1300 "'external-contents':'blah' } ] }",
1301 Lower);
1302 EXPECT_EQ(nullptr, FS.get());
1304 // missing version
1305 FS = getFromYAMLRawString("{ 'roots':[] }", Lower);
1306 EXPECT_EQ(nullptr, FS.get());
1308 // bad version number
1309 FS = getFromYAMLRawString("{ 'version':'foo', 'roots':[] }", Lower);
1310 EXPECT_EQ(nullptr, FS.get());
1311 FS = getFromYAMLRawString("{ 'version':-1, 'roots':[] }", Lower);
1312 EXPECT_EQ(nullptr, FS.get());
1313 FS = getFromYAMLRawString("{ 'version':100000, 'roots':[] }", Lower);
1314 EXPECT_EQ(nullptr, FS.get());
1315 EXPECT_EQ(24, NumDiagnostics);
1318 TEST_F(VFSFromYAMLTest, UseExternalName) {
1319 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
1320 Lower->addRegularFile("//root/external/file");
1322 IntrusiveRefCntPtr<vfs::FileSystem> FS =
1323 getFromYAMLString("{ 'roots': [\n"
1324 " { 'type': 'file', 'name': '//root/A',\n"
1325 " 'external-contents': '//root/external/file'\n"
1326 " },\n"
1327 " { 'type': 'file', 'name': '//root/B',\n"
1328 " 'use-external-name': true,\n"
1329 " 'external-contents': '//root/external/file'\n"
1330 " },\n"
1331 " { 'type': 'file', 'name': '//root/C',\n"
1332 " 'use-external-name': false,\n"
1333 " 'external-contents': '//root/external/file'\n"
1334 " }\n"
1335 "] }",
1336 Lower);
1337 ASSERT_TRUE(nullptr != FS.get());
1339 // default true
1340 EXPECT_EQ("//root/external/file", FS->status("//root/A")->getName());
1341 // explicit
1342 EXPECT_EQ("//root/external/file", FS->status("//root/B")->getName());
1343 EXPECT_EQ("//root/C", FS->status("//root/C")->getName());
1345 // global configuration
1346 FS = getFromYAMLString("{ 'use-external-names': false,\n"
1347 " 'roots': [\n"
1348 " { 'type': 'file', 'name': '//root/A',\n"
1349 " 'external-contents': '//root/external/file'\n"
1350 " },\n"
1351 " { 'type': 'file', 'name': '//root/B',\n"
1352 " 'use-external-name': true,\n"
1353 " 'external-contents': '//root/external/file'\n"
1354 " },\n"
1355 " { 'type': 'file', 'name': '//root/C',\n"
1356 " 'use-external-name': false,\n"
1357 " 'external-contents': '//root/external/file'\n"
1358 " }\n"
1359 "] }",
1360 Lower);
1361 ASSERT_TRUE(nullptr != FS.get());
1363 // default
1364 EXPECT_EQ("//root/A", FS->status("//root/A")->getName());
1365 // explicit
1366 EXPECT_EQ("//root/external/file", FS->status("//root/B")->getName());
1367 EXPECT_EQ("//root/C", FS->status("//root/C")->getName());
1370 TEST_F(VFSFromYAMLTest, MultiComponentPath) {
1371 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
1372 Lower->addRegularFile("//root/other");
1374 // file in roots
1375 IntrusiveRefCntPtr<vfs::FileSystem> FS =
1376 getFromYAMLString("{ 'roots': [\n"
1377 " { 'type': 'file', 'name': '//root/path/to/file',\n"
1378 " 'external-contents': '//root/other' }]\n"
1379 "}",
1380 Lower);
1381 ASSERT_TRUE(nullptr != FS.get());
1382 EXPECT_FALSE(FS->status("//root/path/to/file").getError());
1383 EXPECT_FALSE(FS->status("//root/path/to").getError());
1384 EXPECT_FALSE(FS->status("//root/path").getError());
1385 EXPECT_FALSE(FS->status("//root/").getError());
1387 // at the start
1388 FS = getFromYAMLString(
1389 "{ 'roots': [\n"
1390 " { 'type': 'directory', 'name': '//root/path/to',\n"
1391 " 'contents': [ { 'type': 'file', 'name': 'file',\n"
1392 " 'external-contents': '//root/other' }]}]\n"
1393 "}",
1394 Lower);
1395 ASSERT_TRUE(nullptr != FS.get());
1396 EXPECT_FALSE(FS->status("//root/path/to/file").getError());
1397 EXPECT_FALSE(FS->status("//root/path/to").getError());
1398 EXPECT_FALSE(FS->status("//root/path").getError());
1399 EXPECT_FALSE(FS->status("//root/").getError());
1401 // at the end
1402 FS = getFromYAMLString(
1403 "{ 'roots': [\n"
1404 " { 'type': 'directory', 'name': '//root/',\n"
1405 " 'contents': [ { 'type': 'file', 'name': 'path/to/file',\n"
1406 " 'external-contents': '//root/other' }]}]\n"
1407 "}",
1408 Lower);
1409 ASSERT_TRUE(nullptr != FS.get());
1410 EXPECT_FALSE(FS->status("//root/path/to/file").getError());
1411 EXPECT_FALSE(FS->status("//root/path/to").getError());
1412 EXPECT_FALSE(FS->status("//root/path").getError());
1413 EXPECT_FALSE(FS->status("//root/").getError());
1416 TEST_F(VFSFromYAMLTest, TrailingSlashes) {
1417 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
1418 Lower->addRegularFile("//root/other");
1420 // file in roots
1421 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
1422 "{ 'roots': [\n"
1423 " { 'type': 'directory', 'name': '//root/path/to////',\n"
1424 " 'contents': [ { 'type': 'file', 'name': 'file',\n"
1425 " 'external-contents': '//root/other' }]}]\n"
1426 "}",
1427 Lower);
1428 ASSERT_TRUE(nullptr != FS.get());
1429 EXPECT_FALSE(FS->status("//root/path/to/file").getError());
1430 EXPECT_FALSE(FS->status("//root/path/to").getError());
1431 EXPECT_FALSE(FS->status("//root/path").getError());
1432 EXPECT_FALSE(FS->status("//root/").getError());
1435 TEST_F(VFSFromYAMLTest, DirectoryIteration) {
1436 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
1437 Lower->addDirectory("//root/");
1438 Lower->addDirectory("//root/foo");
1439 Lower->addDirectory("//root/foo/bar");
1440 Lower->addRegularFile("//root/foo/bar/a");
1441 Lower->addRegularFile("//root/foo/bar/b");
1442 Lower->addRegularFile("//root/file3");
1443 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
1444 "{ 'use-external-names': false,\n"
1445 " 'roots': [\n"
1446 "{\n"
1447 " 'type': 'directory',\n"
1448 " 'name': '//root/',\n"
1449 " 'contents': [ {\n"
1450 " 'type': 'file',\n"
1451 " 'name': 'file1',\n"
1452 " 'external-contents': '//root/foo/bar/a'\n"
1453 " },\n"
1454 " {\n"
1455 " 'type': 'file',\n"
1456 " 'name': 'file2',\n"
1457 " 'external-contents': '//root/foo/bar/b'\n"
1458 " }\n"
1459 " ]\n"
1460 "}\n"
1461 "]\n"
1462 "}",
1463 Lower);
1464 ASSERT_TRUE(FS.get() != nullptr);
1466 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
1467 new vfs::OverlayFileSystem(Lower));
1468 O->pushOverlay(FS);
1470 std::error_code EC;
1471 checkContents(O->dir_begin("//root/", EC),
1472 {"//root/file1", "//root/file2", "//root/file3", "//root/foo"});
1474 checkContents(O->dir_begin("//root/foo/bar", EC),
1475 {"//root/foo/bar/a", "//root/foo/bar/b"});
1478 TEST_F(VFSFromYAMLTest, DirectoryIterationSameDirMultipleEntries) {
1479 // https://llvm.org/bugs/show_bug.cgi?id=27725
1480 if (!supportsSameDirMultipleYAMLEntries())
1481 return;
1483 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
1484 Lower->addDirectory("//root/zab");
1485 Lower->addDirectory("//root/baz");
1486 Lower->addRegularFile("//root/zab/a");
1487 Lower->addRegularFile("//root/zab/b");
1488 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
1489 "{ 'use-external-names': false,\n"
1490 " 'roots': [\n"
1491 "{\n"
1492 " 'type': 'directory',\n"
1493 " 'name': '//root/baz/',\n"
1494 " 'contents': [ {\n"
1495 " 'type': 'file',\n"
1496 " 'name': 'x',\n"
1497 " 'external-contents': '//root/zab/a'\n"
1498 " }\n"
1499 " ]\n"
1500 "},\n"
1501 "{\n"
1502 " 'type': 'directory',\n"
1503 " 'name': '//root/baz/',\n"
1504 " 'contents': [ {\n"
1505 " 'type': 'file',\n"
1506 " 'name': 'y',\n"
1507 " 'external-contents': '//root/zab/b'\n"
1508 " }\n"
1509 " ]\n"
1510 "}\n"
1511 "]\n"
1512 "}",
1513 Lower);
1514 ASSERT_TRUE(FS.get() != nullptr);
1516 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
1517 new vfs::OverlayFileSystem(Lower));
1518 O->pushOverlay(FS);
1520 std::error_code EC;
1522 checkContents(O->dir_begin("//root/baz/", EC),
1523 {"//root/baz/x", "//root/baz/y"});
1526 TEST_F(VFSFromYAMLTest, RecursiveDirectoryIterationLevel) {
1528 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
1529 Lower->addDirectory("//root/a");
1530 Lower->addDirectory("//root/a/b");
1531 Lower->addDirectory("//root/a/b/c");
1532 Lower->addRegularFile("//root/a/b/c/file");
1533 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
1534 "{ 'use-external-names': false,\n"
1535 " 'roots': [\n"
1536 "{\n"
1537 " 'type': 'directory',\n"
1538 " 'name': '//root/a/b/c/',\n"
1539 " 'contents': [ {\n"
1540 " 'type': 'file',\n"
1541 " 'name': 'file',\n"
1542 " 'external-contents': '//root/a/b/c/file'\n"
1543 " }\n"
1544 " ]\n"
1545 "},\n"
1546 "]\n"
1547 "}",
1548 Lower);
1549 ASSERT_TRUE(FS.get() != nullptr);
1551 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
1552 new vfs::OverlayFileSystem(Lower));
1553 O->pushOverlay(FS);
1555 std::error_code EC;
1557 // Test recursive_directory_iterator level()
1558 vfs::recursive_directory_iterator I = vfs::recursive_directory_iterator(
1559 *O, "//root", EC),
1561 ASSERT_FALSE(EC);
1562 for (int l = 0; I != E; I.increment(EC), ++l) {
1563 ASSERT_FALSE(EC);
1564 EXPECT_EQ(I.level(), l);
1566 EXPECT_EQ(I, E);
1569 TEST_F(VFSFromYAMLTest, RelativePaths) {
1570 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
1571 // Filename at root level without a parent directory.
1572 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
1573 "{ 'roots': [\n"
1574 " { 'type': 'file', 'name': 'file-not-in-directory.h',\n"
1575 " 'external-contents': '//root/external/file'\n"
1576 " }\n"
1577 "] }",
1578 Lower);
1579 EXPECT_EQ(nullptr, FS.get());
1581 // Relative file path.
1582 FS = getFromYAMLString("{ 'roots': [\n"
1583 " { 'type': 'file', 'name': 'relative/file/path.h',\n"
1584 " 'external-contents': '//root/external/file'\n"
1585 " }\n"
1586 "] }",
1587 Lower);
1588 EXPECT_EQ(nullptr, FS.get());
1590 // Relative directory path.
1591 FS = getFromYAMLString(
1592 "{ 'roots': [\n"
1593 " { 'type': 'directory', 'name': 'relative/directory/path.h',\n"
1594 " 'contents': []\n"
1595 " }\n"
1596 "] }",
1597 Lower);
1598 EXPECT_EQ(nullptr, FS.get());
1600 EXPECT_EQ(3, NumDiagnostics);