1 //===- unittests/Basic/VirtualFileSystem.cpp ---------------- VFS tests ---===//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
10 #include "clang/Basic/VirtualFileSystem.h"
11 #include "llvm/ADT/Triple.h"
12 #include "llvm/Support/Errc.h"
13 #include "llvm/Support/Host.h"
14 #include "llvm/Support/MemoryBuffer.h"
15 #include "llvm/Support/SourceMgr.h"
16 #include "gtest/gtest.h"
19 using namespace clang;
21 using llvm::sys::fs::UniqueID;
24 struct DummyFile : public vfs::File {
26 explicit DummyFile(vfs::Status S) : S(S) {}
27 llvm::ErrorOr<vfs::Status> status() override { return S; }
28 llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
29 getBuffer(const Twine &Name, int64_t FileSize, bool RequiresNullTerminator,
30 bool IsVolatile) override {
31 llvm_unreachable("unimplemented");
33 std::error_code close() override { return std::error_code(); }
36 class DummyFileSystem : public vfs::FileSystem {
37 int FSID; // used to produce UniqueIDs
38 int FileID; // used to produce UniqueIDs
39 std::map<std::string, vfs::Status> FilesAndDirs;
41 static int getNextFSID() {
47 DummyFileSystem() : FSID(getNextFSID()), FileID(0) {}
49 ErrorOr<vfs::Status> status(const Twine &Path) override {
50 std::map<std::string, vfs::Status>::iterator I =
51 FilesAndDirs.find(Path.str());
52 if (I == FilesAndDirs.end())
53 return make_error_code(llvm::errc::no_such_file_or_directory);
56 ErrorOr<std::unique_ptr<vfs::File>>
57 openFileForRead(const Twine &Path) override {
58 auto S = status(Path);
60 return std::unique_ptr<vfs::File>(new DummyFile{*S});
63 llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const override {
66 std::error_code setCurrentWorkingDirectory(const Twine &Path) override {
67 return std::error_code();
70 struct DirIterImpl : public clang::vfs::detail::DirIterImpl {
71 std::map<std::string, vfs::Status> &FilesAndDirs;
72 std::map<std::string, vfs::Status>::iterator I;
74 bool isInPath(StringRef S) {
75 if (Path.size() < S.size() && S.find(Path) == 0) {
76 auto LastSep = S.find_last_of('/');
77 if (LastSep == Path.size() || LastSep == Path.size()-1)
82 DirIterImpl(std::map<std::string, vfs::Status> &FilesAndDirs,
84 : FilesAndDirs(FilesAndDirs), I(FilesAndDirs.begin()),
86 for ( ; I != FilesAndDirs.end(); ++I) {
87 if (isInPath(I->first)) {
88 CurrentEntry = I->second;
93 std::error_code increment() override {
95 for ( ; I != FilesAndDirs.end(); ++I) {
96 if (isInPath(I->first)) {
97 CurrentEntry = I->second;
101 if (I == FilesAndDirs.end())
102 CurrentEntry = vfs::Status();
103 return std::error_code();
107 vfs::directory_iterator dir_begin(const Twine &Dir,
108 std::error_code &EC) override {
109 return vfs::directory_iterator(
110 std::make_shared<DirIterImpl>(FilesAndDirs, Dir));
113 void addEntry(StringRef Path, const vfs::Status &Status) {
114 FilesAndDirs[Path] = Status;
117 void addRegularFile(StringRef Path, sys::fs::perms Perms = sys::fs::all_all) {
118 vfs::Status S(Path, UniqueID(FSID, FileID++),
119 std::chrono::system_clock::now(), 0, 0, 1024,
120 sys::fs::file_type::regular_file, Perms);
124 void addDirectory(StringRef Path, sys::fs::perms Perms = sys::fs::all_all) {
125 vfs::Status S(Path, UniqueID(FSID, FileID++),
126 std::chrono::system_clock::now(), 0, 0, 0,
127 sys::fs::file_type::directory_file, Perms);
131 void addSymlink(StringRef Path) {
132 vfs::Status S(Path, UniqueID(FSID, FileID++),
133 std::chrono::system_clock::now(), 0, 0, 0,
134 sys::fs::file_type::symlink_file, sys::fs::all_all);
138 } // end anonymous namespace
140 TEST(VirtualFileSystemTest, StatusQueries) {
141 IntrusiveRefCntPtr<DummyFileSystem> D(new DummyFileSystem());
142 ErrorOr<vfs::Status> Status((std::error_code()));
144 D->addRegularFile("/foo");
145 Status = D->status("/foo");
146 ASSERT_FALSE(Status.getError());
147 EXPECT_TRUE(Status->isStatusKnown());
148 EXPECT_FALSE(Status->isDirectory());
149 EXPECT_TRUE(Status->isRegularFile());
150 EXPECT_FALSE(Status->isSymlink());
151 EXPECT_FALSE(Status->isOther());
152 EXPECT_TRUE(Status->exists());
154 D->addDirectory("/bar");
155 Status = D->status("/bar");
156 ASSERT_FALSE(Status.getError());
157 EXPECT_TRUE(Status->isStatusKnown());
158 EXPECT_TRUE(Status->isDirectory());
159 EXPECT_FALSE(Status->isRegularFile());
160 EXPECT_FALSE(Status->isSymlink());
161 EXPECT_FALSE(Status->isOther());
162 EXPECT_TRUE(Status->exists());
164 D->addSymlink("/baz");
165 Status = D->status("/baz");
166 ASSERT_FALSE(Status.getError());
167 EXPECT_TRUE(Status->isStatusKnown());
168 EXPECT_FALSE(Status->isDirectory());
169 EXPECT_FALSE(Status->isRegularFile());
170 EXPECT_TRUE(Status->isSymlink());
171 EXPECT_FALSE(Status->isOther());
172 EXPECT_TRUE(Status->exists());
174 EXPECT_TRUE(Status->equivalent(*Status));
175 ErrorOr<vfs::Status> Status2 = D->status("/foo");
176 ASSERT_FALSE(Status2.getError());
177 EXPECT_FALSE(Status->equivalent(*Status2));
180 TEST(VirtualFileSystemTest, BaseOnlyOverlay) {
181 IntrusiveRefCntPtr<DummyFileSystem> D(new DummyFileSystem());
182 ErrorOr<vfs::Status> Status((std::error_code()));
183 EXPECT_FALSE(Status = D->status("/foo"));
185 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(new vfs::OverlayFileSystem(D));
186 EXPECT_FALSE(Status = O->status("/foo"));
188 D->addRegularFile("/foo");
189 Status = D->status("/foo");
190 EXPECT_FALSE(Status.getError());
192 ErrorOr<vfs::Status> Status2((std::error_code()));
193 Status2 = O->status("/foo");
194 EXPECT_FALSE(Status2.getError());
195 EXPECT_TRUE(Status->equivalent(*Status2));
198 TEST(VirtualFileSystemTest, OverlayFiles) {
199 IntrusiveRefCntPtr<DummyFileSystem> Base(new DummyFileSystem());
200 IntrusiveRefCntPtr<DummyFileSystem> Middle(new DummyFileSystem());
201 IntrusiveRefCntPtr<DummyFileSystem> Top(new DummyFileSystem());
202 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
203 new vfs::OverlayFileSystem(Base));
204 O->pushOverlay(Middle);
207 ErrorOr<vfs::Status> Status1((std::error_code())),
208 Status2((std::error_code())), Status3((std::error_code())),
209 StatusB((std::error_code())), StatusM((std::error_code())),
210 StatusT((std::error_code()));
212 Base->addRegularFile("/foo");
213 StatusB = Base->status("/foo");
214 ASSERT_FALSE(StatusB.getError());
215 Status1 = O->status("/foo");
216 ASSERT_FALSE(Status1.getError());
217 Middle->addRegularFile("/foo");
218 StatusM = Middle->status("/foo");
219 ASSERT_FALSE(StatusM.getError());
220 Status2 = O->status("/foo");
221 ASSERT_FALSE(Status2.getError());
222 Top->addRegularFile("/foo");
223 StatusT = Top->status("/foo");
224 ASSERT_FALSE(StatusT.getError());
225 Status3 = O->status("/foo");
226 ASSERT_FALSE(Status3.getError());
228 EXPECT_TRUE(Status1->equivalent(*StatusB));
229 EXPECT_TRUE(Status2->equivalent(*StatusM));
230 EXPECT_TRUE(Status3->equivalent(*StatusT));
232 EXPECT_FALSE(Status1->equivalent(*Status2));
233 EXPECT_FALSE(Status2->equivalent(*Status3));
234 EXPECT_FALSE(Status1->equivalent(*Status3));
237 TEST(VirtualFileSystemTest, OverlayDirsNonMerged) {
238 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
239 IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem());
240 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
241 new vfs::OverlayFileSystem(Lower));
242 O->pushOverlay(Upper);
244 Lower->addDirectory("/lower-only");
245 Upper->addDirectory("/upper-only");
247 // non-merged paths should be the same
248 ErrorOr<vfs::Status> Status1 = Lower->status("/lower-only");
249 ASSERT_FALSE(Status1.getError());
250 ErrorOr<vfs::Status> Status2 = O->status("/lower-only");
251 ASSERT_FALSE(Status2.getError());
252 EXPECT_TRUE(Status1->equivalent(*Status2));
254 Status1 = Upper->status("/upper-only");
255 ASSERT_FALSE(Status1.getError());
256 Status2 = O->status("/upper-only");
257 ASSERT_FALSE(Status2.getError());
258 EXPECT_TRUE(Status1->equivalent(*Status2));
261 TEST(VirtualFileSystemTest, MergedDirPermissions) {
262 // merged directories get the permissions of the upper dir
263 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
264 IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem());
265 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
266 new vfs::OverlayFileSystem(Lower));
267 O->pushOverlay(Upper);
269 ErrorOr<vfs::Status> Status((std::error_code()));
270 Lower->addDirectory("/both", sys::fs::owner_read);
271 Upper->addDirectory("/both", sys::fs::owner_all | sys::fs::group_read);
272 Status = O->status("/both");
273 ASSERT_FALSE(Status.getError());
274 EXPECT_EQ(0740, Status->getPermissions());
276 // permissions (as usual) are not recursively applied
277 Lower->addRegularFile("/both/foo", sys::fs::owner_read);
278 Upper->addRegularFile("/both/bar", sys::fs::owner_write);
279 Status = O->status("/both/foo");
280 ASSERT_FALSE( Status.getError());
281 EXPECT_EQ(0400, Status->getPermissions());
282 Status = O->status("/both/bar");
283 ASSERT_FALSE(Status.getError());
284 EXPECT_EQ(0200, Status->getPermissions());
289 SmallString<128> Path;
290 ScopedDir(const Twine &Name, bool Unique=false) {
293 EC = llvm::sys::fs::createUniqueDirectory(Name, Path);
296 EC = llvm::sys::fs::create_directory(Twine(Path));
304 EXPECT_FALSE(llvm::sys::fs::remove(Path.str()));
306 operator StringRef() { return Path.str(); }
308 } // end anonymous namespace
310 TEST(VirtualFileSystemTest, BasicRealFSIteration) {
311 ScopedDir TestDirectory("virtual-file-system-test", /*Unique*/true);
312 IntrusiveRefCntPtr<vfs::FileSystem> FS = vfs::getRealFileSystem();
315 vfs::directory_iterator I = FS->dir_begin(Twine(TestDirectory), EC);
317 EXPECT_EQ(vfs::directory_iterator(), I); // empty directory is empty
319 ScopedDir _a(TestDirectory+"/a");
320 ScopedDir _ab(TestDirectory+"/a/b");
321 ScopedDir _c(TestDirectory+"/c");
322 ScopedDir _cd(TestDirectory+"/c/d");
324 I = FS->dir_begin(Twine(TestDirectory), EC);
326 ASSERT_NE(vfs::directory_iterator(), I);
327 // Check either a or c, since we can't rely on the iteration order.
328 EXPECT_TRUE(I->getName().endswith("a") || I->getName().endswith("c"));
331 ASSERT_NE(vfs::directory_iterator(), I);
332 EXPECT_TRUE(I->getName().endswith("a") || I->getName().endswith("c"));
334 EXPECT_EQ(vfs::directory_iterator(), I);
337 TEST(VirtualFileSystemTest, BasicRealFSRecursiveIteration) {
338 ScopedDir TestDirectory("virtual-file-system-test", /*Unique*/true);
339 IntrusiveRefCntPtr<vfs::FileSystem> FS = vfs::getRealFileSystem();
342 auto I = vfs::recursive_directory_iterator(*FS, Twine(TestDirectory), EC);
344 EXPECT_EQ(vfs::recursive_directory_iterator(), I); // empty directory is empty
346 ScopedDir _a(TestDirectory+"/a");
347 ScopedDir _ab(TestDirectory+"/a/b");
348 ScopedDir _c(TestDirectory+"/c");
349 ScopedDir _cd(TestDirectory+"/c/d");
351 I = vfs::recursive_directory_iterator(*FS, Twine(TestDirectory), EC);
353 ASSERT_NE(vfs::recursive_directory_iterator(), I);
355 std::vector<std::string> Contents;
356 for (auto E = vfs::recursive_directory_iterator(); !EC && I != E;
358 Contents.push_back(I->getName());
361 // Check contents, which may be in any order
362 EXPECT_EQ(4U, Contents.size());
363 int Counts[4] = { 0, 0, 0, 0 };
364 for (const std::string &Name : Contents) {
365 ASSERT_FALSE(Name.empty());
366 int Index = Name[Name.size()-1] - 'a';
367 ASSERT_TRUE(Index >= 0 && Index < 4);
370 EXPECT_EQ(1, Counts[0]); // a
371 EXPECT_EQ(1, Counts[1]); // b
372 EXPECT_EQ(1, Counts[2]); // c
373 EXPECT_EQ(1, Counts[3]); // d
376 template <typename DirIter>
377 static void checkContents(DirIter I, ArrayRef<StringRef> ExpectedOut) {
379 SmallVector<StringRef, 4> Expected(ExpectedOut.begin(), ExpectedOut.end());
380 SmallVector<std::string, 4> InputToCheck;
382 // Do not rely on iteration order to check for contents, sort both
383 // content vectors before comparison.
384 for (DirIter E; !EC && I != E; I.increment(EC))
385 InputToCheck.push_back(I->getName());
387 std::sort(InputToCheck.begin(), InputToCheck.end());
388 std::sort(Expected.begin(), Expected.end());
389 EXPECT_EQ(InputToCheck.size(), Expected.size());
391 unsigned LastElt = std::min(InputToCheck.size(), Expected.size());
392 for (unsigned Idx = 0; Idx != LastElt; ++Idx)
393 EXPECT_EQ(StringRef(InputToCheck[Idx]), Expected[Idx]);
396 TEST(VirtualFileSystemTest, OverlayIteration) {
397 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
398 IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem());
399 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
400 new vfs::OverlayFileSystem(Lower));
401 O->pushOverlay(Upper);
404 checkContents(O->dir_begin("/", EC), ArrayRef<StringRef>());
406 Lower->addRegularFile("/file1");
407 checkContents(O->dir_begin("/", EC), ArrayRef<StringRef>("/file1"));
409 Upper->addRegularFile("/file2");
410 checkContents(O->dir_begin("/", EC), {"/file2", "/file1"});
412 Lower->addDirectory("/dir1");
413 Lower->addRegularFile("/dir1/foo");
414 Upper->addDirectory("/dir2");
415 Upper->addRegularFile("/dir2/foo");
416 checkContents(O->dir_begin("/dir2", EC), ArrayRef<StringRef>("/dir2/foo"));
417 checkContents(O->dir_begin("/", EC), {"/dir2", "/file2", "/dir1", "/file1"});
420 TEST(VirtualFileSystemTest, OverlayRecursiveIteration) {
421 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
422 IntrusiveRefCntPtr<DummyFileSystem> Middle(new DummyFileSystem());
423 IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem());
424 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
425 new vfs::OverlayFileSystem(Lower));
426 O->pushOverlay(Middle);
427 O->pushOverlay(Upper);
430 checkContents(vfs::recursive_directory_iterator(*O, "/", EC),
431 ArrayRef<StringRef>());
433 Lower->addRegularFile("/file1");
434 checkContents(vfs::recursive_directory_iterator(*O, "/", EC),
435 ArrayRef<StringRef>("/file1"));
437 Upper->addDirectory("/dir");
438 Upper->addRegularFile("/dir/file2");
439 checkContents(vfs::recursive_directory_iterator(*O, "/", EC),
440 {"/dir", "/dir/file2", "/file1"});
442 Lower->addDirectory("/dir1");
443 Lower->addRegularFile("/dir1/foo");
444 Lower->addDirectory("/dir1/a");
445 Lower->addRegularFile("/dir1/a/b");
446 Middle->addDirectory("/a");
447 Middle->addDirectory("/a/b");
448 Middle->addDirectory("/a/b/c");
449 Middle->addRegularFile("/a/b/c/d");
450 Middle->addRegularFile("/hiddenByUp");
451 Upper->addDirectory("/dir2");
452 Upper->addRegularFile("/dir2/foo");
453 Upper->addRegularFile("/hiddenByUp");
454 checkContents(vfs::recursive_directory_iterator(*O, "/dir2", EC),
455 ArrayRef<StringRef>("/dir2/foo"));
456 checkContents(vfs::recursive_directory_iterator(*O, "/", EC),
457 {"/dir", "/dir/file2", "/dir2", "/dir2/foo", "/hiddenByUp",
458 "/a", "/a/b", "/a/b/c", "/a/b/c/d", "/dir1", "/dir1/a",
459 "/dir1/a/b", "/dir1/foo", "/file1"});
462 TEST(VirtualFileSystemTest, ThreeLevelIteration) {
463 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
464 IntrusiveRefCntPtr<DummyFileSystem> Middle(new DummyFileSystem());
465 IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem());
466 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
467 new vfs::OverlayFileSystem(Lower));
468 O->pushOverlay(Middle);
469 O->pushOverlay(Upper);
472 checkContents(O->dir_begin("/", EC), ArrayRef<StringRef>());
474 Middle->addRegularFile("/file2");
475 checkContents(O->dir_begin("/", EC), ArrayRef<StringRef>("/file2"));
477 Lower->addRegularFile("/file1");
478 Upper->addRegularFile("/file3");
479 checkContents(O->dir_begin("/", EC), {"/file3", "/file2", "/file1"});
482 TEST(VirtualFileSystemTest, HiddenInIteration) {
483 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
484 IntrusiveRefCntPtr<DummyFileSystem> Middle(new DummyFileSystem());
485 IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem());
486 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
487 new vfs::OverlayFileSystem(Lower));
488 O->pushOverlay(Middle);
489 O->pushOverlay(Upper);
492 Lower->addRegularFile("/onlyInLow", sys::fs::owner_read);
493 Lower->addRegularFile("/hiddenByMid", sys::fs::owner_read);
494 Lower->addRegularFile("/hiddenByUp", sys::fs::owner_read);
495 Middle->addRegularFile("/onlyInMid", sys::fs::owner_write);
496 Middle->addRegularFile("/hiddenByMid", sys::fs::owner_write);
497 Middle->addRegularFile("/hiddenByUp", sys::fs::owner_write);
498 Upper->addRegularFile("/onlyInUp", sys::fs::owner_all);
499 Upper->addRegularFile("/hiddenByUp", sys::fs::owner_all);
501 O->dir_begin("/", EC),
502 {"/hiddenByUp", "/onlyInUp", "/hiddenByMid", "/onlyInMid", "/onlyInLow"});
504 // Make sure we get the top-most entry
507 vfs::directory_iterator I = O->dir_begin("/", EC), E;
508 for ( ; !EC && I != E; I.increment(EC))
509 if (I->getName() == "/hiddenByUp")
512 EXPECT_EQ(sys::fs::owner_all, I->getPermissions());
516 vfs::directory_iterator I = O->dir_begin("/", EC), E;
517 for ( ; !EC && I != E; I.increment(EC))
518 if (I->getName() == "/hiddenByMid")
521 EXPECT_EQ(sys::fs::owner_write, I->getPermissions());
525 class InMemoryFileSystemTest : public ::testing::Test {
527 clang::vfs::InMemoryFileSystem FS;
528 clang::vfs::InMemoryFileSystem NormalizedFS;
530 InMemoryFileSystemTest()
531 : FS(/*UseNormalizedPaths=*/false),
532 NormalizedFS(/*UseNormalizedPaths=*/true) {}
535 TEST_F(InMemoryFileSystemTest, IsEmpty) {
536 auto Stat = FS.status("/a");
537 ASSERT_EQ(Stat.getError(),errc::no_such_file_or_directory) << FS.toString();
538 Stat = FS.status("/");
539 ASSERT_EQ(Stat.getError(), errc::no_such_file_or_directory) << FS.toString();
542 TEST_F(InMemoryFileSystemTest, WindowsPath) {
543 FS.addFile("c:/windows/system128/foo.cpp", 0, MemoryBuffer::getMemBuffer(""));
544 auto Stat = FS.status("c:");
546 ASSERT_FALSE(Stat.getError()) << Stat.getError() << FS.toString();
548 Stat = FS.status("c:/windows/system128/foo.cpp");
549 ASSERT_FALSE(Stat.getError()) << Stat.getError() << FS.toString();
550 FS.addFile("d:/windows/foo.cpp", 0, MemoryBuffer::getMemBuffer(""));
551 Stat = FS.status("d:/windows/foo.cpp");
552 ASSERT_FALSE(Stat.getError()) << Stat.getError() << FS.toString();
555 TEST_F(InMemoryFileSystemTest, OverlayFile) {
556 FS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a"));
557 NormalizedFS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a"));
558 auto Stat = FS.status("/");
559 ASSERT_FALSE(Stat.getError()) << Stat.getError() << FS.toString();
560 Stat = FS.status("/.");
562 Stat = NormalizedFS.status("/.");
563 ASSERT_FALSE(Stat.getError()) << Stat.getError() << FS.toString();
564 Stat = FS.status("/a");
565 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
566 ASSERT_EQ("/a", Stat->getName());
569 TEST_F(InMemoryFileSystemTest, OverlayFileNoOwn) {
570 auto Buf = MemoryBuffer::getMemBuffer("a");
571 FS.addFileNoOwn("/a", 0, Buf.get());
572 auto Stat = FS.status("/a");
573 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
574 ASSERT_EQ("/a", Stat->getName());
577 TEST_F(InMemoryFileSystemTest, OpenFileForRead) {
578 FS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a"));
579 FS.addFile("././c", 0, MemoryBuffer::getMemBuffer("c"));
580 FS.addFile("./d/../d", 0, MemoryBuffer::getMemBuffer("d"));
581 NormalizedFS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a"));
582 NormalizedFS.addFile("././c", 0, MemoryBuffer::getMemBuffer("c"));
583 NormalizedFS.addFile("./d/../d", 0, MemoryBuffer::getMemBuffer("d"));
584 auto File = FS.openFileForRead("/a");
585 ASSERT_EQ("a", (*(*File)->getBuffer("ignored"))->getBuffer());
586 File = FS.openFileForRead("/a"); // Open again.
587 ASSERT_EQ("a", (*(*File)->getBuffer("ignored"))->getBuffer());
588 File = NormalizedFS.openFileForRead("/././a"); // Open again.
589 ASSERT_EQ("a", (*(*File)->getBuffer("ignored"))->getBuffer());
590 File = FS.openFileForRead("/");
591 ASSERT_EQ(File.getError(), errc::invalid_argument) << FS.toString();
592 File = FS.openFileForRead("/b");
593 ASSERT_EQ(File.getError(), errc::no_such_file_or_directory) << FS.toString();
594 File = FS.openFileForRead("./c");
596 File = FS.openFileForRead("e/../d");
598 File = NormalizedFS.openFileForRead("./c");
599 ASSERT_EQ("c", (*(*File)->getBuffer("ignored"))->getBuffer());
600 File = NormalizedFS.openFileForRead("e/../d");
601 ASSERT_EQ("d", (*(*File)->getBuffer("ignored"))->getBuffer());
604 TEST_F(InMemoryFileSystemTest, DuplicatedFile) {
605 ASSERT_TRUE(FS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a")));
606 ASSERT_FALSE(FS.addFile("/a/b", 0, MemoryBuffer::getMemBuffer("a")));
607 ASSERT_TRUE(FS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a")));
608 ASSERT_FALSE(FS.addFile("/a", 0, MemoryBuffer::getMemBuffer("b")));
611 TEST_F(InMemoryFileSystemTest, DirectoryIteration) {
612 FS.addFile("/a", 0, MemoryBuffer::getMemBuffer(""));
613 FS.addFile("/b/c", 0, MemoryBuffer::getMemBuffer(""));
616 vfs::directory_iterator I = FS.dir_begin("/", EC);
618 ASSERT_EQ("/a", I->getName());
621 ASSERT_EQ("/b", I->getName());
624 ASSERT_EQ(vfs::directory_iterator(), I);
626 I = FS.dir_begin("/b", EC);
628 ASSERT_EQ("/b/c", I->getName());
631 ASSERT_EQ(vfs::directory_iterator(), I);
634 TEST_F(InMemoryFileSystemTest, WorkingDirectory) {
635 FS.setCurrentWorkingDirectory("/b");
636 FS.addFile("c", 0, MemoryBuffer::getMemBuffer(""));
638 auto Stat = FS.status("/b/c");
639 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
640 ASSERT_EQ("c", Stat->getName());
641 ASSERT_EQ("/b", *FS.getCurrentWorkingDirectory());
643 Stat = FS.status("c");
644 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
646 auto ReplaceBackslashes = [](std::string S) {
647 std::replace(S.begin(), S.end(), '\\', '/');
650 NormalizedFS.setCurrentWorkingDirectory("/b/c");
651 NormalizedFS.setCurrentWorkingDirectory(".");
652 ASSERT_EQ("/b/c", ReplaceBackslashes(
653 NormalizedFS.getCurrentWorkingDirectory().get()));
654 NormalizedFS.setCurrentWorkingDirectory("..");
655 ASSERT_EQ("/b", ReplaceBackslashes(
656 NormalizedFS.getCurrentWorkingDirectory().get()));
659 // NOTE: in the tests below, we use '//root/' as our root directory, since it is
660 // a legal *absolute* path on Windows as well as *nix.
661 class VFSFromYAMLTest : public ::testing::Test {
665 void SetUp() override { NumDiagnostics = 0; }
667 static void CountingDiagHandler(const SMDiagnostic &, void *Context) {
668 VFSFromYAMLTest *Test = static_cast<VFSFromYAMLTest *>(Context);
669 ++Test->NumDiagnostics;
672 IntrusiveRefCntPtr<vfs::FileSystem>
673 getFromYAMLRawString(StringRef Content,
674 IntrusiveRefCntPtr<vfs::FileSystem> ExternalFS) {
675 std::unique_ptr<MemoryBuffer> Buffer = MemoryBuffer::getMemBuffer(Content);
676 return getVFSFromYAML(std::move(Buffer), CountingDiagHandler, "", this,
680 IntrusiveRefCntPtr<vfs::FileSystem> getFromYAMLString(
682 IntrusiveRefCntPtr<vfs::FileSystem> ExternalFS = new DummyFileSystem()) {
683 std::string VersionPlusContent("{\n 'version':0,\n");
684 VersionPlusContent += Content.slice(Content.find('{') + 1, StringRef::npos);
685 return getFromYAMLRawString(VersionPlusContent, ExternalFS);
688 // This is intended as a "XFAIL" for windows hosts.
689 bool supportsSameDirMultipleYAMLEntries() {
690 Triple Host(Triple::normalize(sys::getProcessTriple()));
691 return !Host.isOSWindows();
695 TEST_F(VFSFromYAMLTest, BasicVFSFromYAML) {
696 IntrusiveRefCntPtr<vfs::FileSystem> FS;
697 FS = getFromYAMLString("");
698 EXPECT_EQ(nullptr, FS.get());
699 FS = getFromYAMLString("[]");
700 EXPECT_EQ(nullptr, FS.get());
701 FS = getFromYAMLString("'string'");
702 EXPECT_EQ(nullptr, FS.get());
703 EXPECT_EQ(3, NumDiagnostics);
706 TEST_F(VFSFromYAMLTest, MappedFiles) {
707 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
708 Lower->addRegularFile("//root/foo/bar/a");
709 IntrusiveRefCntPtr<vfs::FileSystem> FS =
710 getFromYAMLString("{ 'roots': [\n"
712 " 'type': 'directory',\n"
713 " 'name': '//root/',\n"
716 " 'name': 'file1',\n"
717 " 'external-contents': '//root/foo/bar/a'\n"
721 " 'name': 'file2',\n"
722 " 'external-contents': '//root/foo/b'\n"
729 ASSERT_TRUE(FS.get() != nullptr);
731 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
732 new vfs::OverlayFileSystem(Lower));
736 ErrorOr<vfs::Status> S = O->status("//root/file1");
737 ASSERT_FALSE(S.getError());
738 EXPECT_EQ("//root/foo/bar/a", S->getName());
739 EXPECT_TRUE(S->IsVFSMapped);
741 ErrorOr<vfs::Status> SLower = O->status("//root/foo/bar/a");
742 EXPECT_EQ("//root/foo/bar/a", SLower->getName());
743 EXPECT_TRUE(S->equivalent(*SLower));
744 EXPECT_FALSE(SLower->IsVFSMapped);
746 // file after opening
747 auto OpenedF = O->openFileForRead("//root/file1");
748 ASSERT_FALSE(OpenedF.getError());
749 auto OpenedS = (*OpenedF)->status();
750 ASSERT_FALSE(OpenedS.getError());
751 EXPECT_EQ("//root/foo/bar/a", OpenedS->getName());
752 EXPECT_TRUE(OpenedS->IsVFSMapped);
755 S = O->status("//root/");
756 ASSERT_FALSE(S.getError());
757 EXPECT_TRUE(S->isDirectory());
758 EXPECT_TRUE(S->equivalent(*O->status("//root/"))); // non-volatile UniqueID
761 EXPECT_EQ(O->status("//root/file2").getError(),
762 llvm::errc::no_such_file_or_directory);
763 EXPECT_EQ(0, NumDiagnostics);
766 TEST_F(VFSFromYAMLTest, CaseInsensitive) {
767 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
768 Lower->addRegularFile("//root/foo/bar/a");
769 IntrusiveRefCntPtr<vfs::FileSystem> FS =
770 getFromYAMLString("{ 'case-sensitive': 'false',\n"
773 " 'type': 'directory',\n"
774 " 'name': '//root/',\n"
778 " 'external-contents': '//root/foo/bar/a'\n"
783 ASSERT_TRUE(FS.get() != nullptr);
785 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
786 new vfs::OverlayFileSystem(Lower));
789 ErrorOr<vfs::Status> S = O->status("//root/XX");
790 ASSERT_FALSE(S.getError());
792 ErrorOr<vfs::Status> SS = O->status("//root/xx");
793 ASSERT_FALSE(SS.getError());
794 EXPECT_TRUE(S->equivalent(*SS));
795 SS = O->status("//root/xX");
796 EXPECT_TRUE(S->equivalent(*SS));
797 SS = O->status("//root/Xx");
798 EXPECT_TRUE(S->equivalent(*SS));
799 EXPECT_EQ(0, NumDiagnostics);
802 TEST_F(VFSFromYAMLTest, CaseSensitive) {
803 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
804 Lower->addRegularFile("//root/foo/bar/a");
805 IntrusiveRefCntPtr<vfs::FileSystem> FS =
806 getFromYAMLString("{ 'case-sensitive': 'true',\n"
809 " 'type': 'directory',\n"
810 " 'name': '//root/',\n"
814 " 'external-contents': '//root/foo/bar/a'\n"
819 ASSERT_TRUE(FS.get() != nullptr);
821 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
822 new vfs::OverlayFileSystem(Lower));
825 ErrorOr<vfs::Status> SS = O->status("//root/xx");
826 EXPECT_EQ(SS.getError(), llvm::errc::no_such_file_or_directory);
827 SS = O->status("//root/xX");
828 EXPECT_EQ(SS.getError(), llvm::errc::no_such_file_or_directory);
829 SS = O->status("//root/Xx");
830 EXPECT_EQ(SS.getError(), llvm::errc::no_such_file_or_directory);
831 EXPECT_EQ(0, NumDiagnostics);
834 TEST_F(VFSFromYAMLTest, IllegalVFSFile) {
835 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
837 // invalid YAML at top-level
838 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString("{]", Lower);
839 EXPECT_EQ(nullptr, FS.get());
840 // invalid YAML in roots
841 FS = getFromYAMLString("{ 'roots':[}", Lower);
842 // invalid YAML in directory
843 FS = getFromYAMLString(
844 "{ 'roots':[ { 'name': 'foo', 'type': 'directory', 'contents': [}",
846 EXPECT_EQ(nullptr, FS.get());
848 // invalid configuration
849 FS = getFromYAMLString("{ 'knobular': 'true', 'roots':[] }", Lower);
850 EXPECT_EQ(nullptr, FS.get());
851 FS = getFromYAMLString("{ 'case-sensitive': 'maybe', 'roots':[] }", Lower);
852 EXPECT_EQ(nullptr, FS.get());
855 FS = getFromYAMLString("{ 'roots':'' }", Lower);
856 EXPECT_EQ(nullptr, FS.get());
857 FS = getFromYAMLString("{ 'roots':{} }", Lower);
858 EXPECT_EQ(nullptr, FS.get());
861 FS = getFromYAMLString(
862 "{ 'roots':[ { 'type': 'other', 'name': 'me', 'contents': '' }", Lower);
863 EXPECT_EQ(nullptr, FS.get());
864 FS = getFromYAMLString("{ 'roots':[ { 'type': 'file', 'name': [], "
865 "'external-contents': 'other' }",
867 EXPECT_EQ(nullptr, FS.get());
868 FS = getFromYAMLString(
869 "{ 'roots':[ { 'type': 'file', 'name': 'me', 'external-contents': [] }",
871 EXPECT_EQ(nullptr, FS.get());
872 FS = getFromYAMLString(
873 "{ 'roots':[ { 'type': 'file', 'name': 'me', 'external-contents': {} }",
875 EXPECT_EQ(nullptr, FS.get());
876 FS = getFromYAMLString(
877 "{ 'roots':[ { 'type': 'directory', 'name': 'me', 'contents': {} }",
879 EXPECT_EQ(nullptr, FS.get());
880 FS = getFromYAMLString(
881 "{ 'roots':[ { 'type': 'directory', 'name': 'me', 'contents': '' }",
883 EXPECT_EQ(nullptr, FS.get());
884 FS = getFromYAMLString(
885 "{ 'roots':[ { 'thingy': 'directory', 'name': 'me', 'contents': [] }",
887 EXPECT_EQ(nullptr, FS.get());
889 // missing mandatory fields
890 FS = getFromYAMLString("{ 'roots':[ { 'type': 'file', 'name': 'me' }", Lower);
891 EXPECT_EQ(nullptr, FS.get());
892 FS = getFromYAMLString(
893 "{ 'roots':[ { 'type': 'file', 'external-contents': 'other' }", Lower);
894 EXPECT_EQ(nullptr, FS.get());
895 FS = getFromYAMLString("{ 'roots':[ { 'name': 'me', 'contents': [] }", Lower);
896 EXPECT_EQ(nullptr, FS.get());
899 FS = getFromYAMLString("{ 'roots':[], 'roots':[] }", Lower);
900 EXPECT_EQ(nullptr, FS.get());
901 FS = getFromYAMLString(
902 "{ 'case-sensitive':'true', 'case-sensitive':'true', 'roots':[] }",
904 EXPECT_EQ(nullptr, FS.get());
906 getFromYAMLString("{ 'roots':[{'name':'me', 'name':'you', 'type':'file', "
907 "'external-contents':'blah' } ] }",
909 EXPECT_EQ(nullptr, FS.get());
912 FS = getFromYAMLRawString("{ 'roots':[] }", Lower);
913 EXPECT_EQ(nullptr, FS.get());
915 // bad version number
916 FS = getFromYAMLRawString("{ 'version':'foo', 'roots':[] }", Lower);
917 EXPECT_EQ(nullptr, FS.get());
918 FS = getFromYAMLRawString("{ 'version':-1, 'roots':[] }", Lower);
919 EXPECT_EQ(nullptr, FS.get());
920 FS = getFromYAMLRawString("{ 'version':100000, 'roots':[] }", Lower);
921 EXPECT_EQ(nullptr, FS.get());
922 EXPECT_EQ(24, NumDiagnostics);
925 TEST_F(VFSFromYAMLTest, UseExternalName) {
926 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
927 Lower->addRegularFile("//root/external/file");
929 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
931 " { 'type': 'file', 'name': '//root/A',\n"
932 " 'external-contents': '//root/external/file'\n"
934 " { 'type': 'file', 'name': '//root/B',\n"
935 " 'use-external-name': true,\n"
936 " 'external-contents': '//root/external/file'\n"
938 " { 'type': 'file', 'name': '//root/C',\n"
939 " 'use-external-name': false,\n"
940 " 'external-contents': '//root/external/file'\n"
943 ASSERT_TRUE(nullptr != FS.get());
946 EXPECT_EQ("//root/external/file", FS->status("//root/A")->getName());
948 EXPECT_EQ("//root/external/file", FS->status("//root/B")->getName());
949 EXPECT_EQ("//root/C", FS->status("//root/C")->getName());
951 // global configuration
952 FS = getFromYAMLString(
953 "{ 'use-external-names': false,\n"
955 " { 'type': 'file', 'name': '//root/A',\n"
956 " 'external-contents': '//root/external/file'\n"
958 " { 'type': 'file', 'name': '//root/B',\n"
959 " 'use-external-name': true,\n"
960 " 'external-contents': '//root/external/file'\n"
962 " { 'type': 'file', 'name': '//root/C',\n"
963 " 'use-external-name': false,\n"
964 " 'external-contents': '//root/external/file'\n"
967 ASSERT_TRUE(nullptr != FS.get());
970 EXPECT_EQ("//root/A", FS->status("//root/A")->getName());
972 EXPECT_EQ("//root/external/file", FS->status("//root/B")->getName());
973 EXPECT_EQ("//root/C", FS->status("//root/C")->getName());
976 TEST_F(VFSFromYAMLTest, MultiComponentPath) {
977 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
978 Lower->addRegularFile("//root/other");
981 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
983 " { 'type': 'file', 'name': '//root/path/to/file',\n"
984 " 'external-contents': '//root/other' }]\n"
986 ASSERT_TRUE(nullptr != FS.get());
987 EXPECT_FALSE(FS->status("//root/path/to/file").getError());
988 EXPECT_FALSE(FS->status("//root/path/to").getError());
989 EXPECT_FALSE(FS->status("//root/path").getError());
990 EXPECT_FALSE(FS->status("//root/").getError());
993 FS = getFromYAMLString(
995 " { 'type': 'directory', 'name': '//root/path/to',\n"
996 " 'contents': [ { 'type': 'file', 'name': 'file',\n"
997 " 'external-contents': '//root/other' }]}]\n"
999 ASSERT_TRUE(nullptr != FS.get());
1000 EXPECT_FALSE(FS->status("//root/path/to/file").getError());
1001 EXPECT_FALSE(FS->status("//root/path/to").getError());
1002 EXPECT_FALSE(FS->status("//root/path").getError());
1003 EXPECT_FALSE(FS->status("//root/").getError());
1006 FS = getFromYAMLString(
1008 " { 'type': 'directory', 'name': '//root/',\n"
1009 " 'contents': [ { 'type': 'file', 'name': 'path/to/file',\n"
1010 " 'external-contents': '//root/other' }]}]\n"
1012 ASSERT_TRUE(nullptr != FS.get());
1013 EXPECT_FALSE(FS->status("//root/path/to/file").getError());
1014 EXPECT_FALSE(FS->status("//root/path/to").getError());
1015 EXPECT_FALSE(FS->status("//root/path").getError());
1016 EXPECT_FALSE(FS->status("//root/").getError());
1019 TEST_F(VFSFromYAMLTest, TrailingSlashes) {
1020 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
1021 Lower->addRegularFile("//root/other");
1024 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
1026 " { 'type': 'directory', 'name': '//root/path/to////',\n"
1027 " 'contents': [ { 'type': 'file', 'name': 'file',\n"
1028 " 'external-contents': '//root/other' }]}]\n"
1030 ASSERT_TRUE(nullptr != FS.get());
1031 EXPECT_FALSE(FS->status("//root/path/to/file").getError());
1032 EXPECT_FALSE(FS->status("//root/path/to").getError());
1033 EXPECT_FALSE(FS->status("//root/path").getError());
1034 EXPECT_FALSE(FS->status("//root/").getError());
1037 TEST_F(VFSFromYAMLTest, DirectoryIteration) {
1038 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
1039 Lower->addDirectory("//root/");
1040 Lower->addDirectory("//root/foo");
1041 Lower->addDirectory("//root/foo/bar");
1042 Lower->addRegularFile("//root/foo/bar/a");
1043 Lower->addRegularFile("//root/foo/bar/b");
1044 Lower->addRegularFile("//root/file3");
1045 IntrusiveRefCntPtr<vfs::FileSystem> FS =
1046 getFromYAMLString("{ 'use-external-names': false,\n"
1049 " 'type': 'directory',\n"
1050 " 'name': '//root/',\n"
1051 " 'contents': [ {\n"
1052 " 'type': 'file',\n"
1053 " 'name': 'file1',\n"
1054 " 'external-contents': '//root/foo/bar/a'\n"
1057 " 'type': 'file',\n"
1058 " 'name': 'file2',\n"
1059 " 'external-contents': '//root/foo/bar/b'\n"
1066 ASSERT_TRUE(FS.get() != nullptr);
1068 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
1069 new vfs::OverlayFileSystem(Lower));
1073 checkContents(O->dir_begin("//root/", EC),
1074 {"//root/file1", "//root/file2", "//root/file3", "//root/foo"});
1076 checkContents(O->dir_begin("//root/foo/bar", EC),
1077 {"//root/foo/bar/a", "//root/foo/bar/b"});
1080 TEST_F(VFSFromYAMLTest, DirectoryIterationSameDirMultipleEntries) {
1081 // https://llvm.org/bugs/show_bug.cgi?id=27725
1082 if (!supportsSameDirMultipleYAMLEntries())
1085 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
1086 Lower->addDirectory("//root/zab");
1087 Lower->addDirectory("//root/baz");
1088 Lower->addRegularFile("//root/zab/a");
1089 Lower->addRegularFile("//root/zab/b");
1090 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
1091 "{ 'use-external-names': false,\n"
1094 " 'type': 'directory',\n"
1095 " 'name': '//root/baz/',\n"
1096 " 'contents': [ {\n"
1097 " 'type': 'file',\n"
1099 " 'external-contents': '//root/zab/a'\n"
1104 " 'type': 'directory',\n"
1105 " 'name': '//root/baz/',\n"
1106 " 'contents': [ {\n"
1107 " 'type': 'file',\n"
1109 " 'external-contents': '//root/zab/b'\n"
1116 ASSERT_TRUE(FS.get() != nullptr);
1118 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
1119 new vfs::OverlayFileSystem(Lower));
1124 checkContents(O->dir_begin("//root/baz/", EC),
1125 {"//root/baz/x", "//root/baz/y"});
1128 TEST_F(VFSFromYAMLTest, RecursiveDirectoryIterationLevel) {
1130 IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
1131 Lower->addDirectory("//root/a");
1132 Lower->addDirectory("//root/a/b");
1133 Lower->addDirectory("//root/a/b/c");
1134 Lower->addRegularFile("//root/a/b/c/file");
1135 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
1136 "{ 'use-external-names': false,\n"
1139 " 'type': 'directory',\n"
1140 " 'name': '//root/a/b/c/',\n"
1141 " 'contents': [ {\n"
1142 " 'type': 'file',\n"
1143 " 'name': 'file',\n"
1144 " 'external-contents': '//root/a/b/c/file'\n"
1151 ASSERT_TRUE(FS.get() != nullptr);
1153 IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
1154 new vfs::OverlayFileSystem(Lower));
1159 // Test recursive_directory_iterator level()
1160 vfs::recursive_directory_iterator I = vfs::recursive_directory_iterator(
1161 *O, "//root", EC), E;
1163 for (int l = 0; I != E; I.increment(EC), ++l) {
1165 EXPECT_EQ(I.level(), l);