3709 lines · cpp
1//===- unittests/Support/VirtualFileSystem.cpp -------------- VFS tests ---===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "llvm/Support/VirtualFileSystem.h"10#include "llvm/ADT/IntrusiveRefCntPtr.h"11#include "llvm/ADT/ScopeExit.h"12#include "llvm/Config/llvm-config.h"13#include "llvm/Support/Errc.h"14#include "llvm/Support/FileSystem.h"15#include "llvm/Support/MemoryBuffer.h"16#include "llvm/Support/Path.h"17#include "llvm/Support/SourceMgr.h"18#include "llvm/TargetParser/Host.h"19#include "llvm/TargetParser/Triple.h"20#include "llvm/Testing/Support/SupportHelpers.h"21#include "gmock/gmock.h"22#include "gtest/gtest.h"23#include <map>24#include <string>25 26using namespace llvm;27using llvm::sys::fs::UniqueID;28using llvm::unittest::TempDir;29using llvm::unittest::TempFile;30using llvm::unittest::TempLink;31using testing::ElementsAre;32using testing::Pair;33using testing::UnorderedElementsAre;34 35namespace {36struct DummyFile : public vfs::File {37 vfs::Status S;38 explicit DummyFile(vfs::Status S) : S(S) {}39 llvm::ErrorOr<vfs::Status> status() override { return S; }40 llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>41 getBuffer(const Twine &Name, int64_t FileSize, bool RequiresNullTerminator,42 bool IsVolatile) override {43 llvm_unreachable("unimplemented");44 }45 std::error_code close() override { return std::error_code(); }46};47 48class DummyFileSystem : public vfs::FileSystem {49 int FSID; // used to produce UniqueIDs50 int FileID; // used to produce UniqueIDs51 std::string WorkingDirectory;52 std::map<std::string, vfs::Status> FilesAndDirs;53 typedef std::map<std::string, vfs::Status>::const_iterator const_iterator;54 55 static int getNextFSID() {56 static int Count = 0;57 return Count++;58 }59 60public:61 DummyFileSystem() : FSID(getNextFSID()), FileID(0) {}62 63 ErrorOr<vfs::Status> status(const Twine &Path) override {64 auto I = findEntry(Path);65 if (I == FilesAndDirs.end())66 return make_error_code(llvm::errc::no_such_file_or_directory);67 return I->second;68 }69 ErrorOr<std::unique_ptr<vfs::File>>70 openFileForRead(const Twine &Path) override {71 auto S = status(Path);72 if (S)73 return std::unique_ptr<vfs::File>(new DummyFile{*S});74 return S.getError();75 }76 llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const override {77 return WorkingDirectory;78 }79 std::error_code setCurrentWorkingDirectory(const Twine &Path) override {80 WorkingDirectory = Path.str();81 return std::error_code();82 }83 // Map any symlink to "/symlink".84 std::error_code getRealPath(const Twine &Path,85 SmallVectorImpl<char> &Output) override {86 auto I = findEntry(Path);87 if (I == FilesAndDirs.end())88 return make_error_code(llvm::errc::no_such_file_or_directory);89 if (I->second.isSymlink()) {90 Output.clear();91 Twine("/symlink").toVector(Output);92 return std::error_code();93 }94 Output.clear();95 Path.toVector(Output);96 return std::error_code();97 }98 99 struct DirIterImpl : public llvm::vfs::detail::DirIterImpl {100 std::map<std::string, vfs::Status> &FilesAndDirs;101 std::map<std::string, vfs::Status>::iterator I;102 std::string Path;103 bool isInPath(StringRef S) {104 if (Path.size() < S.size() && S.starts_with(Path)) {105 auto LastSep = S.find_last_of('/');106 if (LastSep == Path.size() || LastSep == Path.size() - 1)107 return true;108 }109 return false;110 }111 DirIterImpl(std::map<std::string, vfs::Status> &FilesAndDirs,112 const Twine &_Path)113 : FilesAndDirs(FilesAndDirs), I(FilesAndDirs.begin()),114 Path(_Path.str()) {115 for (; I != FilesAndDirs.end(); ++I) {116 if (isInPath(I->first)) {117 CurrentEntry = vfs::directory_entry(std::string(I->second.getName()),118 I->second.getType());119 break;120 }121 }122 }123 std::error_code increment() override {124 ++I;125 for (; I != FilesAndDirs.end(); ++I) {126 if (isInPath(I->first)) {127 CurrentEntry = vfs::directory_entry(std::string(I->second.getName()),128 I->second.getType());129 break;130 }131 }132 if (I == FilesAndDirs.end())133 CurrentEntry = vfs::directory_entry();134 return std::error_code();135 }136 };137 138 vfs::directory_iterator dir_begin(const Twine &Dir,139 std::error_code &EC) override {140 return vfs::directory_iterator(141 std::make_shared<DirIterImpl>(FilesAndDirs, Dir));142 }143 144 void addEntry(StringRef Path, const vfs::Status &Status) {145 FilesAndDirs[std::string(Path)] = Status;146 }147 148 const_iterator findEntry(const Twine &Path) const {149 SmallString<128> P;150 Path.toVector(P);151 std::error_code EC = makeAbsolute(P);152 assert(!EC);153 (void)EC;154 return FilesAndDirs.find(std::string(P.str()));155 }156 157 void addRegularFile(StringRef Path, sys::fs::perms Perms = sys::fs::all_all) {158 vfs::Status S(Path, UniqueID(FSID, FileID++),159 std::chrono::system_clock::now(), 0, 0, 1024,160 sys::fs::file_type::regular_file, Perms);161 addEntry(Path, S);162 }163 164 void addDirectory(StringRef Path, sys::fs::perms Perms = sys::fs::all_all) {165 vfs::Status S(Path, UniqueID(FSID, FileID++),166 std::chrono::system_clock::now(), 0, 0, 0,167 sys::fs::file_type::directory_file, Perms);168 addEntry(Path, S);169 }170 171 void addSymlink(StringRef Path) {172 vfs::Status S(Path, UniqueID(FSID, FileID++),173 std::chrono::system_clock::now(), 0, 0, 0,174 sys::fs::file_type::symlink_file, sys::fs::all_all);175 addEntry(Path, S);176 }177 178protected:179 void printImpl(raw_ostream &OS, PrintType Type,180 unsigned IndentLevel) const override {181 printIndent(OS, IndentLevel);182 OS << "DummyFileSystem (";183 switch (Type) {184 case vfs::FileSystem::PrintType::Summary:185 OS << "Summary";186 break;187 case vfs::FileSystem::PrintType::Contents:188 OS << "Contents";189 break;190 case vfs::FileSystem::PrintType::RecursiveContents:191 OS << "RecursiveContents";192 break;193 }194 OS << ")\n";195 }196};197 198class ErrorDummyFileSystem : public DummyFileSystem {199 std::error_code setCurrentWorkingDirectory(const Twine &Path) override {200 return llvm::errc::no_such_file_or_directory;201 }202};203 204/// A version of \c DummyFileSystem that aborts on \c status() to test that205/// \c exists() is being used.206class NoStatusDummyFileSystem : public DummyFileSystem {207public:208 ErrorOr<vfs::Status> status(const Twine &Path) override {209 llvm::report_fatal_error(210 "unexpected call to NoStatusDummyFileSystem::status");211 }212 213 bool exists(const Twine &Path) override {214 auto Status = DummyFileSystem::status(Path);215 return Status && Status->exists();216 }217};218 219/// Replace back-slashes by front-slashes.220std::string getPosixPath(const Twine &S) {221 SmallString<128> Result;222 llvm::sys::path::native(S, Result, llvm::sys::path::Style::posix);223 return std::string(Result.str());224}225} // end anonymous namespace226 227TEST(VirtualFileSystemTest, StatusQueries) {228 auto D = makeIntrusiveRefCnt<DummyFileSystem>();229 ErrorOr<vfs::Status> Status((std::error_code()));230 231 D->addRegularFile("/foo");232 Status = D->status("/foo");233 ASSERT_FALSE(Status.getError());234 EXPECT_TRUE(Status->isStatusKnown());235 EXPECT_FALSE(Status->isDirectory());236 EXPECT_TRUE(Status->isRegularFile());237 EXPECT_FALSE(Status->isSymlink());238 EXPECT_FALSE(Status->isOther());239 EXPECT_TRUE(Status->exists());240 241 D->addDirectory("/bar");242 Status = D->status("/bar");243 ASSERT_FALSE(Status.getError());244 EXPECT_TRUE(Status->isStatusKnown());245 EXPECT_TRUE(Status->isDirectory());246 EXPECT_FALSE(Status->isRegularFile());247 EXPECT_FALSE(Status->isSymlink());248 EXPECT_FALSE(Status->isOther());249 EXPECT_TRUE(Status->exists());250 251 D->addSymlink("/baz");252 Status = D->status("/baz");253 ASSERT_FALSE(Status.getError());254 EXPECT_TRUE(Status->isStatusKnown());255 EXPECT_FALSE(Status->isDirectory());256 EXPECT_FALSE(Status->isRegularFile());257 EXPECT_TRUE(Status->isSymlink());258 EXPECT_FALSE(Status->isOther());259 EXPECT_TRUE(Status->exists());260 261 EXPECT_TRUE(Status->equivalent(*Status));262 ErrorOr<vfs::Status> Status2 = D->status("/foo");263 ASSERT_FALSE(Status2.getError());264 EXPECT_FALSE(Status->equivalent(*Status2));265}266 267TEST(VirtualFileSystemTest, BaseOnlyOverlay) {268 auto D = makeIntrusiveRefCnt<DummyFileSystem>();269 ErrorOr<vfs::Status> Status((std::error_code()));270 EXPECT_FALSE(Status = D->status("/foo"));271 272 auto O = makeIntrusiveRefCnt<vfs::OverlayFileSystem>(D);273 EXPECT_FALSE(Status = O->status("/foo"));274 275 D->addRegularFile("/foo");276 Status = D->status("/foo");277 EXPECT_FALSE(Status.getError());278 279 ErrorOr<vfs::Status> Status2((std::error_code()));280 Status2 = O->status("/foo");281 EXPECT_FALSE(Status2.getError());282 EXPECT_TRUE(Status->equivalent(*Status2));283}284 285TEST(VirtualFileSystemTest, GetRealPathInOverlay) {286 auto Lower = makeIntrusiveRefCnt<DummyFileSystem>();287 Lower->addRegularFile("/foo");288 Lower->addSymlink("/lower_link");289 auto Upper = makeIntrusiveRefCnt<DummyFileSystem>();290 291 auto O = makeIntrusiveRefCnt<vfs::OverlayFileSystem>(Lower);292 O->pushOverlay(Upper);293 294 // Regular file.295 SmallString<16> RealPath;296 EXPECT_FALSE(O->getRealPath("/foo", RealPath));297 EXPECT_EQ(RealPath.str(), "/foo");298 299 // Expect no error getting real path for symlink in lower overlay.300 EXPECT_FALSE(O->getRealPath("/lower_link", RealPath));301 EXPECT_EQ(RealPath.str(), "/symlink");302 303 // Try a non-existing link.304 EXPECT_EQ(O->getRealPath("/upper_link", RealPath),305 errc::no_such_file_or_directory);306 307 // Add a new symlink in upper.308 Upper->addSymlink("/upper_link");309 EXPECT_FALSE(O->getRealPath("/upper_link", RealPath));310 EXPECT_EQ(RealPath.str(), "/symlink");311}312 313TEST(VirtualFileSystemTest, OverlayFiles) {314 auto Base = makeIntrusiveRefCnt<DummyFileSystem>();315 auto Middle = makeIntrusiveRefCnt<DummyFileSystem>();316 auto Top = makeIntrusiveRefCnt<DummyFileSystem>();317 auto O = makeIntrusiveRefCnt<vfs::OverlayFileSystem>(Base);318 O->pushOverlay(Middle);319 O->pushOverlay(Top);320 321 ErrorOr<vfs::Status> Status1((std::error_code())),322 Status2((std::error_code())), Status3((std::error_code())),323 StatusB((std::error_code())), StatusM((std::error_code())),324 StatusT((std::error_code()));325 326 Base->addRegularFile("/foo");327 StatusB = Base->status("/foo");328 ASSERT_FALSE(StatusB.getError());329 Status1 = O->status("/foo");330 ASSERT_FALSE(Status1.getError());331 Middle->addRegularFile("/foo");332 StatusM = Middle->status("/foo");333 ASSERT_FALSE(StatusM.getError());334 Status2 = O->status("/foo");335 ASSERT_FALSE(Status2.getError());336 Top->addRegularFile("/foo");337 StatusT = Top->status("/foo");338 ASSERT_FALSE(StatusT.getError());339 Status3 = O->status("/foo");340 ASSERT_FALSE(Status3.getError());341 342 EXPECT_TRUE(Status1->equivalent(*StatusB));343 EXPECT_TRUE(Status2->equivalent(*StatusM));344 EXPECT_TRUE(Status3->equivalent(*StatusT));345 346 EXPECT_FALSE(Status1->equivalent(*Status2));347 EXPECT_FALSE(Status2->equivalent(*Status3));348 EXPECT_FALSE(Status1->equivalent(*Status3));349}350 351TEST(VirtualFileSystemTest, OverlayDirsNonMerged) {352 auto Lower = makeIntrusiveRefCnt<DummyFileSystem>();353 auto Upper = makeIntrusiveRefCnt<DummyFileSystem>();354 auto O = makeIntrusiveRefCnt<vfs::OverlayFileSystem>(Lower);355 O->pushOverlay(Upper);356 357 Lower->addDirectory("/lower-only");358 Upper->addDirectory("/upper-only");359 360 // non-merged paths should be the same361 ErrorOr<vfs::Status> Status1 = Lower->status("/lower-only");362 ASSERT_FALSE(Status1.getError());363 ErrorOr<vfs::Status> Status2 = O->status("/lower-only");364 ASSERT_FALSE(Status2.getError());365 EXPECT_TRUE(Status1->equivalent(*Status2));366 367 Status1 = Upper->status("/upper-only");368 ASSERT_FALSE(Status1.getError());369 Status2 = O->status("/upper-only");370 ASSERT_FALSE(Status2.getError());371 EXPECT_TRUE(Status1->equivalent(*Status2));372}373 374TEST(VirtualFileSystemTest, MergedDirPermissions) {375 // merged directories get the permissions of the upper dir376 auto Lower = makeIntrusiveRefCnt<DummyFileSystem>();377 auto Upper = makeIntrusiveRefCnt<DummyFileSystem>();378 auto O = makeIntrusiveRefCnt<vfs::OverlayFileSystem>(Lower);379 O->pushOverlay(Upper);380 381 ErrorOr<vfs::Status> Status((std::error_code()));382 Lower->addDirectory("/both", sys::fs::owner_read);383 Upper->addDirectory("/both", sys::fs::owner_all | sys::fs::group_read);384 Status = O->status("/both");385 ASSERT_FALSE(Status.getError());386 EXPECT_EQ(0740, Status->getPermissions());387 388 // permissions (as usual) are not recursively applied389 Lower->addRegularFile("/both/foo", sys::fs::owner_read);390 Upper->addRegularFile("/both/bar", sys::fs::owner_write);391 Status = O->status("/both/foo");392 ASSERT_FALSE(Status.getError());393 EXPECT_EQ(0400, Status->getPermissions());394 Status = O->status("/both/bar");395 ASSERT_FALSE(Status.getError());396 EXPECT_EQ(0200, Status->getPermissions());397}398 399TEST(VirtualFileSystemTest, OverlayIterator) {400 auto Lower = makeIntrusiveRefCnt<DummyFileSystem>();401 Lower->addRegularFile("/foo");402 auto Upper = makeIntrusiveRefCnt<DummyFileSystem>();403 404 auto O = makeIntrusiveRefCnt<vfs::OverlayFileSystem>(Lower);405 O->pushOverlay(Upper);406 407 ErrorOr<vfs::Status> Status((std::error_code()));408 {409 auto it = O->overlays_begin();410 auto end = O->overlays_end();411 412 EXPECT_NE(it, end);413 414 Status = (*it)->status("/foo");415 ASSERT_TRUE(Status.getError());416 417 it++;418 EXPECT_NE(it, end);419 420 Status = (*it)->status("/foo");421 ASSERT_FALSE(Status.getError());422 EXPECT_TRUE(Status->exists());423 424 it++;425 EXPECT_EQ(it, end);426 }427 428 {429 auto it = O->overlays_rbegin();430 auto end = O->overlays_rend();431 432 EXPECT_NE(it, end);433 434 Status = (*it)->status("/foo");435 ASSERT_FALSE(Status.getError());436 EXPECT_TRUE(Status->exists());437 438 it++;439 EXPECT_NE(it, end);440 441 Status = (*it)->status("/foo");442 ASSERT_TRUE(Status.getError());443 444 it++;445 EXPECT_EQ(it, end);446 }447}448 449TEST(VirtualFileSystemTest, BasicRealFSIteration) {450 TempDir TestDirectory("virtual-file-system-test", /*Unique*/ true);451 IntrusiveRefCntPtr<vfs::FileSystem> FS = vfs::getRealFileSystem();452 453 std::error_code EC;454 vfs::directory_iterator I = FS->dir_begin(Twine(TestDirectory.path()), EC);455 ASSERT_FALSE(EC);456 EXPECT_EQ(vfs::directory_iterator(), I); // empty directory is empty457 458 TempDir _a(TestDirectory.path("a"));459 TempDir _ab(TestDirectory.path("a/b"));460 TempDir _c(TestDirectory.path("c"));461 TempDir _cd(TestDirectory.path("c/d"));462 463 I = FS->dir_begin(Twine(TestDirectory.path()), EC);464 ASSERT_FALSE(EC);465 ASSERT_NE(vfs::directory_iterator(), I);466 // Check either a or c, since we can't rely on the iteration order.467 EXPECT_TRUE(I->path().ends_with("a") || I->path().ends_with("c"));468 I.increment(EC);469 ASSERT_FALSE(EC);470 ASSERT_NE(vfs::directory_iterator(), I);471 EXPECT_TRUE(I->path().ends_with("a") || I->path().ends_with("c"));472 I.increment(EC);473 EXPECT_EQ(vfs::directory_iterator(), I);474}475 476#ifdef LLVM_ON_UNIX477TEST(VirtualFileSystemTest, MultipleWorkingDirs) {478 // Our root contains a/aa, b/bb, c, where c is a link to a/.479 // Run tests both in root/b/ and root/c/ (to test "normal" and symlink dirs).480 // Interleave operations to show the working directories are independent.481 TempDir Root("r", /*Unique*/ true);482 TempDir ADir(Root.path("a"));483 TempDir BDir(Root.path("b"));484 TempLink C(ADir.path(), Root.path("c"));485 TempFile AA(ADir.path("aa"), "", "aaaa");486 TempFile BB(BDir.path("bb"), "", "bbbb");487 std::unique_ptr<vfs::FileSystem> BFS = vfs::createPhysicalFileSystem(),488 CFS = vfs::createPhysicalFileSystem();489 490 ASSERT_FALSE(BFS->setCurrentWorkingDirectory(BDir.path()));491 ASSERT_FALSE(CFS->setCurrentWorkingDirectory(C.path()));492 EXPECT_EQ(BDir.path(), *BFS->getCurrentWorkingDirectory());493 EXPECT_EQ(C.path(), *CFS->getCurrentWorkingDirectory());494 495 // openFileForRead(), indirectly.496 auto BBuf = BFS->getBufferForFile("bb");497 ASSERT_TRUE(BBuf);498 EXPECT_EQ("bbbb", (*BBuf)->getBuffer());499 500 auto ABuf = CFS->getBufferForFile("aa");501 ASSERT_TRUE(ABuf);502 EXPECT_EQ("aaaa", (*ABuf)->getBuffer());503 504 // status()505 auto BStat = BFS->status("bb");506 ASSERT_TRUE(BStat);507 EXPECT_EQ("bb", BStat->getName());508 509 auto AStat = CFS->status("aa");510 ASSERT_TRUE(AStat);511 EXPECT_EQ("aa", AStat->getName()); // unresolved name512 513 // getRealPath()514 SmallString<128> BPath;515 ASSERT_FALSE(BFS->getRealPath("bb", BPath));516 EXPECT_EQ(BB.path(), BPath);517 518 SmallString<128> APath;519 ASSERT_FALSE(CFS->getRealPath("aa", APath));520 EXPECT_EQ(AA.path(), APath); // Reports resolved name.521 522 // dir_begin523 std::error_code EC;524 auto BIt = BFS->dir_begin(".", EC);525 ASSERT_FALSE(EC);526 ASSERT_NE(BIt, vfs::directory_iterator());527 EXPECT_EQ((BDir.path() + "/./bb").str(), BIt->path());528 BIt.increment(EC);529 ASSERT_FALSE(EC);530 ASSERT_EQ(BIt, vfs::directory_iterator());531 532 auto CIt = CFS->dir_begin(".", EC);533 ASSERT_FALSE(EC);534 ASSERT_NE(CIt, vfs::directory_iterator());535 EXPECT_EQ((ADir.path() + "/./aa").str(),536 CIt->path()); // Partly resolved name!537 CIt.increment(EC); // Because likely to read through this path.538 ASSERT_FALSE(EC);539 ASSERT_EQ(CIt, vfs::directory_iterator());540}541 542TEST(VirtualFileSystemTest, PhysicalFileSystemWorkingDirFailure) {543 TempDir D2("d2", /*Unique*/ true);544 SmallString<128> WD, PrevWD;545 ASSERT_EQ(sys::fs::current_path(PrevWD), std::error_code());546 ASSERT_EQ(sys::fs::createUniqueDirectory("d1", WD), std::error_code());547 ASSERT_EQ(sys::fs::set_current_path(WD), std::error_code());548 auto Restore =549 llvm::make_scope_exit([&] { sys::fs::set_current_path(PrevWD); });550 551 // Delete the working directory to create an error.552 if (sys::fs::remove_directories(WD, /*IgnoreErrors=*/false))553 // Some platforms (e.g. Solaris) disallow removal of the working directory.554 GTEST_SKIP() << "test requires deletion of working directory";555 556#ifdef __CYGWIN__557 GTEST_SKIP() << "Cygwin getcwd succeeds with unlinked working directory";558#endif559 560 // Verify that we still get two separate working directories.561 auto FS1 = vfs::createPhysicalFileSystem();562 auto FS2 = vfs::createPhysicalFileSystem();563 ASSERT_EQ(FS1->getCurrentWorkingDirectory().getError(),564 errc::no_such_file_or_directory);565 ASSERT_EQ(FS1->setCurrentWorkingDirectory(D2.path()), std::error_code());566 ASSERT_EQ(FS1->getCurrentWorkingDirectory().get(), D2.path());567 EXPECT_EQ(FS2->getCurrentWorkingDirectory().getError(),568 errc::no_such_file_or_directory);569 SmallString<128> WD2;570 EXPECT_EQ(sys::fs::current_path(WD2), errc::no_such_file_or_directory);571}572 573TEST(VirtualFileSystemTest, BrokenSymlinkRealFSIteration) {574 TempDir TestDirectory("virtual-file-system-test", /*Unique*/ true);575 IntrusiveRefCntPtr<vfs::FileSystem> FS = vfs::getRealFileSystem();576 577 TempLink _a("no_such_file", TestDirectory.path("a"));578 TempDir _b(TestDirectory.path("b"));579 TempLink _c("no_such_file", TestDirectory.path("c"));580 581 // Should get no iteration error, but a stat error for the broken symlinks.582 std::map<std::string, std::error_code> StatResults;583 std::error_code EC;584 for (vfs::directory_iterator585 I = FS->dir_begin(Twine(TestDirectory.path()), EC),586 E;587 I != E; I.increment(EC)) {588 EXPECT_FALSE(EC);589 StatResults[std::string(sys::path::filename(I->path()))] =590 FS->status(I->path()).getError();591 }592 EXPECT_THAT(593 StatResults,594 ElementsAre(595 Pair("a", std::make_error_code(std::errc::no_such_file_or_directory)),596 Pair("b", std::error_code()),597 Pair("c",598 std::make_error_code(std::errc::no_such_file_or_directory))));599}600#endif601 602TEST(VirtualFileSystemTest, BasicRealFSRecursiveIteration) {603 TempDir TestDirectory("virtual-file-system-test", /*Unique*/ true);604 IntrusiveRefCntPtr<vfs::FileSystem> FS = vfs::getRealFileSystem();605 606 std::error_code EC;607 auto I =608 vfs::recursive_directory_iterator(*FS, Twine(TestDirectory.path()), EC);609 ASSERT_FALSE(EC);610 EXPECT_EQ(vfs::recursive_directory_iterator(), I); // empty directory is empty611 612 TempDir _a(TestDirectory.path("a"));613 TempDir _ab(TestDirectory.path("a/b"));614 TempDir _c(TestDirectory.path("c"));615 TempDir _cd(TestDirectory.path("c/d"));616 617 I = vfs::recursive_directory_iterator(*FS, Twine(TestDirectory.path()), EC);618 ASSERT_FALSE(EC);619 ASSERT_NE(vfs::recursive_directory_iterator(), I);620 621 std::vector<std::string> Contents;622 for (auto E = vfs::recursive_directory_iterator(); !EC && I != E;623 I.increment(EC)) {624 Contents.push_back(std::string(I->path()));625 }626 627 // Check contents, which may be in any order628 EXPECT_EQ(4U, Contents.size());629 int Counts[4] = {0, 0, 0, 0};630 for (const std::string &Name : Contents) {631 ASSERT_FALSE(Name.empty());632 int Index = Name[Name.size() - 1] - 'a';633 ASSERT_GE(Index, 0);634 ASSERT_LT(Index, 4);635 Counts[Index]++;636 }637 EXPECT_EQ(1, Counts[0]); // a638 EXPECT_EQ(1, Counts[1]); // b639 EXPECT_EQ(1, Counts[2]); // c640 EXPECT_EQ(1, Counts[3]); // d641}642 643TEST(VirtualFileSystemTest, BasicRealFSRecursiveIterationNoPush) {644 TempDir TestDirectory("virtual-file-system-test", /*Unique*/ true);645 646 TempDir _a(TestDirectory.path("a"));647 TempDir _ab(TestDirectory.path("a/b"));648 TempDir _c(TestDirectory.path("c"));649 TempDir _cd(TestDirectory.path("c/d"));650 TempDir _e(TestDirectory.path("e"));651 TempDir _ef(TestDirectory.path("e/f"));652 TempDir _g(TestDirectory.path("g"));653 654 IntrusiveRefCntPtr<vfs::FileSystem> FS = vfs::getRealFileSystem();655 656 // Test that calling no_push on entries without subdirectories has no effect.657 {658 std::error_code EC;659 auto I =660 vfs::recursive_directory_iterator(*FS, Twine(TestDirectory.path()), EC);661 ASSERT_FALSE(EC);662 663 std::vector<std::string> Contents;664 for (auto E = vfs::recursive_directory_iterator(); !EC && I != E;665 I.increment(EC)) {666 Contents.push_back(std::string(I->path()));667 char last = I->path().back();668 switch (last) {669 case 'b':670 case 'd':671 case 'f':672 case 'g':673 I.no_push();674 break;675 default:676 break;677 }678 }679 EXPECT_EQ(7U, Contents.size());680 }681 682 // Test that calling no_push skips subdirectories.683 {684 std::error_code EC;685 auto I =686 vfs::recursive_directory_iterator(*FS, Twine(TestDirectory.path()), EC);687 ASSERT_FALSE(EC);688 689 std::vector<std::string> Contents;690 for (auto E = vfs::recursive_directory_iterator(); !EC && I != E;691 I.increment(EC)) {692 Contents.push_back(std::string(I->path()));693 char last = I->path().back();694 switch (last) {695 case 'a':696 case 'c':697 case 'e':698 I.no_push();699 break;700 default:701 break;702 }703 }704 705 // Check contents, which may be in any order706 EXPECT_EQ(4U, Contents.size());707 int Counts[7] = {0, 0, 0, 0, 0, 0, 0};708 for (const std::string &Name : Contents) {709 ASSERT_FALSE(Name.empty());710 int Index = Name[Name.size() - 1] - 'a';711 ASSERT_GE(Index, 0);712 ASSERT_LT(Index, 7);713 Counts[Index]++;714 }715 EXPECT_EQ(1, Counts[0]); // a716 EXPECT_EQ(0, Counts[1]); // b717 EXPECT_EQ(1, Counts[2]); // c718 EXPECT_EQ(0, Counts[3]); // d719 EXPECT_EQ(1, Counts[4]); // e720 EXPECT_EQ(0, Counts[5]); // f721 EXPECT_EQ(1, Counts[6]); // g722 }723}724 725#ifdef LLVM_ON_UNIX726TEST(VirtualFileSystemTest, BrokenSymlinkRealFSRecursiveIteration) {727 TempDir TestDirectory("virtual-file-system-test", /*Unique*/ true);728 IntrusiveRefCntPtr<vfs::FileSystem> FS = vfs::getRealFileSystem();729 730 TempLink _a("no_such_file", TestDirectory.path("a"));731 TempDir _b(TestDirectory.path("b"));732 TempLink _ba("no_such_file", TestDirectory.path("b/a"));733 TempDir _bb(TestDirectory.path("b/b"));734 TempLink _bc("no_such_file", TestDirectory.path("b/c"));735 TempLink _c("no_such_file", TestDirectory.path("c"));736 TempDir _d(TestDirectory.path("d"));737 TempDir _dd(TestDirectory.path("d/d"));738 TempDir _ddd(TestDirectory.path("d/d/d"));739 TempLink _e("no_such_file", TestDirectory.path("e"));740 741 std::vector<std::string> VisitedBrokenSymlinks;742 std::vector<std::string> VisitedNonBrokenSymlinks;743 std::error_code EC;744 for (vfs::recursive_directory_iterator745 I(*FS, Twine(TestDirectory.path()), EC),746 E;747 I != E; I.increment(EC)) {748 EXPECT_FALSE(EC);749 (FS->status(I->path()) ? VisitedNonBrokenSymlinks : VisitedBrokenSymlinks)750 .push_back(std::string(I->path()));751 }752 753 // Check visited file names.754 EXPECT_THAT(VisitedBrokenSymlinks,755 UnorderedElementsAre(_a.path().str(), _ba.path().str(),756 _bc.path().str(), _c.path().str(),757 _e.path().str()));758 EXPECT_THAT(VisitedNonBrokenSymlinks,759 UnorderedElementsAre(_b.path().str(), _bb.path().str(),760 _d.path().str(), _dd.path().str(),761 _ddd.path().str()));762}763#endif764 765template <typename DirIter>766static void checkContents(DirIter I, ArrayRef<StringRef> ExpectedOut) {767 std::error_code EC;768 SmallVector<StringRef, 4> Expected(ExpectedOut);769 SmallVector<std::string, 4> InputToCheck;770 771 // Do not rely on iteration order to check for contents, sort both772 // content vectors before comparison.773 for (DirIter E; !EC && I != E; I.increment(EC))774 InputToCheck.push_back(std::string(I->path()));775 776 llvm::sort(InputToCheck);777 llvm::sort(Expected);778 EXPECT_EQ(InputToCheck.size(), Expected.size());779 780 unsigned LastElt = std::min(InputToCheck.size(), Expected.size());781 for (unsigned Idx = 0; Idx != LastElt; ++Idx)782 EXPECT_EQ(StringRef(InputToCheck[Idx]), Expected[Idx]);783}784 785TEST(VirtualFileSystemTest, OverlayIteration) {786 auto Lower = makeIntrusiveRefCnt<DummyFileSystem>();787 auto Upper = makeIntrusiveRefCnt<DummyFileSystem>();788 auto O = makeIntrusiveRefCnt<vfs::OverlayFileSystem>(Lower);789 O->pushOverlay(Upper);790 791 std::error_code EC;792 checkContents(O->dir_begin("/", EC), ArrayRef<StringRef>());793 794 Lower->addRegularFile("/file1");795 checkContents(O->dir_begin("/", EC), ArrayRef<StringRef>("/file1"));796 797 Upper->addRegularFile("/file2");798 checkContents(O->dir_begin("/", EC), {"/file2", "/file1"});799 800 Lower->addDirectory("/dir1");801 Lower->addRegularFile("/dir1/foo");802 Upper->addDirectory("/dir2");803 Upper->addRegularFile("/dir2/foo");804 checkContents(O->dir_begin("/dir2", EC), ArrayRef<StringRef>("/dir2/foo"));805 checkContents(O->dir_begin("/", EC), {"/dir2", "/file2", "/dir1", "/file1"});806}807 808TEST(VirtualFileSystemTest, OverlayRecursiveIteration) {809 auto Lower = makeIntrusiveRefCnt<DummyFileSystem>();810 auto Middle = makeIntrusiveRefCnt<DummyFileSystem>();811 auto Upper = makeIntrusiveRefCnt<DummyFileSystem>();812 auto O = makeIntrusiveRefCnt<vfs::OverlayFileSystem>(Lower);813 O->pushOverlay(Middle);814 O->pushOverlay(Upper);815 816 std::error_code EC;817 checkContents(vfs::recursive_directory_iterator(*O, "/", EC),818 ArrayRef<StringRef>());819 820 Lower->addRegularFile("/file1");821 checkContents(vfs::recursive_directory_iterator(*O, "/", EC),822 ArrayRef<StringRef>("/file1"));823 824 Upper->addDirectory("/dir");825 Upper->addRegularFile("/dir/file2");826 checkContents(vfs::recursive_directory_iterator(*O, "/", EC),827 {"/dir", "/dir/file2", "/file1"});828 829 Lower->addDirectory("/dir1");830 Lower->addRegularFile("/dir1/foo");831 Lower->addDirectory("/dir1/a");832 Lower->addRegularFile("/dir1/a/b");833 Middle->addDirectory("/a");834 Middle->addDirectory("/a/b");835 Middle->addDirectory("/a/b/c");836 Middle->addRegularFile("/a/b/c/d");837 Middle->addRegularFile("/hiddenByUp");838 Upper->addDirectory("/dir2");839 Upper->addRegularFile("/dir2/foo");840 Upper->addRegularFile("/hiddenByUp");841 checkContents(vfs::recursive_directory_iterator(*O, "/dir2", EC),842 ArrayRef<StringRef>("/dir2/foo"));843 checkContents(vfs::recursive_directory_iterator(*O, "/", EC),844 {"/dir", "/dir/file2", "/dir2", "/dir2/foo", "/hiddenByUp",845 "/a", "/a/b", "/a/b/c", "/a/b/c/d", "/dir1", "/dir1/a",846 "/dir1/a/b", "/dir1/foo", "/file1"});847}848 849TEST(VirtualFileSystemTest, ThreeLevelIteration) {850 auto Lower = makeIntrusiveRefCnt<DummyFileSystem>();851 auto Middle = makeIntrusiveRefCnt<DummyFileSystem>();852 auto Upper = makeIntrusiveRefCnt<DummyFileSystem>();853 auto O = makeIntrusiveRefCnt<vfs::OverlayFileSystem>(Lower);854 O->pushOverlay(Middle);855 O->pushOverlay(Upper);856 857 std::error_code EC;858 checkContents(O->dir_begin("/", EC), ArrayRef<StringRef>());859 860 Middle->addRegularFile("/file2");861 checkContents(O->dir_begin("/", EC), ArrayRef<StringRef>("/file2"));862 863 Lower->addRegularFile("/file1");864 Upper->addRegularFile("/file3");865 checkContents(O->dir_begin("/", EC), {"/file3", "/file2", "/file1"});866}867 868TEST(VirtualFileSystemTest, HiddenInIteration) {869 auto Lower = makeIntrusiveRefCnt<DummyFileSystem>();870 auto Middle = makeIntrusiveRefCnt<DummyFileSystem>();871 auto Upper = makeIntrusiveRefCnt<DummyFileSystem>();872 auto O = makeIntrusiveRefCnt<vfs::OverlayFileSystem>(Lower);873 O->pushOverlay(Middle);874 O->pushOverlay(Upper);875 876 std::error_code EC;877 Lower->addRegularFile("/onlyInLow");878 Lower->addDirectory("/hiddenByMid");879 Lower->addDirectory("/hiddenByUp");880 Middle->addRegularFile("/onlyInMid");881 Middle->addRegularFile("/hiddenByMid");882 Middle->addDirectory("/hiddenByUp");883 Upper->addRegularFile("/onlyInUp");884 Upper->addRegularFile("/hiddenByUp");885 checkContents(886 O->dir_begin("/", EC),887 {"/hiddenByUp", "/onlyInUp", "/hiddenByMid", "/onlyInMid", "/onlyInLow"});888 889 // Make sure we get the top-most entry890 {891 std::error_code EC;892 vfs::directory_iterator I = O->dir_begin("/", EC), E;893 for (; !EC && I != E; I.increment(EC))894 if (I->path() == "/hiddenByUp")895 break;896 ASSERT_NE(E, I);897 EXPECT_EQ(sys::fs::file_type::regular_file, I->type());898 }899 {900 std::error_code EC;901 vfs::directory_iterator I = O->dir_begin("/", EC), E;902 for (; !EC && I != E; I.increment(EC))903 if (I->path() == "/hiddenByMid")904 break;905 ASSERT_NE(E, I);906 EXPECT_EQ(sys::fs::file_type::regular_file, I->type());907 }908}909 910TEST(VirtualFileSystemTest, Visit) {911 auto Base = makeIntrusiveRefCnt<DummyFileSystem>();912 auto Middle = makeIntrusiveRefCnt<DummyFileSystem>();913 auto Top = makeIntrusiveRefCnt<DummyFileSystem>();914 auto O = makeIntrusiveRefCnt<vfs::OverlayFileSystem>(Base);915 O->pushOverlay(Middle);916 O->pushOverlay(Top);917 918 auto YAML =919 MemoryBuffer::getMemBuffer("{\n"920 " 'version': 0,\n"921 " 'redirecting-with': 'redirect-only',\n"922 " 'roots': [\n"923 " {\n"924 " 'type': 'file',\n"925 " 'name': '/vfile',\n"926 " 'external-contents': '/a',\n"927 " },"928 " ]\n"929 "}");930 931 IntrusiveRefCntPtr<vfs::RedirectingFileSystem> Redirecting =932 vfs::RedirectingFileSystem::create(std::move(YAML), nullptr, "", nullptr,933 O)934 .release();935 936 vfs::ProxyFileSystem PFS(Redirecting);937 938 std::vector<const vfs::FileSystem *> FSs;939 PFS.visit([&](const vfs::FileSystem &FS) { FSs.push_back(&FS); });940 941 ASSERT_EQ(size_t(6), FSs.size());942 EXPECT_TRUE(isa<vfs::ProxyFileSystem>(FSs[0]));943 EXPECT_TRUE(isa<vfs::RedirectingFileSystem>(FSs[1]));944 EXPECT_TRUE(isa<vfs::OverlayFileSystem>(FSs[2]));945 EXPECT_TRUE(isa<vfs::FileSystem>(FSs[3]));946 EXPECT_TRUE(isa<vfs::FileSystem>(FSs[4]));947 EXPECT_TRUE(isa<vfs::FileSystem>(FSs[5]));948}949 950TEST(OverlayFileSystemTest, PrintOutput) {951 auto Dummy = makeIntrusiveRefCnt<DummyFileSystem>();952 auto Overlay1 = makeIntrusiveRefCnt<vfs::OverlayFileSystem>(Dummy);953 Overlay1->pushOverlay(Dummy);954 auto Overlay2 = makeIntrusiveRefCnt<vfs::OverlayFileSystem>(Overlay1);955 Overlay2->pushOverlay(Dummy);956 957 SmallString<0> Output;958 raw_svector_ostream OuputStream{Output};959 960 Overlay2->print(OuputStream, vfs::FileSystem::PrintType::Summary);961 ASSERT_EQ("OverlayFileSystem\n", Output);962 963 Output.clear();964 Overlay2->print(OuputStream, vfs::FileSystem::PrintType::Contents);965 ASSERT_EQ("OverlayFileSystem\n"966 " DummyFileSystem (Summary)\n"967 " OverlayFileSystem\n",968 Output);969 970 Output.clear();971 Overlay2->print(OuputStream, vfs::FileSystem::PrintType::RecursiveContents);972 ASSERT_EQ("OverlayFileSystem\n"973 " DummyFileSystem (RecursiveContents)\n"974 " OverlayFileSystem\n"975 " DummyFileSystem (RecursiveContents)\n"976 " DummyFileSystem (RecursiveContents)\n",977 Output);978}979 980TEST(OverlayFileSystemTest, Exists) {981 auto Lower = makeIntrusiveRefCnt<NoStatusDummyFileSystem>();982 auto Upper = makeIntrusiveRefCnt<NoStatusDummyFileSystem>();983 auto O = makeIntrusiveRefCnt<vfs::OverlayFileSystem>(Lower);984 O->pushOverlay(Upper);985 986 Lower->addDirectory("/both");987 Upper->addDirectory("/both");988 Lower->addRegularFile("/both/lower_file");989 Upper->addRegularFile("/both/upper_file");990 Lower->addDirectory("/lower");991 Upper->addDirectory("/upper");992 993 EXPECT_TRUE(O->exists("/both"));994 EXPECT_TRUE(O->exists("/both"));995 EXPECT_TRUE(O->exists("/both/lower_file"));996 EXPECT_TRUE(O->exists("/both/upper_file"));997 EXPECT_TRUE(O->exists("/lower"));998 EXPECT_TRUE(O->exists("/upper"));999 EXPECT_FALSE(O->exists("/both/nope"));1000 EXPECT_FALSE(O->exists("/nope"));1001}1002 1003TEST(ProxyFileSystemTest, Basic) {1004 auto Base = makeIntrusiveRefCnt<vfs::InMemoryFileSystem>();1005 vfs::ProxyFileSystem PFS(Base);1006 1007 Base->addFile("/a", 0, MemoryBuffer::getMemBuffer("test"));1008 1009 auto Stat = PFS.status("/a");1010 ASSERT_FALSE(Stat.getError());1011 1012 auto File = PFS.openFileForRead("/a");1013 ASSERT_FALSE(File.getError());1014 EXPECT_EQ("test", (*(*File)->getBuffer("ignored"))->getBuffer());1015 1016 std::error_code EC;1017 vfs::directory_iterator I = PFS.dir_begin("/", EC);1018 ASSERT_FALSE(EC);1019 ASSERT_EQ("/a", I->path());1020 I.increment(EC);1021 ASSERT_FALSE(EC);1022 ASSERT_EQ(vfs::directory_iterator(), I);1023 1024 ASSERT_FALSE(PFS.setCurrentWorkingDirectory("/"));1025 1026 auto PWD = PFS.getCurrentWorkingDirectory();1027 ASSERT_FALSE(PWD.getError());1028 ASSERT_EQ("/", getPosixPath(*PWD));1029 1030 SmallString<16> Path;1031 ASSERT_FALSE(PFS.getRealPath("a", Path));1032 ASSERT_EQ("/a", getPosixPath(Path));1033 1034 bool Local = true;1035 ASSERT_FALSE(PFS.isLocal("/a", Local));1036 EXPECT_FALSE(Local);1037}1038 1039class InMemoryFileSystemTest : public ::testing::Test {1040protected:1041 llvm::vfs::InMemoryFileSystem FS;1042 llvm::vfs::InMemoryFileSystem NormalizedFS;1043 1044 InMemoryFileSystemTest()1045 : FS(/*UseNormalizedPaths=*/false),1046 NormalizedFS(/*UseNormalizedPaths=*/true) {}1047};1048 1049MATCHER_P2(IsHardLinkTo, FS, Target, "") {1050 StringRef From = arg;1051 StringRef To = Target;1052 auto OpenedFrom = FS->openFileForRead(From);1053 auto OpenedTo = FS->openFileForRead(To);1054 return !OpenedFrom.getError() && !OpenedTo.getError() &&1055 (*OpenedFrom)->status()->getUniqueID() ==1056 (*OpenedTo)->status()->getUniqueID();1057}1058 1059TEST_F(InMemoryFileSystemTest, IsEmpty) {1060 auto Stat = FS.status("/a");1061 ASSERT_EQ(Stat.getError(), errc::no_such_file_or_directory) << FS.toString();1062 Stat = FS.status("/");1063 ASSERT_EQ(Stat.getError(), errc::no_such_file_or_directory) << FS.toString();1064}1065 1066TEST_F(InMemoryFileSystemTest, WindowsPath) {1067 FS.addFile("c:/windows/system128/foo.cpp", 0, MemoryBuffer::getMemBuffer(""));1068 auto Stat = FS.status("c:");1069#if !defined(_WIN32)1070 ASSERT_FALSE(Stat.getError()) << Stat.getError() << FS.toString();1071#endif1072 Stat = FS.status("c:/windows/system128/foo.cpp");1073 ASSERT_FALSE(Stat.getError()) << Stat.getError() << FS.toString();1074 FS.addFile("d:/windows/foo.cpp", 0, MemoryBuffer::getMemBuffer(""));1075 Stat = FS.status("d:/windows/foo.cpp");1076 ASSERT_FALSE(Stat.getError()) << Stat.getError() << FS.toString();1077}1078 1079TEST_F(InMemoryFileSystemTest, OverlayFile) {1080 FS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a"));1081 NormalizedFS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a"));1082 auto Stat = FS.status("/");1083 ASSERT_FALSE(Stat.getError()) << Stat.getError() << FS.toString();1084 Stat = FS.status("/.");1085 ASSERT_FALSE(Stat);1086 Stat = NormalizedFS.status("/.");1087 ASSERT_FALSE(Stat.getError()) << Stat.getError() << FS.toString();1088 Stat = FS.status("/a");1089 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();1090 ASSERT_EQ("/a", Stat->getName());1091}1092 1093TEST_F(InMemoryFileSystemTest, OverlayFileNoOwn) {1094 auto Buf = MemoryBuffer::getMemBuffer("a");1095 FS.addFileNoOwn("/a", 0, *Buf);1096 auto Stat = FS.status("/a");1097 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();1098 ASSERT_EQ("/a", Stat->getName());1099}1100 1101TEST_F(InMemoryFileSystemTest, OpenFileForRead) {1102 FS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a"));1103 FS.addFile("././c", 0, MemoryBuffer::getMemBuffer("c"));1104 FS.addFile("./d/../d", 0, MemoryBuffer::getMemBuffer("d"));1105 NormalizedFS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a"));1106 NormalizedFS.addFile("././c", 0, MemoryBuffer::getMemBuffer("c"));1107 NormalizedFS.addFile("./d/../d", 0, MemoryBuffer::getMemBuffer("d"));1108 auto File = FS.openFileForRead("/a");1109 ASSERT_EQ("a", (*(*File)->getBuffer("ignored"))->getBuffer());1110 File = FS.openFileForRead("/a"); // Open again.1111 ASSERT_EQ("a", (*(*File)->getBuffer("ignored"))->getBuffer());1112 File = NormalizedFS.openFileForRead("/././a"); // Open again.1113 ASSERT_EQ("a", (*(*File)->getBuffer("ignored"))->getBuffer());1114 File = FS.openFileForRead("/");1115 ASSERT_EQ(File.getError(), errc::invalid_argument) << FS.toString();1116 File = FS.openFileForRead("/b");1117 ASSERT_EQ(File.getError(), errc::no_such_file_or_directory) << FS.toString();1118 File = FS.openFileForRead("./c");1119 ASSERT_FALSE(File);1120 File = FS.openFileForRead("e/../d");1121 ASSERT_FALSE(File);1122 File = NormalizedFS.openFileForRead("./c");1123 ASSERT_EQ("c", (*(*File)->getBuffer("ignored"))->getBuffer());1124 File = NormalizedFS.openFileForRead("e/../d");1125 ASSERT_EQ("d", (*(*File)->getBuffer("ignored"))->getBuffer());1126}1127 1128TEST_F(InMemoryFileSystemTest, DuplicatedFile) {1129 ASSERT_TRUE(FS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a")));1130 ASSERT_FALSE(FS.addFile("/a/b", 0, MemoryBuffer::getMemBuffer("a")));1131 ASSERT_TRUE(FS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a")));1132 ASSERT_FALSE(FS.addFile("/a", 0, MemoryBuffer::getMemBuffer("b")));1133 ASSERT_TRUE(FS.addFile("/b/c/d", 0, MemoryBuffer::getMemBuffer("a")));1134 ASSERT_FALSE(FS.addFile("/b/c", 0, MemoryBuffer::getMemBuffer("a")));1135 ASSERT_TRUE(FS.addFile(1136 "/b/c", 0, MemoryBuffer::getMemBuffer(""), /*User=*/std::nullopt,1137 /*Group=*/std::nullopt, sys::fs::file_type::directory_file));1138}1139 1140TEST_F(InMemoryFileSystemTest, DirectoryIteration) {1141 FS.addFile("/a", 0, MemoryBuffer::getMemBuffer(""));1142 FS.addFile("/b/c", 0, MemoryBuffer::getMemBuffer(""));1143 1144 std::error_code EC;1145 vfs::directory_iterator I = FS.dir_begin("/", EC);1146 ASSERT_FALSE(EC);1147 ASSERT_EQ("/a", I->path());1148 I.increment(EC);1149 ASSERT_FALSE(EC);1150 ASSERT_EQ("/b", I->path());1151 I.increment(EC);1152 ASSERT_FALSE(EC);1153 ASSERT_EQ(vfs::directory_iterator(), I);1154 1155 I = FS.dir_begin("/b", EC);1156 ASSERT_FALSE(EC);1157 // When on Windows, we end up with "/b\\c" as the name. Convert to Posix1158 // path for the sake of the comparison.1159 ASSERT_EQ("/b/c", getPosixPath(std::string(I->path())));1160 I.increment(EC);1161 ASSERT_FALSE(EC);1162 ASSERT_EQ(vfs::directory_iterator(), I);1163}1164 1165TEST_F(InMemoryFileSystemTest, WorkingDirectory) {1166 FS.setCurrentWorkingDirectory("/b");1167 FS.addFile("c", 0, MemoryBuffer::getMemBuffer(""));1168 1169 auto Stat = FS.status("/b/c");1170 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();1171 ASSERT_EQ("/b/c", Stat->getName());1172 ASSERT_EQ("/b", *FS.getCurrentWorkingDirectory());1173 1174 Stat = FS.status("c");1175 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();1176 1177 NormalizedFS.setCurrentWorkingDirectory("/b/c");1178 NormalizedFS.setCurrentWorkingDirectory(".");1179 ASSERT_EQ("/b/c",1180 getPosixPath(NormalizedFS.getCurrentWorkingDirectory().get()));1181 NormalizedFS.setCurrentWorkingDirectory("..");1182 ASSERT_EQ("/b",1183 getPosixPath(NormalizedFS.getCurrentWorkingDirectory().get()));1184}1185 1186TEST_F(InMemoryFileSystemTest, IsLocal) {1187 FS.setCurrentWorkingDirectory("/b");1188 FS.addFile("c", 0, MemoryBuffer::getMemBuffer(""));1189 1190 std::error_code EC;1191 bool IsLocal = true;1192 EC = FS.isLocal("c", IsLocal);1193 ASSERT_FALSE(EC);1194 ASSERT_FALSE(IsLocal);1195}1196 1197#if !defined(_WIN32)1198TEST_F(InMemoryFileSystemTest, GetRealPath) {1199 SmallString<16> Path;1200 EXPECT_EQ(FS.getRealPath("b", Path), errc::operation_not_permitted);1201 1202 auto GetRealPath = [this](StringRef P) {1203 SmallString<16> Output;1204 auto EC = FS.getRealPath(P, Output);1205 EXPECT_FALSE(EC);1206 return std::string(Output);1207 };1208 1209 FS.setCurrentWorkingDirectory("a");1210 EXPECT_EQ(GetRealPath("b"), "a/b");1211 EXPECT_EQ(GetRealPath("../b"), "b");1212 EXPECT_EQ(GetRealPath("b/./c"), "a/b/c");1213 1214 FS.setCurrentWorkingDirectory("/a");1215 EXPECT_EQ(GetRealPath("b"), "/a/b");1216 EXPECT_EQ(GetRealPath("../b"), "/b");1217 EXPECT_EQ(GetRealPath("b/./c"), "/a/b/c");1218}1219#endif // _WIN321220 1221TEST_F(InMemoryFileSystemTest, AddFileWithUser) {1222 FS.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"), 0xFEEDFACE);1223 auto Stat = FS.status("/a");1224 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();1225 ASSERT_TRUE(Stat->isDirectory());1226 ASSERT_EQ(0xFEEDFACE, Stat->getUser());1227 Stat = FS.status("/a/b");1228 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();1229 ASSERT_TRUE(Stat->isDirectory());1230 ASSERT_EQ(0xFEEDFACE, Stat->getUser());1231 Stat = FS.status("/a/b/c");1232 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();1233 ASSERT_TRUE(Stat->isRegularFile());1234 ASSERT_EQ(sys::fs::perms::all_all, Stat->getPermissions());1235 ASSERT_EQ(0xFEEDFACE, Stat->getUser());1236}1237 1238TEST_F(InMemoryFileSystemTest, AddFileWithGroup) {1239 FS.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"), std::nullopt,1240 0xDABBAD00);1241 auto Stat = FS.status("/a");1242 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();1243 ASSERT_TRUE(Stat->isDirectory());1244 ASSERT_EQ(0xDABBAD00, Stat->getGroup());1245 Stat = FS.status("/a/b");1246 ASSERT_TRUE(Stat->isDirectory());1247 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();1248 ASSERT_EQ(0xDABBAD00, Stat->getGroup());1249 Stat = FS.status("/a/b/c");1250 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();1251 ASSERT_TRUE(Stat->isRegularFile());1252 ASSERT_EQ(sys::fs::perms::all_all, Stat->getPermissions());1253 ASSERT_EQ(0xDABBAD00, Stat->getGroup());1254}1255 1256TEST_F(InMemoryFileSystemTest, AddFileWithFileType) {1257 FS.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"), std::nullopt,1258 std::nullopt, sys::fs::file_type::socket_file);1259 auto Stat = FS.status("/a");1260 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();1261 ASSERT_TRUE(Stat->isDirectory());1262 Stat = FS.status("/a/b");1263 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();1264 ASSERT_TRUE(Stat->isDirectory());1265 Stat = FS.status("/a/b/c");1266 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();1267 ASSERT_EQ(sys::fs::file_type::socket_file, Stat->getType());1268 ASSERT_EQ(sys::fs::perms::all_all, Stat->getPermissions());1269}1270 1271TEST_F(InMemoryFileSystemTest, AddFileWithPerms) {1272 FS.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"), std::nullopt,1273 std::nullopt, std::nullopt,1274 sys::fs::perms::owner_read | sys::fs::perms::owner_write);1275 auto Stat = FS.status("/a");1276 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();1277 ASSERT_TRUE(Stat->isDirectory());1278 ASSERT_EQ(sys::fs::perms::owner_read | sys::fs::perms::owner_write |1279 sys::fs::perms::owner_exe,1280 Stat->getPermissions());1281 Stat = FS.status("/a/b");1282 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();1283 ASSERT_TRUE(Stat->isDirectory());1284 ASSERT_EQ(sys::fs::perms::owner_read | sys::fs::perms::owner_write |1285 sys::fs::perms::owner_exe,1286 Stat->getPermissions());1287 Stat = FS.status("/a/b/c");1288 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();1289 ASSERT_TRUE(Stat->isRegularFile());1290 ASSERT_EQ(sys::fs::perms::owner_read | sys::fs::perms::owner_write,1291 Stat->getPermissions());1292}1293 1294TEST_F(InMemoryFileSystemTest, AddDirectoryThenAddChild) {1295 FS.addFile("/a", 0, MemoryBuffer::getMemBuffer(""), /*User=*/std::nullopt,1296 /*Group=*/std::nullopt, sys::fs::file_type::directory_file);1297 FS.addFile("/a/b", 0, MemoryBuffer::getMemBuffer("abc"),1298 /*User=*/std::nullopt,1299 /*Group=*/std::nullopt, sys::fs::file_type::regular_file);1300 auto Stat = FS.status("/a");1301 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();1302 ASSERT_TRUE(Stat->isDirectory());1303 Stat = FS.status("/a/b");1304 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();1305 ASSERT_TRUE(Stat->isRegularFile());1306}1307 1308// Test that the name returned by status() is in the same form as the path that1309// was requested (to match the behavior of RealFileSystem).1310TEST_F(InMemoryFileSystemTest, StatusName) {1311 NormalizedFS.addFile("/a/b/c", 0, MemoryBuffer::getMemBuffer("abc"),1312 /*User=*/std::nullopt,1313 /*Group=*/std::nullopt,1314 sys::fs::file_type::regular_file);1315 NormalizedFS.setCurrentWorkingDirectory("/a/b");1316 1317 // Access using InMemoryFileSystem::status.1318 auto Stat = NormalizedFS.status("../b/c");1319 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n"1320 << NormalizedFS.toString();1321 ASSERT_TRUE(Stat->isRegularFile());1322 ASSERT_EQ("../b/c", Stat->getName());1323 1324 // Access using InMemoryFileAdaptor::status.1325 auto File = NormalizedFS.openFileForRead("../b/c");1326 ASSERT_FALSE(File.getError()) << File.getError() << "\n"1327 << NormalizedFS.toString();1328 Stat = (*File)->status();1329 ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n"1330 << NormalizedFS.toString();1331 ASSERT_TRUE(Stat->isRegularFile());1332 ASSERT_EQ("../b/c", Stat->getName());1333 1334 // Access using a directory iterator.1335 std::error_code EC;1336 llvm::vfs::directory_iterator It = NormalizedFS.dir_begin("../b", EC);1337 // When on Windows, we end up with "../b\\c" as the name. Convert to Posix1338 // path for the sake of the comparison.1339 ASSERT_EQ("../b/c", getPosixPath(std::string(It->path())));1340}1341 1342TEST_F(InMemoryFileSystemTest, AddHardLinkToFile) {1343 StringRef FromLink = "/path/to/FROM/link";1344 StringRef Target = "/path/to/TO/file";1345 FS.addFile(Target, 0, MemoryBuffer::getMemBuffer("content of target"));1346 EXPECT_TRUE(FS.addHardLink(FromLink, Target));1347 EXPECT_THAT(FromLink, IsHardLinkTo(&FS, Target));1348 EXPECT_EQ(FS.status(FromLink)->getSize(), FS.status(Target)->getSize());1349 EXPECT_EQ(FS.getBufferForFile(FromLink)->get()->getBuffer(),1350 FS.getBufferForFile(Target)->get()->getBuffer());1351}1352 1353TEST_F(InMemoryFileSystemTest, AddHardLinkInChainPattern) {1354 StringRef Link0 = "/path/to/0/link";1355 StringRef Link1 = "/path/to/1/link";1356 StringRef Link2 = "/path/to/2/link";1357 StringRef Target = "/path/to/target";1358 FS.addFile(Target, 0, MemoryBuffer::getMemBuffer("content of target file"));1359 EXPECT_TRUE(FS.addHardLink(Link2, Target));1360 EXPECT_TRUE(FS.addHardLink(Link1, Link2));1361 EXPECT_TRUE(FS.addHardLink(Link0, Link1));1362 EXPECT_THAT(Link0, IsHardLinkTo(&FS, Target));1363 EXPECT_THAT(Link1, IsHardLinkTo(&FS, Target));1364 EXPECT_THAT(Link2, IsHardLinkTo(&FS, Target));1365}1366 1367TEST_F(InMemoryFileSystemTest, AddHardLinkToAFileThatWasNotAddedBefore) {1368 EXPECT_FALSE(FS.addHardLink("/path/to/link", "/path/to/target"));1369}1370 1371TEST_F(InMemoryFileSystemTest, AddHardLinkFromAFileThatWasAddedBefore) {1372 StringRef Link = "/path/to/link";1373 StringRef Target = "/path/to/target";1374 FS.addFile(Target, 0, MemoryBuffer::getMemBuffer("content of target"));1375 FS.addFile(Link, 0, MemoryBuffer::getMemBuffer("content of link"));1376 EXPECT_FALSE(FS.addHardLink(Link, Target));1377}1378 1379TEST_F(InMemoryFileSystemTest, AddSameHardLinkMoreThanOnce) {1380 StringRef Link = "/path/to/link";1381 StringRef Target = "/path/to/target";1382 FS.addFile(Target, 0, MemoryBuffer::getMemBuffer("content of target"));1383 EXPECT_TRUE(FS.addHardLink(Link, Target));1384 EXPECT_FALSE(FS.addHardLink(Link, Target));1385}1386 1387TEST_F(InMemoryFileSystemTest, AddFileInPlaceOfAHardLinkWithSameContent) {1388 StringRef Link = "/path/to/link";1389 StringRef Target = "/path/to/target";1390 StringRef Content = "content of target";1391 EXPECT_TRUE(FS.addFile(Target, 0, MemoryBuffer::getMemBuffer(Content)));1392 EXPECT_TRUE(FS.addHardLink(Link, Target));1393 EXPECT_TRUE(FS.addFile(Link, 0, MemoryBuffer::getMemBuffer(Content)));1394}1395 1396TEST_F(InMemoryFileSystemTest, AddFileInPlaceOfAHardLinkWithDifferentContent) {1397 StringRef Link = "/path/to/link";1398 StringRef Target = "/path/to/target";1399 StringRef Content = "content of target";1400 StringRef LinkContent = "different content of link";1401 EXPECT_TRUE(FS.addFile(Target, 0, MemoryBuffer::getMemBuffer(Content)));1402 EXPECT_TRUE(FS.addHardLink(Link, Target));1403 EXPECT_FALSE(FS.addFile(Link, 0, MemoryBuffer::getMemBuffer(LinkContent)));1404}1405 1406TEST_F(InMemoryFileSystemTest, AddHardLinkToADirectory) {1407 StringRef Dir = "path/to/dummy/dir";1408 StringRef Link = "/path/to/link";1409 StringRef File = "path/to/dummy/dir/target";1410 StringRef Content = "content of target";1411 EXPECT_TRUE(FS.addFile(File, 0, MemoryBuffer::getMemBuffer(Content)));1412 EXPECT_FALSE(FS.addHardLink(Link, Dir));1413}1414 1415TEST_F(InMemoryFileSystemTest, AddHardLinkToASymlink) {1416 EXPECT_TRUE(FS.addFile("/file", 0, MemoryBuffer::getMemBuffer("content")));1417 EXPECT_TRUE(FS.addSymbolicLink("/symlink", "/file", 0));1418 EXPECT_TRUE(FS.addHardLink("/hardlink", "/symlink"));1419 EXPECT_EQ((*FS.getBufferForFile("/hardlink"))->getBuffer(), "content");1420}1421 1422TEST_F(InMemoryFileSystemTest, AddHardLinkFromADirectory) {1423 StringRef Dir = "path/to/dummy/dir";1424 StringRef Target = "path/to/dummy/dir/target";1425 StringRef Content = "content of target";1426 EXPECT_TRUE(FS.addFile(Target, 0, MemoryBuffer::getMemBuffer(Content)));1427 EXPECT_FALSE(FS.addHardLink(Dir, Target));1428}1429 1430TEST_F(InMemoryFileSystemTest, AddHardLinkUnderAFile) {1431 StringRef CommonContent = "content string";1432 FS.addFile("/a/b", 0, MemoryBuffer::getMemBuffer(CommonContent));1433 FS.addFile("/c/d", 0, MemoryBuffer::getMemBuffer(CommonContent));1434 EXPECT_FALSE(FS.addHardLink("/c/d/e", "/a/b"));1435}1436 1437TEST_F(InMemoryFileSystemTest, RecursiveIterationWithHardLink) {1438 std::error_code EC;1439 FS.addFile("/a/b", 0, MemoryBuffer::getMemBuffer("content string"));1440 EXPECT_TRUE(FS.addHardLink("/c/d", "/a/b"));1441 auto I = vfs::recursive_directory_iterator(FS, "/", EC);1442 ASSERT_FALSE(EC);1443 std::vector<std::string> Nodes;1444 for (auto E = vfs::recursive_directory_iterator(); !EC && I != E;1445 I.increment(EC)) {1446 Nodes.push_back(getPosixPath(std::string(I->path())));1447 }1448 EXPECT_THAT(Nodes, testing::UnorderedElementsAre("/a", "/a/b", "/c", "/c/d"));1449}1450 1451TEST_F(InMemoryFileSystemTest, UniqueID) {1452 ASSERT_TRUE(FS.addFile("/a/b", 0, MemoryBuffer::getMemBuffer("text")));1453 ASSERT_TRUE(FS.addFile("/c/d", 0, MemoryBuffer::getMemBuffer("text")));1454 ASSERT_TRUE(FS.addHardLink("/e/f", "/a/b"));1455 1456 EXPECT_EQ(FS.status("/a/b")->getUniqueID(), FS.status("/a/b")->getUniqueID());1457 EXPECT_NE(FS.status("/a/b")->getUniqueID(), FS.status("/c/d")->getUniqueID());1458 EXPECT_EQ(FS.status("/a/b")->getUniqueID(), FS.status("/e/f")->getUniqueID());1459 EXPECT_EQ(FS.status("/a")->getUniqueID(), FS.status("/a")->getUniqueID());1460 EXPECT_NE(FS.status("/a")->getUniqueID(), FS.status("/c")->getUniqueID());1461 EXPECT_NE(FS.status("/a")->getUniqueID(), FS.status("/e")->getUniqueID());1462 1463 // Recreating the "same" FS yields the same UniqueIDs.1464 // Note: FS2 should match FS with respect to path normalization.1465 vfs::InMemoryFileSystem FS2(/*UseNormalizedPath=*/false);1466 ASSERT_TRUE(FS2.addFile("/a/b", 0, MemoryBuffer::getMemBuffer("text")));1467 EXPECT_EQ(FS.status("/a/b")->getUniqueID(),1468 FS2.status("/a/b")->getUniqueID());1469 EXPECT_EQ(FS.status("/a")->getUniqueID(), FS2.status("/a")->getUniqueID());1470}1471 1472TEST_F(InMemoryFileSystemTest, AddSymlinkToAFile) {1473 EXPECT_TRUE(1474 FS.addFile("/some/file", 0, MemoryBuffer::getMemBuffer("contents")));1475 EXPECT_TRUE(FS.addSymbolicLink("/other/file/link", "/some/file", 0));1476 ErrorOr<vfs::Status> Stat = FS.status("/some/file");1477 EXPECT_TRUE(Stat->isRegularFile());1478}1479 1480TEST_F(InMemoryFileSystemTest, AddSymlinkToADirectory) {1481 EXPECT_TRUE(FS.addSymbolicLink("/link", "/target", 0));1482 EXPECT_TRUE(1483 FS.addFile("/target/foo.h", 0, MemoryBuffer::getMemBuffer("foo")));1484 ErrorOr<vfs::Status> Stat = FS.status("/link/foo.h");1485 EXPECT_TRUE(Stat);1486 EXPECT_EQ((*Stat).getName(), "/link/foo.h");1487 EXPECT_TRUE(Stat->isRegularFile());1488}1489 1490TEST_F(InMemoryFileSystemTest, AddSymlinkToASymlink) {1491 EXPECT_TRUE(FS.addSymbolicLink("/first", "/second", 0));1492 EXPECT_TRUE(FS.addSymbolicLink("/second", "/third", 0));1493 EXPECT_TRUE(FS.addFile("/third", 0, MemoryBuffer::getMemBuffer("")));1494 ErrorOr<vfs::Status> Stat = FS.status("/first");1495 EXPECT_TRUE(Stat);1496 EXPECT_EQ((*Stat).getName(), "/first");1497 // Follow-through symlinks by default. This matches RealFileSystem's1498 // semantics.1499 EXPECT_TRUE(Stat->isRegularFile());1500 Stat = FS.status("/second");1501 EXPECT_TRUE(Stat);1502 EXPECT_EQ((*Stat).getName(), "/second");1503 EXPECT_TRUE(Stat->isRegularFile());1504 Stat = FS.status("/third");1505 EXPECT_TRUE(Stat);1506 EXPECT_EQ((*Stat).getName(), "/third");1507 EXPECT_TRUE(Stat->isRegularFile());1508}1509 1510TEST_F(InMemoryFileSystemTest, AddRecursiveSymlink) {1511 EXPECT_TRUE(FS.addSymbolicLink("/link-a", "/link-b", 0));1512 EXPECT_TRUE(FS.addSymbolicLink("/link-b", "/link-a", 0));1513 ErrorOr<vfs::Status> Stat = FS.status("/link-a/foo");1514 EXPECT_FALSE(Stat);1515 EXPECT_EQ(Stat.getError(), errc::no_such_file_or_directory);1516}1517 1518TEST_F(InMemoryFileSystemTest, DirectoryIteratorWithSymlinkToAFile) {1519 std::error_code EC;1520 1521 EXPECT_TRUE(FS.addFile("/file", 0, MemoryBuffer::getMemBuffer("")));1522 EXPECT_TRUE(FS.addSymbolicLink("/symlink", "/file", 0));1523 1524 vfs::directory_iterator I = FS.dir_begin("/", EC), E;1525 ASSERT_FALSE(EC);1526 1527 std::vector<std::string> Nodes;1528 for (; !EC && I != E; I.increment(EC))1529 Nodes.push_back(getPosixPath(std::string(I->path())));1530 1531 EXPECT_THAT(Nodes, testing::UnorderedElementsAre("/file", "/file"));1532}1533 1534TEST_F(InMemoryFileSystemTest, RecursiveDirectoryIteratorWithSymlinkToADir) {1535 std::error_code EC;1536 1537 EXPECT_TRUE(FS.addFile("/dir/file", 0, MemoryBuffer::getMemBuffer("")));1538 EXPECT_TRUE(FS.addSymbolicLink("/dir_symlink", "/dir", 0));1539 1540 vfs::recursive_directory_iterator I(FS, "/", EC), E;1541 ASSERT_FALSE(EC);1542 1543 std::vector<std::string> Nodes;1544 for (; !EC && I != E; I.increment(EC))1545 Nodes.push_back(getPosixPath(std::string(I->path())));1546 1547 EXPECT_THAT(Nodes, testing::UnorderedElementsAre("/dir", "/dir/file", "/dir",1548 "/dir/file"));1549}1550 1551// NOTE: in the tests below, we use '//root/' as our root directory, since it is1552// a legal *absolute* path on Windows as well as *nix.1553class VFSFromYAMLTest : public ::testing::Test {1554public:1555 int NumDiagnostics;1556 1557 void SetUp() override { NumDiagnostics = 0; }1558 1559 static void CountingDiagHandler(const SMDiagnostic &, void *Context) {1560 VFSFromYAMLTest *Test = static_cast<VFSFromYAMLTest *>(Context);1561 ++Test->NumDiagnostics;1562 }1563 1564 std::unique_ptr<vfs::FileSystem>1565 getFromYAMLRawString(StringRef Content,1566 IntrusiveRefCntPtr<vfs::FileSystem> ExternalFS,1567 StringRef YAMLFilePath = "") {1568 std::unique_ptr<MemoryBuffer> Buffer = MemoryBuffer::getMemBuffer(Content);1569 return getVFSFromYAML(std::move(Buffer), CountingDiagHandler, YAMLFilePath,1570 this, ExternalFS);1571 }1572 1573 std::unique_ptr<vfs::FileSystem> getFromYAMLString(1574 StringRef Content,1575 IntrusiveRefCntPtr<vfs::FileSystem> ExternalFS = new DummyFileSystem(),1576 StringRef YAMLFilePath = "") {1577 std::string VersionPlusContent("{\n 'version':0,\n");1578 VersionPlusContent += Content.substr(Content.find('{') + 1);1579 return getFromYAMLRawString(VersionPlusContent, ExternalFS, YAMLFilePath);1580 }1581 1582 // This is intended as a "XFAIL" for windows hosts.1583 bool supportsSameDirMultipleYAMLEntries() {1584 Triple Host(Triple::normalize(sys::getProcessTriple()));1585 return !Host.isOSWindows();1586 }1587};1588 1589TEST_F(VFSFromYAMLTest, BasicVFSFromYAML) {1590 IntrusiveRefCntPtr<vfs::FileSystem> FS;1591 FS = getFromYAMLString("");1592 EXPECT_EQ(nullptr, FS.get());1593 FS = getFromYAMLString("[]");1594 EXPECT_EQ(nullptr, FS.get());1595 FS = getFromYAMLString("'string'");1596 EXPECT_EQ(nullptr, FS.get());1597 EXPECT_EQ(3, NumDiagnostics);1598}1599 1600TEST_F(VFSFromYAMLTest, MappedFiles) {1601 auto Lower = makeIntrusiveRefCnt<DummyFileSystem>();1602 Lower->addDirectory("//root/foo/bar");1603 Lower->addRegularFile("//root/foo/bar/a");1604 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(1605 "{ 'roots': [\n"1606 "{\n"1607 " 'type': 'directory',\n"1608 " 'name': '//root/',\n"1609 " 'contents': [ {\n"1610 " 'type': 'file',\n"1611 " 'name': 'file1',\n"1612 " 'external-contents': '//root/foo/bar/a'\n"1613 " },\n"1614 " {\n"1615 " 'type': 'file',\n"1616 " 'name': 'file2',\n"1617 " 'external-contents': '//root/foo/b'\n"1618 " },\n"1619 " {\n"1620 " 'type': 'directory-remap',\n"1621 " 'name': 'mappeddir',\n"1622 " 'external-contents': '//root/foo/bar'\n"1623 " },\n"1624 " {\n"1625 " 'type': 'directory-remap',\n"1626 " 'name': 'mappeddir2',\n"1627 " 'use-external-name': false,\n"1628 " 'external-contents': '//root/foo/bar'\n"1629 " }\n"1630 " ]\n"1631 "}\n"1632 "]\n"1633 "}",1634 Lower);1635 ASSERT_NE(FS.get(), nullptr);1636 1637 auto O = makeIntrusiveRefCnt<vfs::OverlayFileSystem>(Lower);1638 O->pushOverlay(FS);1639 1640 // file1641 ErrorOr<vfs::Status> S = O->status("//root/file1");1642 ASSERT_FALSE(S.getError());1643 EXPECT_EQ("//root/foo/bar/a", S->getName());1644 EXPECT_TRUE(S->ExposesExternalVFSPath);1645 1646 ErrorOr<vfs::Status> SLower = O->status("//root/foo/bar/a");1647 EXPECT_EQ("//root/foo/bar/a", SLower->getName());1648 EXPECT_TRUE(S->equivalent(*SLower));1649 EXPECT_FALSE(SLower->ExposesExternalVFSPath);1650 1651 // file after opening1652 auto OpenedF = O->openFileForRead("//root/file1");1653 ASSERT_FALSE(OpenedF.getError());1654 auto OpenedS = (*OpenedF)->status();1655 ASSERT_FALSE(OpenedS.getError());1656 EXPECT_EQ("//root/foo/bar/a", OpenedS->getName());1657 EXPECT_TRUE(OpenedS->ExposesExternalVFSPath);1658 1659 // directory1660 S = O->status("//root/");1661 ASSERT_FALSE(S.getError());1662 EXPECT_TRUE(S->isDirectory());1663 EXPECT_TRUE(S->equivalent(*O->status("//root/"))); // non-volatile UniqueID1664 1665 // remapped directory1666 S = O->status("//root/mappeddir");1667 ASSERT_FALSE(S.getError());1668 EXPECT_TRUE(S->isDirectory());1669 EXPECT_TRUE(S->ExposesExternalVFSPath);1670 EXPECT_TRUE(S->equivalent(*O->status("//root/foo/bar")));1671 1672 SLower = O->status("//root/foo/bar");1673 EXPECT_EQ("//root/foo/bar", SLower->getName());1674 EXPECT_TRUE(S->equivalent(*SLower));1675 EXPECT_FALSE(SLower->ExposesExternalVFSPath);1676 1677 // file in remapped directory1678 S = O->status("//root/mappeddir/a");1679 ASSERT_FALSE(S.getError());1680 EXPECT_FALSE(S->isDirectory());1681 EXPECT_TRUE(S->ExposesExternalVFSPath);1682 EXPECT_EQ("//root/foo/bar/a", S->getName());1683 1684 // file in remapped directory, with use-external-name=false1685 S = O->status("//root/mappeddir2/a");1686 ASSERT_FALSE(S.getError());1687 EXPECT_FALSE(S->isDirectory());1688 EXPECT_FALSE(S->ExposesExternalVFSPath);1689 EXPECT_EQ("//root/mappeddir2/a", S->getName());1690 1691 // file contents in remapped directory1692 OpenedF = O->openFileForRead("//root/mappeddir/a");1693 ASSERT_FALSE(OpenedF.getError());1694 OpenedS = (*OpenedF)->status();1695 ASSERT_FALSE(OpenedS.getError());1696 EXPECT_EQ("//root/foo/bar/a", OpenedS->getName());1697 EXPECT_TRUE(OpenedS->ExposesExternalVFSPath);1698 1699 // file contents in remapped directory, with use-external-name=false1700 OpenedF = O->openFileForRead("//root/mappeddir2/a");1701 ASSERT_FALSE(OpenedF.getError());1702 OpenedS = (*OpenedF)->status();1703 ASSERT_FALSE(OpenedS.getError());1704 EXPECT_EQ("//root/mappeddir2/a", OpenedS->getName());1705 EXPECT_FALSE(OpenedS->ExposesExternalVFSPath);1706 1707 // broken mapping1708 EXPECT_EQ(O->status("//root/file2").getError(),1709 llvm::errc::no_such_file_or_directory);1710 EXPECT_EQ(0, NumDiagnostics);1711}1712 1713TEST_F(VFSFromYAMLTest, MappedRoot) {1714 auto Lower = makeIntrusiveRefCnt<DummyFileSystem>();1715 Lower->addDirectory("//root/foo/bar");1716 Lower->addRegularFile("//root/foo/bar/a");1717 IntrusiveRefCntPtr<vfs::FileSystem> FS =1718 getFromYAMLString("{ 'roots': [\n"1719 "{\n"1720 " 'type': 'directory-remap',\n"1721 " 'name': '//mappedroot/',\n"1722 " 'external-contents': '//root/foo/bar'\n"1723 "}\n"1724 "]\n"1725 "}",1726 Lower);1727 ASSERT_NE(FS.get(), nullptr);1728 1729 auto O = makeIntrusiveRefCnt<vfs::OverlayFileSystem>(Lower);1730 O->pushOverlay(FS);1731 1732 // file1733 ErrorOr<vfs::Status> S = O->status("//mappedroot/a");1734 ASSERT_FALSE(S.getError());1735 EXPECT_EQ("//root/foo/bar/a", S->getName());1736 EXPECT_TRUE(S->ExposesExternalVFSPath);1737 1738 ErrorOr<vfs::Status> SLower = O->status("//root/foo/bar/a");1739 EXPECT_EQ("//root/foo/bar/a", SLower->getName());1740 EXPECT_TRUE(S->equivalent(*SLower));1741 EXPECT_FALSE(SLower->ExposesExternalVFSPath);1742 1743 // file after opening1744 auto OpenedF = O->openFileForRead("//mappedroot/a");1745 ASSERT_FALSE(OpenedF.getError());1746 auto OpenedS = (*OpenedF)->status();1747 ASSERT_FALSE(OpenedS.getError());1748 EXPECT_EQ("//root/foo/bar/a", OpenedS->getName());1749 EXPECT_TRUE(OpenedS->ExposesExternalVFSPath);1750 1751 EXPECT_EQ(0, NumDiagnostics);1752}1753 1754TEST_F(VFSFromYAMLTest, RemappedDirectoryOverlay) {1755 auto Lower = makeIntrusiveRefCnt<DummyFileSystem>();1756 Lower->addDirectory("//root/foo");1757 Lower->addRegularFile("//root/foo/a");1758 Lower->addDirectory("//root/bar");1759 Lower->addRegularFile("//root/bar/b");1760 Lower->addRegularFile("//root/bar/c");1761 IntrusiveRefCntPtr<vfs::FileSystem> FS =1762 getFromYAMLString("{ 'roots': [\n"1763 "{\n"1764 " 'type': 'directory',\n"1765 " 'name': '//root/',\n"1766 " 'contents': [ {\n"1767 " 'type': 'directory-remap',\n"1768 " 'name': 'bar',\n"1769 " 'external-contents': '//root/foo'\n"1770 " }\n"1771 " ]\n"1772 "}]}",1773 Lower);1774 ASSERT_NE(FS.get(), nullptr);1775 1776 auto O = makeIntrusiveRefCnt<vfs::OverlayFileSystem>(Lower);1777 O->pushOverlay(FS);1778 1779 ErrorOr<vfs::Status> S = O->status("//root/foo");1780 ASSERT_FALSE(S.getError());1781 1782 ErrorOr<vfs::Status> SS = O->status("//root/bar");1783 ASSERT_FALSE(SS.getError());1784 EXPECT_TRUE(S->equivalent(*SS));1785 1786 std::error_code EC;1787 checkContents(O->dir_begin("//root/bar", EC),1788 {"//root/foo/a", "//root/bar/b", "//root/bar/c"});1789 1790 Lower->addRegularFile("//root/foo/b");1791 checkContents(O->dir_begin("//root/bar", EC),1792 {"//root/foo/a", "//root/foo/b", "//root/bar/c"});1793 1794 EXPECT_EQ(0, NumDiagnostics);1795}1796 1797TEST_F(VFSFromYAMLTest, RemappedDirectoryOverlayNoExternalNames) {1798 auto Lower = makeIntrusiveRefCnt<DummyFileSystem>();1799 Lower->addDirectory("//root/foo");1800 Lower->addRegularFile("//root/foo/a");1801 Lower->addDirectory("//root/bar");1802 Lower->addRegularFile("//root/bar/b");1803 Lower->addRegularFile("//root/bar/c");1804 IntrusiveRefCntPtr<vfs::FileSystem> FS =1805 getFromYAMLString("{ 'use-external-names': false,\n"1806 " 'roots': [\n"1807 "{\n"1808 " 'type': 'directory',\n"1809 " 'name': '//root/',\n"1810 " 'contents': [ {\n"1811 " 'type': 'directory-remap',\n"1812 " 'name': 'bar',\n"1813 " 'external-contents': '//root/foo'\n"1814 " }\n"1815 " ]\n"1816 "}]}",1817 Lower);1818 ASSERT_NE(FS.get(), nullptr);1819 1820 ErrorOr<vfs::Status> S = FS->status("//root/foo");1821 ASSERT_FALSE(S.getError());1822 1823 ErrorOr<vfs::Status> SS = FS->status("//root/bar");1824 ASSERT_FALSE(SS.getError());1825 EXPECT_TRUE(S->equivalent(*SS));1826 1827 std::error_code EC;1828 checkContents(FS->dir_begin("//root/bar", EC),1829 {"//root/bar/a", "//root/bar/b", "//root/bar/c"});1830 1831 Lower->addRegularFile("//root/foo/b");1832 checkContents(FS->dir_begin("//root/bar", EC),1833 {"//root/bar/a", "//root/bar/b", "//root/bar/c"});1834 1835 EXPECT_EQ(0, NumDiagnostics);1836}1837 1838TEST_F(VFSFromYAMLTest, RemappedDirectoryOverlayNoFallthrough) {1839 auto Lower = makeIntrusiveRefCnt<DummyFileSystem>();1840 Lower->addDirectory("//root/foo");1841 Lower->addRegularFile("//root/foo/a");1842 Lower->addDirectory("//root/bar");1843 Lower->addRegularFile("//root/bar/b");1844 Lower->addRegularFile("//root/bar/c");1845 IntrusiveRefCntPtr<vfs::FileSystem> FS =1846 getFromYAMLString("{ 'fallthrough': false,\n"1847 " 'roots': [\n"1848 "{\n"1849 " 'type': 'directory',\n"1850 " 'name': '//root/',\n"1851 " 'contents': [ {\n"1852 " 'type': 'directory-remap',\n"1853 " 'name': 'bar',\n"1854 " 'external-contents': '//root/foo'\n"1855 " }\n"1856 " ]\n"1857 "}]}",1858 Lower);1859 ASSERT_NE(FS.get(), nullptr);1860 1861 ErrorOr<vfs::Status> S = Lower->status("//root/foo");1862 ASSERT_FALSE(S.getError());1863 1864 ErrorOr<vfs::Status> SS = FS->status("//root/bar");1865 ASSERT_FALSE(SS.getError());1866 EXPECT_TRUE(S->equivalent(*SS));1867 1868 std::error_code EC;1869 checkContents(FS->dir_begin("//root/bar", EC), {"//root/foo/a"});1870 1871 Lower->addRegularFile("//root/foo/b");1872 checkContents(FS->dir_begin("//root/bar", EC),1873 {"//root/foo/a", "//root/foo/b"});1874 1875 EXPECT_EQ(0, NumDiagnostics);1876}1877 1878TEST_F(VFSFromYAMLTest, ReturnsRequestedPathVFSMiss) {1879 auto BaseFS = makeIntrusiveRefCnt<vfs::InMemoryFileSystem>();1880 BaseFS->addFile("//root/foo/a", 0,1881 MemoryBuffer::getMemBuffer("contents of a"));1882 ASSERT_FALSE(BaseFS->setCurrentWorkingDirectory("//root/foo"));1883 auto RemappedFS = vfs::RedirectingFileSystem::create(1884 {}, /*UseExternalNames=*/false, BaseFS);1885 1886 auto OpenedF = RemappedFS->openFileForRead("a");1887 ASSERT_FALSE(OpenedF.getError());1888 llvm::ErrorOr<std::string> Name = (*OpenedF)->getName();1889 ASSERT_FALSE(Name.getError());1890 EXPECT_EQ("a", Name.get());1891 1892 auto OpenedS = (*OpenedF)->status();1893 ASSERT_FALSE(OpenedS.getError());1894 EXPECT_EQ("a", OpenedS->getName());1895 EXPECT_FALSE(OpenedS->ExposesExternalVFSPath);1896 1897 auto DirectS = RemappedFS->status("a");1898 ASSERT_FALSE(DirectS.getError());1899 EXPECT_EQ("a", DirectS->getName());1900 EXPECT_FALSE(DirectS->ExposesExternalVFSPath);1901 1902 EXPECT_EQ(0, NumDiagnostics);1903}1904 1905TEST_F(VFSFromYAMLTest, ReturnsExternalPathVFSHit) {1906 auto BaseFS = makeIntrusiveRefCnt<vfs::InMemoryFileSystem>();1907 BaseFS->addFile("//root/foo/realname", 0,1908 MemoryBuffer::getMemBuffer("contents of a"));1909 auto FS =1910 getFromYAMLString("{ 'use-external-names': true,\n"1911 " 'roots': [\n"1912 "{\n"1913 " 'type': 'directory',\n"1914 " 'name': '//root/foo',\n"1915 " 'contents': [ {\n"1916 " 'type': 'file',\n"1917 " 'name': 'vfsname',\n"1918 " 'external-contents': 'realname'\n"1919 " }\n"1920 " ]\n"1921 "}]}",1922 BaseFS);1923 ASSERT_FALSE(FS->setCurrentWorkingDirectory("//root/foo"));1924 1925 auto OpenedF = FS->openFileForRead("vfsname");1926 ASSERT_FALSE(OpenedF.getError());1927 llvm::ErrorOr<std::string> Name = (*OpenedF)->getName();1928 ASSERT_FALSE(Name.getError());1929 EXPECT_EQ("realname", Name.get());1930 1931 auto OpenedS = (*OpenedF)->status();1932 ASSERT_FALSE(OpenedS.getError());1933 EXPECT_EQ("realname", OpenedS->getName());1934 EXPECT_TRUE(OpenedS->ExposesExternalVFSPath);1935 1936 auto DirectS = FS->status("vfsname");1937 ASSERT_FALSE(DirectS.getError());1938 EXPECT_EQ("realname", DirectS->getName());1939 EXPECT_TRUE(DirectS->ExposesExternalVFSPath);1940 1941 EXPECT_EQ(0, NumDiagnostics);1942}1943 1944TEST_F(VFSFromYAMLTest, RelativeFileDirWithOverlayRelativeSetting) {1945 auto Lower = makeIntrusiveRefCnt<DummyFileSystem>();1946 Lower->addDirectory("//root/foo/bar");1947 Lower->addRegularFile("//root/foo/bar/a");1948 Lower->setCurrentWorkingDirectory("//root/foo");1949 IntrusiveRefCntPtr<vfs::FileSystem> FS =1950 getFromYAMLString("{\n"1951 " 'case-sensitive': false,\n"1952 " 'overlay-relative': true,\n"1953 " 'roots': [\n"1954 " { 'name': '//root/foo/bar/b', 'type': 'file',\n"1955 " 'external-contents': 'a'\n"1956 " }\n"1957 " ]\n"1958 "}",1959 Lower, "bar/overlay");1960 1961 ASSERT_NE(FS.get(), nullptr);1962 ErrorOr<vfs::Status> S = FS->status("//root/foo/bar/b");1963 ASSERT_FALSE(S.getError());1964 EXPECT_EQ("//root/foo/bar/a", S->getName());1965}1966 1967TEST_F(VFSFromYAMLTest, RootRelativeToOverlayDirTest) {1968 auto Lower = makeIntrusiveRefCnt<DummyFileSystem>();1969 Lower->addDirectory("//root/foo/bar");1970 Lower->addRegularFile("//root/foo/bar/a");1971 IntrusiveRefCntPtr<vfs::FileSystem> FS =1972 getFromYAMLString("{\n"1973 " 'case-sensitive': false,\n"1974 " 'root-relative': 'overlay-dir',\n"1975 " 'roots': [\n"1976 " { 'name': 'b', 'type': 'file',\n"1977 " 'external-contents': '//root/foo/bar/a'\n"1978 " }\n"1979 " ]\n"1980 "}",1981 Lower, "//root/foo/bar/overlay");1982 1983 ASSERT_NE(FS.get(), nullptr);1984 ErrorOr<vfs::Status> S = FS->status("//root/foo/bar/b");1985 ASSERT_FALSE(S.getError());1986 EXPECT_EQ("//root/foo/bar/a", S->getName());1987 1988 // On Windows, with overlay-relative set to true, the relative1989 // path in external-contents field will be prepend by OverlayDir1990 // with native path separator, regardless of the actual path separator1991 // used in YAMLFilePath field.1992#ifndef _WIN321993 FS = getFromYAMLString("{\n"1994 " 'case-sensitive': false,\n"1995 " 'overlay-relative': true,\n"1996 " 'root-relative': 'overlay-dir',\n"1997 " 'roots': [\n"1998 " { 'name': 'b', 'type': 'file',\n"1999 " 'external-contents': 'a'\n"2000 " }\n"2001 " ]\n"2002 "}",2003 Lower, "//root/foo/bar/overlay");2004 ASSERT_NE(FS.get(), nullptr);2005 S = FS->status("//root/foo/bar/b");2006 ASSERT_FALSE(S.getError());2007 EXPECT_EQ("//root/foo/bar/a", S->getName());2008#else2009 auto LowerWindows = makeIntrusiveRefCnt<DummyFileSystem>();2010 LowerWindows->addDirectory("\\\\root\\foo\\bar");2011 LowerWindows->addRegularFile("\\\\root\\foo\\bar\\a");2012 FS = getFromYAMLString("{\n"2013 " 'case-sensitive': false,\n"2014 " 'overlay-relative': true,\n"2015 " 'root-relative': 'overlay-dir',\n"2016 " 'roots': [\n"2017 " { 'name': 'b', 'type': 'file',\n"2018 " 'external-contents': 'a'\n"2019 " }\n"2020 " ]\n"2021 "}",2022 LowerWindows, "\\\\root\\foo\\bar\\overlay");2023 ASSERT_NE(FS.get(), nullptr);2024 S = FS->status("\\\\root\\foo\\bar\\b");2025 ASSERT_FALSE(S.getError());2026 EXPECT_EQ("\\\\root\\foo\\bar\\a", S->getName());2027#endif2028}2029 2030TEST_F(VFSFromYAMLTest, RootRelativeToCWDTest) {2031 auto Lower = makeIntrusiveRefCnt<DummyFileSystem>();2032 Lower->addDirectory("//root/foo/bar");2033 Lower->addRegularFile("//root/foo/bar/a");2034 Lower->addDirectory("//root/foo/bar/cwd");2035 Lower->addRegularFile("//root/foo/bar/cwd/a");2036 Lower->setCurrentWorkingDirectory("//root/foo/bar/cwd");2037 IntrusiveRefCntPtr<vfs::FileSystem> FS =2038 getFromYAMLString("{\n"2039 " 'case-sensitive': false,\n"2040 " 'root-relative': 'cwd',\n"2041 " 'roots': [\n"2042 " { 'name': 'b', 'type': 'file',\n"2043 " 'external-contents': '//root/foo/bar/a'\n"2044 " }\n"2045 " ]\n"2046 "}",2047 Lower, "//root/foo/bar/overlay");2048 2049 ASSERT_NE(FS.get(), nullptr);2050 2051 ErrorOr<vfs::Status> S1 = FS->status("//root/foo/bar/b");2052 ASSERT_TRUE(S1.getError());2053 2054 ErrorOr<vfs::Status> S2 = FS->status("//root/foo/bar/cwd/b");2055 ASSERT_FALSE(S2.getError());2056 EXPECT_EQ("//root/foo/bar/a", S2->getName());2057}2058 2059TEST_F(VFSFromYAMLTest, ReturnsInternalPathVFSHit) {2060 auto BaseFS = makeIntrusiveRefCnt<vfs::InMemoryFileSystem>();2061 BaseFS->addFile("//root/foo/realname", 0,2062 MemoryBuffer::getMemBuffer("contents of a"));2063 auto FS =2064 getFromYAMLString("{ 'use-external-names': false,\n"2065 " 'roots': [\n"2066 "{\n"2067 " 'type': 'directory',\n"2068 " 'name': '//root/foo',\n"2069 " 'contents': [ {\n"2070 " 'type': 'file',\n"2071 " 'name': 'vfsname',\n"2072 " 'external-contents': 'realname'\n"2073 " }\n"2074 " ]\n"2075 "}]}",2076 BaseFS);2077 ASSERT_FALSE(FS->setCurrentWorkingDirectory("//root/foo"));2078 2079 auto OpenedF = FS->openFileForRead("vfsname");2080 ASSERT_FALSE(OpenedF.getError());2081 llvm::ErrorOr<std::string> Name = (*OpenedF)->getName();2082 ASSERT_FALSE(Name.getError());2083 EXPECT_EQ("vfsname", Name.get());2084 2085 auto OpenedS = (*OpenedF)->status();2086 ASSERT_FALSE(OpenedS.getError());2087 EXPECT_EQ("vfsname", OpenedS->getName());2088 EXPECT_FALSE(OpenedS->ExposesExternalVFSPath);2089 2090 auto DirectS = FS->status("vfsname");2091 ASSERT_FALSE(DirectS.getError());2092 EXPECT_EQ("vfsname", DirectS->getName());2093 EXPECT_FALSE(DirectS->ExposesExternalVFSPath);2094 2095 EXPECT_EQ(0, NumDiagnostics);2096}2097 2098TEST_F(VFSFromYAMLTest, CaseInsensitive) {2099 auto Lower = makeIntrusiveRefCnt<DummyFileSystem>();2100 Lower->addRegularFile("//root/foo/bar/a");2101 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(2102 "{ 'case-sensitive': 'false',\n"2103 " 'roots': [\n"2104 "{\n"2105 " 'type': 'directory',\n"2106 " 'name': '//root/',\n"2107 " 'contents': [ {\n"2108 " 'type': 'file',\n"2109 " 'name': 'XX',\n"2110 " 'external-contents': '//root/foo/bar/a'\n"2111 " }\n"2112 " ]\n"2113 "}]}",2114 Lower);2115 ASSERT_NE(FS.get(), nullptr);2116 2117 auto O = makeIntrusiveRefCnt<vfs::OverlayFileSystem>(Lower);2118 O->pushOverlay(FS);2119 2120 ErrorOr<vfs::Status> S = O->status("//root/XX");2121 ASSERT_FALSE(S.getError());2122 2123 ErrorOr<vfs::Status> SS = O->status("//root/xx");2124 ASSERT_FALSE(SS.getError());2125 EXPECT_TRUE(S->equivalent(*SS));2126 SS = O->status("//root/xX");2127 EXPECT_TRUE(S->equivalent(*SS));2128 SS = O->status("//root/Xx");2129 EXPECT_TRUE(S->equivalent(*SS));2130 EXPECT_EQ(0, NumDiagnostics);2131}2132 2133TEST_F(VFSFromYAMLTest, CaseSensitive) {2134 auto Lower = makeIntrusiveRefCnt<DummyFileSystem>();2135 Lower->addRegularFile("//root/foo/bar/a");2136 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(2137 "{ 'case-sensitive': 'true',\n"2138 " 'roots': [\n"2139 "{\n"2140 " 'type': 'directory',\n"2141 " 'name': '//root/',\n"2142 " 'contents': [ {\n"2143 " 'type': 'file',\n"2144 " 'name': 'XX',\n"2145 " 'external-contents': '//root/foo/bar/a'\n"2146 " }\n"2147 " ]\n"2148 "}]}",2149 Lower);2150 ASSERT_NE(FS.get(), nullptr);2151 2152 auto O = makeIntrusiveRefCnt<vfs::OverlayFileSystem>(Lower);2153 O->pushOverlay(FS);2154 2155 ErrorOr<vfs::Status> SS = O->status("//root/xx");2156 EXPECT_EQ(SS.getError(), llvm::errc::no_such_file_or_directory);2157 SS = O->status("//root/xX");2158 EXPECT_EQ(SS.getError(), llvm::errc::no_such_file_or_directory);2159 SS = O->status("//root/Xx");2160 EXPECT_EQ(SS.getError(), llvm::errc::no_such_file_or_directory);2161 EXPECT_EQ(0, NumDiagnostics);2162}2163 2164TEST_F(VFSFromYAMLTest, IllegalVFSFile) {2165 auto Lower = makeIntrusiveRefCnt<DummyFileSystem>();2166 2167 // invalid YAML at top-level2168 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString("{]", Lower);2169 EXPECT_EQ(nullptr, FS.get());2170 // invalid YAML in roots2171 FS = getFromYAMLString("{ 'roots':[}", Lower);2172 // invalid YAML in directory2173 FS = getFromYAMLString(2174 "{ 'roots':[ { 'name': 'foo', 'type': 'directory', 'contents': [}",2175 Lower);2176 EXPECT_EQ(nullptr, FS.get());2177 2178 // invalid configuration2179 FS = getFromYAMLString("{ 'knobular': 'true', 'roots':[] }", Lower);2180 EXPECT_EQ(nullptr, FS.get());2181 FS = getFromYAMLString("{ 'case-sensitive': 'maybe', 'roots':[] }", Lower);2182 EXPECT_EQ(nullptr, FS.get());2183 2184 // invalid roots2185 FS = getFromYAMLString("{ 'roots':'' }", Lower);2186 EXPECT_EQ(nullptr, FS.get());2187 FS = getFromYAMLString("{ 'roots':{} }", Lower);2188 EXPECT_EQ(nullptr, FS.get());2189 2190 // invalid entries2191 FS = getFromYAMLString(2192 "{ 'roots':[ { 'type': 'other', 'name': 'me', 'contents': '' }", Lower);2193 EXPECT_EQ(nullptr, FS.get());2194 FS = getFromYAMLString("{ 'roots':[ { 'type': 'file', 'name': [], "2195 "'external-contents': 'other' }",2196 Lower);2197 EXPECT_EQ(nullptr, FS.get());2198 FS = getFromYAMLString(2199 "{ 'roots':[ { 'type': 'file', 'name': 'me', 'external-contents': [] }",2200 Lower);2201 EXPECT_EQ(nullptr, FS.get());2202 FS = getFromYAMLString(2203 "{ 'roots':[ { 'type': 'file', 'name': 'me', 'external-contents': {} }",2204 Lower);2205 EXPECT_EQ(nullptr, FS.get());2206 FS = getFromYAMLString(2207 "{ 'roots':[ { 'type': 'directory', 'name': 'me', 'contents': {} }",2208 Lower);2209 EXPECT_EQ(nullptr, FS.get());2210 FS = getFromYAMLString(2211 "{ 'roots':[ { 'type': 'directory', 'name': 'me', 'contents': '' }",2212 Lower);2213 EXPECT_EQ(nullptr, FS.get());2214 FS = getFromYAMLString(2215 "{ 'roots':[ { 'thingy': 'directory', 'name': 'me', 'contents': [] }",2216 Lower);2217 EXPECT_EQ(nullptr, FS.get());2218 2219 // missing mandatory fields2220 FS = getFromYAMLString("{ 'roots':[ { 'type': 'file', 'name': 'me' }", Lower);2221 EXPECT_EQ(nullptr, FS.get());2222 FS = getFromYAMLString(2223 "{ 'roots':[ { 'type': 'file', 'external-contents': 'other' }", Lower);2224 EXPECT_EQ(nullptr, FS.get());2225 FS = getFromYAMLString("{ 'roots':[ { 'name': 'me', 'contents': [] }", Lower);2226 EXPECT_EQ(nullptr, FS.get());2227 2228 // duplicate keys2229 FS = getFromYAMLString("{ 'roots':[], 'roots':[] }", Lower);2230 EXPECT_EQ(nullptr, FS.get());2231 FS = getFromYAMLString(2232 "{ 'case-sensitive':'true', 'case-sensitive':'true', 'roots':[] }",2233 Lower);2234 EXPECT_EQ(nullptr, FS.get());2235 FS =2236 getFromYAMLString("{ 'roots':[{'name':'me', 'name':'you', 'type':'file', "2237 "'external-contents':'blah' } ] }",2238 Lower);2239 EXPECT_EQ(nullptr, FS.get());2240 2241 // missing version2242 FS = getFromYAMLRawString("{ 'roots':[] }", Lower);2243 EXPECT_EQ(nullptr, FS.get());2244 2245 // bad version number2246 FS = getFromYAMLRawString("{ 'version':'foo', 'roots':[] }", Lower);2247 EXPECT_EQ(nullptr, FS.get());2248 FS = getFromYAMLRawString("{ 'version':-1, 'roots':[] }", Lower);2249 EXPECT_EQ(nullptr, FS.get());2250 FS = getFromYAMLRawString("{ 'version':100000, 'roots':[] }", Lower);2251 EXPECT_EQ(nullptr, FS.get());2252 2253 // both 'external-contents' and 'contents' specified2254 Lower->addDirectory("//root/external/dir");2255 FS = getFromYAMLString(2256 "{ 'roots':[ \n"2257 "{ 'type': 'directory', 'name': '//root/A', 'contents': [],\n"2258 " 'external-contents': '//root/external/dir'}]}",2259 Lower);2260 EXPECT_EQ(nullptr, FS.get());2261 2262 // 'directory-remap' with 'contents'2263 FS = getFromYAMLString(2264 "{ 'roots':[ \n"2265 "{ 'type': 'directory-remap', 'name': '//root/A', 'contents': [] }]}",2266 Lower);2267 EXPECT_EQ(nullptr, FS.get());2268 2269 // invalid redirect kind2270 FS = getFromYAMLString("{ 'redirecting-with': 'none', 'roots': [{\n"2271 " 'type': 'directory-remap',\n"2272 " 'name': '//root/A',\n"2273 " 'external-contents': '//root/B' }]}",2274 Lower);2275 EXPECT_EQ(nullptr, FS.get());2276 2277 // redirect and fallthrough passed2278 FS = getFromYAMLString("{ 'redirecting-with': 'fallthrough',\n"2279 " 'fallthrough': true,\n"2280 " 'roots': [{\n"2281 " 'type': 'directory-remap',\n"2282 " 'name': '//root/A',\n"2283 " 'external-contents': '//root/B' }]}",2284 Lower);2285 EXPECT_EQ(nullptr, FS.get());2286 2287 EXPECT_EQ(28, NumDiagnostics);2288}2289 2290TEST_F(VFSFromYAMLTest, UseExternalName) {2291 auto Lower = makeIntrusiveRefCnt<DummyFileSystem>();2292 Lower->addRegularFile("//root/external/file");2293 2294 IntrusiveRefCntPtr<vfs::FileSystem> FS =2295 getFromYAMLString("{ 'roots': [\n"2296 " { 'type': 'file', 'name': '//root/A',\n"2297 " 'external-contents': '//root/external/file'\n"2298 " },\n"2299 " { 'type': 'file', 'name': '//root/B',\n"2300 " 'use-external-name': true,\n"2301 " 'external-contents': '//root/external/file'\n"2302 " },\n"2303 " { 'type': 'file', 'name': '//root/C',\n"2304 " 'use-external-name': false,\n"2305 " 'external-contents': '//root/external/file'\n"2306 " }\n"2307 "] }",2308 Lower);2309 ASSERT_NE(nullptr, FS.get());2310 2311 // default true2312 EXPECT_EQ("//root/external/file", FS->status("//root/A")->getName());2313 // explicit2314 EXPECT_EQ("//root/external/file", FS->status("//root/B")->getName());2315 EXPECT_EQ("//root/C", FS->status("//root/C")->getName());2316 2317 // global configuration2318 FS = getFromYAMLString("{ 'use-external-names': false,\n"2319 " 'roots': [\n"2320 " { 'type': 'file', 'name': '//root/A',\n"2321 " 'external-contents': '//root/external/file'\n"2322 " },\n"2323 " { 'type': 'file', 'name': '//root/B',\n"2324 " 'use-external-name': true,\n"2325 " 'external-contents': '//root/external/file'\n"2326 " },\n"2327 " { 'type': 'file', 'name': '//root/C',\n"2328 " 'use-external-name': false,\n"2329 " 'external-contents': '//root/external/file'\n"2330 " }\n"2331 "] }",2332 Lower);2333 ASSERT_NE(nullptr, FS.get());2334 2335 // default2336 EXPECT_EQ("//root/A", FS->status("//root/A")->getName());2337 // explicit2338 EXPECT_EQ("//root/external/file", FS->status("//root/B")->getName());2339 EXPECT_EQ("//root/C", FS->status("//root/C")->getName());2340}2341 2342TEST_F(VFSFromYAMLTest, MultiComponentPath) {2343 auto Lower = makeIntrusiveRefCnt<DummyFileSystem>();2344 Lower->addRegularFile("//root/other");2345 2346 // file in roots2347 IntrusiveRefCntPtr<vfs::FileSystem> FS =2348 getFromYAMLString("{ 'roots': [\n"2349 " { 'type': 'file', 'name': '//root/path/to/file',\n"2350 " 'external-contents': '//root/other' }]\n"2351 "}",2352 Lower);2353 ASSERT_NE(nullptr, FS.get());2354 EXPECT_FALSE(FS->status("//root/path/to/file").getError());2355 EXPECT_FALSE(FS->status("//root/path/to").getError());2356 EXPECT_FALSE(FS->status("//root/path").getError());2357 EXPECT_FALSE(FS->status("//root/").getError());2358 2359 // at the start2360 FS = getFromYAMLString(2361 "{ 'roots': [\n"2362 " { 'type': 'directory', 'name': '//root/path/to',\n"2363 " 'contents': [ { 'type': 'file', 'name': 'file',\n"2364 " 'external-contents': '//root/other' }]}]\n"2365 "}",2366 Lower);2367 ASSERT_NE(nullptr, FS.get());2368 EXPECT_FALSE(FS->status("//root/path/to/file").getError());2369 EXPECT_FALSE(FS->status("//root/path/to").getError());2370 EXPECT_FALSE(FS->status("//root/path").getError());2371 EXPECT_FALSE(FS->status("//root/").getError());2372 2373 // at the end2374 FS = getFromYAMLString(2375 "{ 'roots': [\n"2376 " { 'type': 'directory', 'name': '//root/',\n"2377 " 'contents': [ { 'type': 'file', 'name': 'path/to/file',\n"2378 " 'external-contents': '//root/other' }]}]\n"2379 "}",2380 Lower);2381 ASSERT_NE(nullptr, FS.get());2382 EXPECT_FALSE(FS->status("//root/path/to/file").getError());2383 EXPECT_FALSE(FS->status("//root/path/to").getError());2384 EXPECT_FALSE(FS->status("//root/path").getError());2385 EXPECT_FALSE(FS->status("//root/").getError());2386}2387 2388TEST_F(VFSFromYAMLTest, TrailingSlashes) {2389 auto Lower = makeIntrusiveRefCnt<DummyFileSystem>();2390 Lower->addRegularFile("//root/other");2391 2392 // file in roots2393 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(2394 "{ 'roots': [\n"2395 " { 'type': 'directory', 'name': '//root/path/to////',\n"2396 " 'contents': [ { 'type': 'file', 'name': 'file',\n"2397 " 'external-contents': '//root/other' }]}]\n"2398 "}",2399 Lower);2400 ASSERT_NE(nullptr, FS.get());2401 EXPECT_FALSE(FS->status("//root/path/to/file").getError());2402 EXPECT_FALSE(FS->status("//root/path/to").getError());2403 EXPECT_FALSE(FS->status("//root/path").getError());2404 EXPECT_FALSE(FS->status("//root/").getError());2405}2406 2407TEST_F(VFSFromYAMLTest, DirectoryIteration) {2408 auto Lower = makeIntrusiveRefCnt<DummyFileSystem>();2409 Lower->addDirectory("//root/");2410 Lower->addDirectory("//root/foo");2411 Lower->addDirectory("//root/foo/bar");2412 Lower->addRegularFile("//root/foo/bar/a");2413 Lower->addRegularFile("//root/foo/bar/b");2414 Lower->addRegularFile("//root/file3");2415 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(2416 "{ 'use-external-names': false,\n"2417 " 'roots': [\n"2418 "{\n"2419 " 'type': 'directory',\n"2420 " 'name': '//root/',\n"2421 " 'contents': [ {\n"2422 " 'type': 'file',\n"2423 " 'name': 'file1',\n"2424 " 'external-contents': '//root/foo/bar/a'\n"2425 " },\n"2426 " {\n"2427 " 'type': 'file',\n"2428 " 'name': 'file2',\n"2429 " 'external-contents': '//root/foo/bar/b'\n"2430 " }\n"2431 " ]\n"2432 "}\n"2433 "]\n"2434 "}",2435 Lower);2436 ASSERT_NE(FS.get(), nullptr);2437 2438 auto O = makeIntrusiveRefCnt<vfs::OverlayFileSystem>(Lower);2439 O->pushOverlay(FS);2440 2441 std::error_code EC;2442 checkContents(O->dir_begin("//root/", EC),2443 {"//root/file1", "//root/file2", "//root/file3", "//root/foo"});2444 2445 checkContents(O->dir_begin("//root/foo/bar", EC),2446 {"//root/foo/bar/a", "//root/foo/bar/b"});2447}2448 2449TEST_F(VFSFromYAMLTest, DirectoryIterationSameDirMultipleEntries) {2450 // https://llvm.org/bugs/show_bug.cgi?id=277252451 if (!supportsSameDirMultipleYAMLEntries())2452 GTEST_SKIP();2453 2454 auto Lower = makeIntrusiveRefCnt<DummyFileSystem>();2455 Lower->addDirectory("//root/zab");2456 Lower->addDirectory("//root/baz");2457 Lower->addRegularFile("//root/zab/a");2458 Lower->addRegularFile("//root/zab/b");2459 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(2460 "{ 'use-external-names': false,\n"2461 " 'roots': [\n"2462 "{\n"2463 " 'type': 'directory',\n"2464 " 'name': '//root/baz/',\n"2465 " 'contents': [ {\n"2466 " 'type': 'file',\n"2467 " 'name': 'x',\n"2468 " 'external-contents': '//root/zab/a'\n"2469 " }\n"2470 " ]\n"2471 "},\n"2472 "{\n"2473 " 'type': 'directory',\n"2474 " 'name': '//root/baz/',\n"2475 " 'contents': [ {\n"2476 " 'type': 'file',\n"2477 " 'name': 'y',\n"2478 " 'external-contents': '//root/zab/b'\n"2479 " }\n"2480 " ]\n"2481 "}\n"2482 "]\n"2483 "}",2484 Lower);2485 ASSERT_NE(FS.get(), nullptr);2486 2487 auto O = makeIntrusiveRefCnt<vfs::OverlayFileSystem>(Lower);2488 O->pushOverlay(FS);2489 2490 std::error_code EC;2491 2492 checkContents(O->dir_begin("//root/baz/", EC),2493 {"//root/baz/x", "//root/baz/y"});2494}2495 2496TEST_F(VFSFromYAMLTest, RecursiveDirectoryIterationLevel) {2497 2498 auto Lower = makeIntrusiveRefCnt<DummyFileSystem>();2499 Lower->addDirectory("//root/a");2500 Lower->addDirectory("//root/a/b");2501 Lower->addDirectory("//root/a/b/c");2502 Lower->addRegularFile("//root/a/b/c/file");2503 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(2504 "{ 'use-external-names': false,\n"2505 " 'roots': [\n"2506 "{\n"2507 " 'type': 'directory',\n"2508 " 'name': '//root/a/b/c/',\n"2509 " 'contents': [ {\n"2510 " 'type': 'file',\n"2511 " 'name': 'file',\n"2512 " 'external-contents': '//root/a/b/c/file'\n"2513 " }\n"2514 " ]\n"2515 "},\n"2516 "]\n"2517 "}",2518 Lower);2519 ASSERT_NE(FS.get(), nullptr);2520 2521 auto O = makeIntrusiveRefCnt<vfs::OverlayFileSystem>(Lower);2522 O->pushOverlay(FS);2523 2524 std::error_code EC;2525 2526 // Test recursive_directory_iterator level()2527 vfs::recursive_directory_iterator I = vfs::recursive_directory_iterator(2528 *O, "//root", EC),2529 E;2530 ASSERT_FALSE(EC);2531 for (int l = 0; I != E; I.increment(EC), ++l) {2532 ASSERT_FALSE(EC);2533 EXPECT_EQ(I.level(), l);2534 }2535 EXPECT_EQ(I, E);2536}2537 2538TEST_F(VFSFromYAMLTest, RelativePaths) {2539 auto Lower = makeIntrusiveRefCnt<DummyFileSystem>();2540 std::error_code EC;2541 SmallString<128> CWD;2542 EC = llvm::sys::fs::current_path(CWD);2543 ASSERT_FALSE(EC);2544 Lower->setCurrentWorkingDirectory(CWD);2545 2546 // Filename at root level without a parent directory.2547 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(2548 "{ 'roots': [\n"2549 " { 'type': 'file', 'name': 'file-not-in-directory.h',\n"2550 " 'external-contents': '//root/external/file'\n"2551 " }\n"2552 "] }",2553 Lower);2554 ASSERT_TRUE(FS.get() != nullptr);2555 SmallString<128> ExpectedPathNotInDir("file-not-in-directory.h");2556 llvm::sys::fs::make_absolute(ExpectedPathNotInDir);2557 checkContents(FS->dir_begin(CWD, EC), {ExpectedPathNotInDir});2558 2559 // Relative file path.2560 FS = getFromYAMLString("{ 'roots': [\n"2561 " { 'type': 'file', 'name': 'relative/path.h',\n"2562 " 'external-contents': '//root/external/file'\n"2563 " }\n"2564 "] }",2565 Lower);2566 ASSERT_TRUE(FS.get() != nullptr);2567 SmallString<128> Parent("relative");2568 llvm::sys::fs::make_absolute(Parent);2569 auto I = FS->dir_begin(Parent, EC);2570 ASSERT_FALSE(EC);2571 // Convert to POSIX path for comparison of windows paths2572 ASSERT_EQ("relative/path.h",2573 getPosixPath(std::string(I->path().substr(CWD.size() + 1))));2574 2575 // Relative directory path.2576 FS = getFromYAMLString(2577 "{ 'roots': [\n"2578 " { 'type': 'directory', 'name': 'relative/directory/path.h',\n"2579 " 'contents': []\n"2580 " }\n"2581 "] }",2582 Lower);2583 ASSERT_TRUE(FS.get() != nullptr);2584 SmallString<128> Root("relative/directory");2585 llvm::sys::fs::make_absolute(Root);2586 I = FS->dir_begin(Root, EC);2587 ASSERT_FALSE(EC);2588 ASSERT_EQ("path.h", std::string(I->path().substr(Root.size() + 1)));2589 2590 EXPECT_EQ(0, NumDiagnostics);2591}2592 2593TEST_F(VFSFromYAMLTest, NonFallthroughDirectoryIteration) {2594 auto Lower = makeIntrusiveRefCnt<DummyFileSystem>();2595 Lower->addDirectory("//root/");2596 Lower->addRegularFile("//root/a");2597 Lower->addRegularFile("//root/b");2598 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(2599 "{ 'use-external-names': false,\n"2600 " 'fallthrough': false,\n"2601 " 'roots': [\n"2602 "{\n"2603 " 'type': 'directory',\n"2604 " 'name': '//root/',\n"2605 " 'contents': [ {\n"2606 " 'type': 'file',\n"2607 " 'name': 'c',\n"2608 " 'external-contents': '//root/a'\n"2609 " }\n"2610 " ]\n"2611 "}\n"2612 "]\n"2613 "}",2614 Lower);2615 ASSERT_NE(FS.get(), nullptr);2616 2617 std::error_code EC;2618 checkContents(FS->dir_begin("//root/", EC),2619 {"//root/c"});2620}2621 2622TEST_F(VFSFromYAMLTest, DirectoryIterationWithDuplicates) {2623 auto Lower = makeIntrusiveRefCnt<DummyFileSystem>();2624 Lower->addDirectory("//root/");2625 Lower->addRegularFile("//root/a");2626 Lower->addRegularFile("//root/b");2627 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(2628 "{ 'use-external-names': false,\n"2629 " 'roots': [\n"2630 "{\n"2631 " 'type': 'directory',\n"2632 " 'name': '//root/',\n"2633 " 'contents': [ {\n"2634 " 'type': 'file',\n"2635 " 'name': 'a',\n"2636 " 'external-contents': '//root/a'\n"2637 " }\n"2638 " ]\n"2639 "}\n"2640 "]\n"2641 "}",2642 Lower);2643 ASSERT_NE(FS.get(), nullptr);2644 2645 std::error_code EC;2646 checkContents(FS->dir_begin("//root/", EC),2647 {"//root/a", "//root/b"});2648}2649 2650TEST_F(VFSFromYAMLTest, DirectoryIterationErrorInVFSLayer) {2651 auto Lower = makeIntrusiveRefCnt<DummyFileSystem>();2652 Lower->addDirectory("//root/");2653 Lower->addDirectory("//root/foo");2654 Lower->addRegularFile("//root/foo/a");2655 Lower->addRegularFile("//root/foo/b");2656 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(2657 "{ 'use-external-names': false,\n"2658 " 'roots': [\n"2659 "{\n"2660 " 'type': 'directory',\n"2661 " 'name': '//root/',\n"2662 " 'contents': [ {\n"2663 " 'type': 'file',\n"2664 " 'name': 'bar/a',\n"2665 " 'external-contents': '//root/foo/a'\n"2666 " }\n"2667 " ]\n"2668 "}\n"2669 "]\n"2670 "}",2671 Lower);2672 ASSERT_NE(FS.get(), nullptr);2673 2674 std::error_code EC;2675 checkContents(FS->dir_begin("//root/foo", EC),2676 {"//root/foo/a", "//root/foo/b"});2677}2678 2679TEST_F(VFSFromYAMLTest, GetRealPath) {2680 auto Lower = makeIntrusiveRefCnt<DummyFileSystem>();2681 Lower->addDirectory("//dir/");2682 Lower->addRegularFile("/foo");2683 Lower->addSymlink("/link");2684 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(2685 "{ 'use-external-names': false,\n"2686 " 'case-sensitive': false,\n"2687 " 'roots': [\n"2688 "{\n"2689 " 'type': 'directory',\n"2690 " 'name': '//root/',\n"2691 " 'contents': [ {\n"2692 " 'type': 'file',\n"2693 " 'name': 'bar',\n"2694 " 'external-contents': '/link'\n"2695 " },\n"2696 " {\n"2697 " 'type': 'directory',\n"2698 " 'name': 'baz',\n"2699 " 'contents': []\n"2700 " }\n"2701 " ]\n"2702 "},\n"2703 "{\n"2704 " 'type': 'directory',\n"2705 " 'name': '//dir/',\n"2706 " 'contents': []\n"2707 "}\n"2708 "]\n"2709 "}",2710 Lower);2711 ASSERT_NE(FS.get(), nullptr);2712 2713 // Regular file present in underlying file system.2714 SmallString<16> RealPath;2715 EXPECT_FALSE(FS->getRealPath("/foo", RealPath));2716 EXPECT_EQ(RealPath.str(), "/foo");2717 2718 // File present in YAML pointing to symlink in underlying file system.2719 EXPECT_FALSE(FS->getRealPath("//root/bar", RealPath));2720 EXPECT_EQ(RealPath.str(), "/symlink");2721 2722 // Directories should return the virtual path as written in the definition.2723 EXPECT_FALSE(FS->getRealPath("//ROOT/baz", RealPath));2724 EXPECT_EQ(RealPath.str(), "//root/baz");2725 2726 // Try a non-existing file.2727 EXPECT_EQ(FS->getRealPath("/non_existing", RealPath),2728 errc::no_such_file_or_directory);2729}2730 2731TEST_F(VFSFromYAMLTest, WorkingDirectory) {2732 auto Lower = makeIntrusiveRefCnt<DummyFileSystem>();2733 Lower->addDirectory("//root/");2734 Lower->addDirectory("//root/foo");2735 Lower->addRegularFile("//root/foo/a");2736 Lower->addRegularFile("//root/foo/b");2737 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(2738 "{ 'use-external-names': false,\n"2739 " 'roots': [\n"2740 "{\n"2741 " 'type': 'directory',\n"2742 " 'name': '//root/bar',\n"2743 " 'contents': [ {\n"2744 " 'type': 'file',\n"2745 " 'name': 'a',\n"2746 " 'external-contents': '//root/foo/a'\n"2747 " }\n"2748 " ]\n"2749 "}\n"2750 "]\n"2751 "}",2752 Lower);2753 ASSERT_NE(FS.get(), nullptr);2754 std::error_code EC = FS->setCurrentWorkingDirectory("//root/bar");2755 ASSERT_FALSE(EC);2756 2757 llvm::ErrorOr<std::string> WorkingDir = FS->getCurrentWorkingDirectory();2758 ASSERT_TRUE(WorkingDir);2759 EXPECT_EQ(*WorkingDir, "//root/bar");2760 2761 llvm::ErrorOr<vfs::Status> Status = FS->status("./a");2762 ASSERT_FALSE(Status.getError());2763 EXPECT_TRUE(Status->isStatusKnown());2764 EXPECT_FALSE(Status->isDirectory());2765 EXPECT_TRUE(Status->isRegularFile());2766 EXPECT_FALSE(Status->isSymlink());2767 EXPECT_FALSE(Status->isOther());2768 EXPECT_TRUE(Status->exists());2769 2770 EC = FS->setCurrentWorkingDirectory("bogus");2771 ASSERT_TRUE(EC);2772 WorkingDir = FS->getCurrentWorkingDirectory();2773 ASSERT_TRUE(WorkingDir);2774 EXPECT_EQ(*WorkingDir, "//root/bar");2775 2776 EC = FS->setCurrentWorkingDirectory("//root/");2777 ASSERT_FALSE(EC);2778 WorkingDir = FS->getCurrentWorkingDirectory();2779 ASSERT_TRUE(WorkingDir);2780 EXPECT_EQ(*WorkingDir, "//root/");2781 2782 EC = FS->setCurrentWorkingDirectory("bar");2783 ASSERT_FALSE(EC);2784 WorkingDir = FS->getCurrentWorkingDirectory();2785 ASSERT_TRUE(WorkingDir);2786 EXPECT_EQ(*WorkingDir, "//root/bar");2787}2788 2789TEST_F(VFSFromYAMLTest, WorkingDirectoryFallthrough) {2790 auto Lower = makeIntrusiveRefCnt<DummyFileSystem>();2791 Lower->addDirectory("//root/");2792 Lower->addDirectory("//root/foo");2793 Lower->addRegularFile("//root/foo/a");2794 Lower->addRegularFile("//root/foo/b");2795 Lower->addRegularFile("//root/c");2796 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(2797 "{ 'use-external-names': false,\n"2798 " 'roots': [\n"2799 "{\n"2800 " 'type': 'directory',\n"2801 " 'name': '//root/bar',\n"2802 " 'contents': [ {\n"2803 " 'type': 'file',\n"2804 " 'name': 'a',\n"2805 " 'external-contents': '//root/foo/a'\n"2806 " }\n"2807 " ]\n"2808 "},\n"2809 "{\n"2810 " 'type': 'directory',\n"2811 " 'name': '//root/bar/baz',\n"2812 " 'contents': [ {\n"2813 " 'type': 'file',\n"2814 " 'name': 'a',\n"2815 " 'external-contents': '//root/foo/a'\n"2816 " }\n"2817 " ]\n"2818 "}\n"2819 "]\n"2820 "}",2821 Lower);2822 ASSERT_NE(FS.get(), nullptr);2823 std::error_code EC = FS->setCurrentWorkingDirectory("//root/");2824 ASSERT_FALSE(EC);2825 ASSERT_NE(FS.get(), nullptr);2826 2827 llvm::ErrorOr<vfs::Status> Status = FS->status("bar/a");2828 ASSERT_FALSE(Status.getError());2829 EXPECT_TRUE(Status->exists());2830 2831 Status = FS->status("foo/a");2832 ASSERT_FALSE(Status.getError());2833 EXPECT_TRUE(Status->exists());2834 2835 EC = FS->setCurrentWorkingDirectory("//root/bar");2836 ASSERT_FALSE(EC);2837 2838 Status = FS->status("./a");2839 ASSERT_FALSE(Status.getError());2840 EXPECT_TRUE(Status->exists());2841 2842 Status = FS->status("./b");2843 ASSERT_TRUE(Status.getError());2844 2845 Status = FS->status("./c");2846 ASSERT_TRUE(Status.getError());2847 2848 EC = FS->setCurrentWorkingDirectory("//root/");2849 ASSERT_FALSE(EC);2850 2851 Status = FS->status("c");2852 ASSERT_FALSE(Status.getError());2853 EXPECT_TRUE(Status->exists());2854 2855 Status = FS->status("./bar/baz/a");2856 ASSERT_FALSE(Status.getError());2857 EXPECT_TRUE(Status->exists());2858 2859 EC = FS->setCurrentWorkingDirectory("//root/bar");2860 ASSERT_FALSE(EC);2861 2862 Status = FS->status("./baz/a");2863 ASSERT_FALSE(Status.getError());2864 EXPECT_TRUE(Status->exists());2865 2866 Status = FS->status("../bar/baz/a");2867 ASSERT_FALSE(Status.getError());2868 EXPECT_TRUE(Status->exists());2869}2870 2871TEST_F(VFSFromYAMLTest, WorkingDirectoryFallthroughInvalid) {2872 auto Lower = makeIntrusiveRefCnt<ErrorDummyFileSystem>();2873 Lower->addDirectory("//root/");2874 Lower->addDirectory("//root/foo");2875 Lower->addRegularFile("//root/foo/a");2876 Lower->addRegularFile("//root/foo/b");2877 Lower->addRegularFile("//root/c");2878 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(2879 "{ 'use-external-names': false,\n"2880 " 'roots': [\n"2881 "{\n"2882 " 'type': 'directory',\n"2883 " 'name': '//root/bar',\n"2884 " 'contents': [ {\n"2885 " 'type': 'file',\n"2886 " 'name': 'a',\n"2887 " 'external-contents': '//root/foo/a'\n"2888 " }\n"2889 " ]\n"2890 "}\n"2891 "]\n"2892 "}",2893 Lower);2894 ASSERT_NE(FS.get(), nullptr);2895 std::error_code EC = FS->setCurrentWorkingDirectory("//root/");2896 ASSERT_FALSE(EC);2897 ASSERT_NE(FS.get(), nullptr);2898 2899 llvm::ErrorOr<vfs::Status> Status = FS->status("bar/a");2900 ASSERT_FALSE(Status.getError());2901 EXPECT_TRUE(Status->exists());2902 2903 Status = FS->status("foo/a");2904 ASSERT_FALSE(Status.getError());2905 EXPECT_TRUE(Status->exists());2906}2907 2908TEST_F(VFSFromYAMLTest, VirtualWorkingDirectory) {2909 auto Lower = makeIntrusiveRefCnt<ErrorDummyFileSystem>();2910 Lower->addDirectory("//root/");2911 Lower->addDirectory("//root/foo");2912 Lower->addRegularFile("//root/foo/a");2913 Lower->addRegularFile("//root/foo/b");2914 Lower->addRegularFile("//root/c");2915 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(2916 "{ 'use-external-names': false,\n"2917 " 'roots': [\n"2918 "{\n"2919 " 'type': 'directory',\n"2920 " 'name': '//root/bar',\n"2921 " 'contents': [ {\n"2922 " 'type': 'file',\n"2923 " 'name': 'a',\n"2924 " 'external-contents': '//root/foo/a'\n"2925 " }\n"2926 " ]\n"2927 "}\n"2928 "]\n"2929 "}",2930 Lower);2931 ASSERT_NE(FS.get(), nullptr);2932 std::error_code EC = FS->setCurrentWorkingDirectory("//root/bar");2933 ASSERT_FALSE(EC);2934 ASSERT_NE(FS.get(), nullptr);2935 2936 llvm::ErrorOr<vfs::Status> Status = FS->status("a");2937 ASSERT_FALSE(Status.getError());2938 EXPECT_TRUE(Status->exists());2939}2940 2941TEST_F(VFSFromYAMLTest, YAMLVFSWriterTest) {2942 TempDir TestDirectory("virtual-file-system-test", /*Unique*/ true);2943 TempDir _a(TestDirectory.path("a"));2944 TempFile _ab(TestDirectory.path("a, b"));2945 TempDir _c(TestDirectory.path("c"));2946 TempFile _cd(TestDirectory.path("c/d"));2947 TempDir _e(TestDirectory.path("e"));2948 TempDir _ef(TestDirectory.path("e/f"));2949 TempFile _g(TestDirectory.path("g"));2950 TempDir _h(TestDirectory.path("h"));2951 2952 vfs::YAMLVFSWriter VFSWriter;2953 VFSWriter.addDirectoryMapping(_a.path(), "//root/a");2954 VFSWriter.addFileMapping(_ab.path(), "//root/a/b");2955 VFSWriter.addFileMapping(_cd.path(), "//root/c/d");2956 VFSWriter.addDirectoryMapping(_e.path(), "//root/e");2957 VFSWriter.addDirectoryMapping(_ef.path(), "//root/e/f");2958 VFSWriter.addFileMapping(_g.path(), "//root/g");2959 VFSWriter.addDirectoryMapping(_h.path(), "//root/h");2960 2961 std::string Buffer;2962 raw_string_ostream OS(Buffer);2963 VFSWriter.write(OS);2964 2965 auto Lower = makeIntrusiveRefCnt<ErrorDummyFileSystem>();2966 Lower->addDirectory("//root/");2967 Lower->addDirectory("//root/a");2968 Lower->addRegularFile("//root/a/b");2969 Lower->addDirectory("//root/b");2970 Lower->addDirectory("//root/c");2971 Lower->addRegularFile("//root/c/d");2972 Lower->addDirectory("//root/e");2973 Lower->addDirectory("//root/e/f");2974 Lower->addRegularFile("//root/g");2975 Lower->addDirectory("//root/h");2976 2977 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLRawString(Buffer, Lower);2978 ASSERT_NE(FS.get(), nullptr);2979 2980 EXPECT_TRUE(FS->exists(_a.path()));2981 EXPECT_TRUE(FS->exists(_ab.path()));2982 EXPECT_TRUE(FS->exists(_c.path()));2983 EXPECT_TRUE(FS->exists(_cd.path()));2984 EXPECT_TRUE(FS->exists(_e.path()));2985 EXPECT_TRUE(FS->exists(_ef.path()));2986 EXPECT_TRUE(FS->exists(_g.path()));2987 EXPECT_TRUE(FS->exists(_h.path()));2988}2989 2990TEST_F(VFSFromYAMLTest, YAMLVFSWriterTest2) {2991 TempDir TestDirectory("virtual-file-system-test", /*Unique*/ true);2992 TempDir _a(TestDirectory.path("a"));2993 TempFile _ab(TestDirectory.path("a/b"));2994 TempDir _ac(TestDirectory.path("a/c"));2995 TempFile _acd(TestDirectory.path("a/c/d"));2996 TempFile _ace(TestDirectory.path("a/c/e"));2997 TempFile _acf(TestDirectory.path("a/c/f"));2998 TempDir _ag(TestDirectory.path("a/g"));2999 TempFile _agh(TestDirectory.path("a/g/h"));3000 3001 vfs::YAMLVFSWriter VFSWriter;3002 VFSWriter.addDirectoryMapping(_a.path(), "//root/a");3003 VFSWriter.addFileMapping(_ab.path(), "//root/a/b");3004 VFSWriter.addDirectoryMapping(_ac.path(), "//root/a/c");3005 VFSWriter.addFileMapping(_acd.path(), "//root/a/c/d");3006 VFSWriter.addFileMapping(_ace.path(), "//root/a/c/e");3007 VFSWriter.addFileMapping(_acf.path(), "//root/a/c/f");3008 VFSWriter.addDirectoryMapping(_ag.path(), "//root/a/g");3009 VFSWriter.addFileMapping(_agh.path(), "//root/a/g/h");3010 3011 std::string Buffer;3012 raw_string_ostream OS(Buffer);3013 VFSWriter.write(OS);3014 3015 auto Lower = makeIntrusiveRefCnt<ErrorDummyFileSystem>();3016 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLRawString(Buffer, Lower);3017 EXPECT_NE(FS.get(), nullptr);3018}3019 3020TEST_F(VFSFromYAMLTest, YAMLVFSWriterTest3) {3021 TempDir TestDirectory("virtual-file-system-test", /*Unique*/ true);3022 TempDir _a(TestDirectory.path("a"));3023 TempFile _ab(TestDirectory.path("a/b"));3024 TempDir _ac(TestDirectory.path("a/c"));3025 TempDir _acd(TestDirectory.path("a/c/d"));3026 TempDir _acde(TestDirectory.path("a/c/d/e"));3027 TempFile _acdef(TestDirectory.path("a/c/d/e/f"));3028 TempFile _acdeg(TestDirectory.path("a/c/d/e/g"));3029 TempDir _ah(TestDirectory.path("a/h"));3030 TempFile _ahi(TestDirectory.path("a/h/i"));3031 3032 vfs::YAMLVFSWriter VFSWriter;3033 VFSWriter.addDirectoryMapping(_a.path(), "//root/a");3034 VFSWriter.addFileMapping(_ab.path(), "//root/a/b");3035 VFSWriter.addDirectoryMapping(_ac.path(), "//root/a/c");3036 VFSWriter.addDirectoryMapping(_acd.path(), "//root/a/c/d");3037 VFSWriter.addDirectoryMapping(_acde.path(), "//root/a/c/d/e");3038 VFSWriter.addFileMapping(_acdef.path(), "//root/a/c/d/e/f");3039 VFSWriter.addFileMapping(_acdeg.path(), "//root/a/c/d/e/g");3040 VFSWriter.addDirectoryMapping(_ahi.path(), "//root/a/h");3041 VFSWriter.addFileMapping(_ahi.path(), "//root/a/h/i");3042 3043 std::string Buffer;3044 raw_string_ostream OS(Buffer);3045 VFSWriter.write(OS);3046 3047 auto Lower = makeIntrusiveRefCnt<ErrorDummyFileSystem>();3048 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLRawString(Buffer, Lower);3049 EXPECT_NE(FS.get(), nullptr);3050}3051 3052TEST_F(VFSFromYAMLTest, YAMLVFSWriterTestHandleDirs) {3053 TempDir TestDirectory("virtual-file-system-test", /*Unique*/ true);3054 TempDir _a(TestDirectory.path("a"));3055 TempDir _b(TestDirectory.path("b"));3056 TempDir _c(TestDirectory.path("c"));3057 3058 vfs::YAMLVFSWriter VFSWriter;3059 VFSWriter.addDirectoryMapping(_a.path(), "//root/a");3060 VFSWriter.addDirectoryMapping(_b.path(), "//root/b");3061 VFSWriter.addDirectoryMapping(_c.path(), "//root/c");3062 3063 std::string Buffer;3064 raw_string_ostream OS(Buffer);3065 VFSWriter.write(OS);3066 3067 // We didn't add a single file - only directories.3068 EXPECT_EQ(Buffer.find("'type': 'file'"), std::string::npos);3069 3070 auto Lower = makeIntrusiveRefCnt<ErrorDummyFileSystem>();3071 Lower->addDirectory("//root/a");3072 Lower->addDirectory("//root/b");3073 Lower->addDirectory("//root/c");3074 // canaries3075 Lower->addRegularFile("//root/a/a");3076 Lower->addRegularFile("//root/b/b");3077 Lower->addRegularFile("//root/c/c");3078 3079 IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLRawString(Buffer, Lower);3080 ASSERT_NE(FS.get(), nullptr);3081 3082 EXPECT_FALSE(FS->exists(_a.path("a")));3083 EXPECT_FALSE(FS->exists(_b.path("b")));3084 EXPECT_FALSE(FS->exists(_c.path("c")));3085}3086 3087TEST_F(VFSFromYAMLTest, RedirectingWith) {3088 auto Both = makeIntrusiveRefCnt<DummyFileSystem>();3089 Both->addDirectory("//root/a");3090 Both->addRegularFile("//root/a/f");3091 Both->addDirectory("//root/b");3092 Both->addRegularFile("//root/b/f");3093 3094 auto AOnly = makeIntrusiveRefCnt<DummyFileSystem>();3095 AOnly->addDirectory("//root/a");3096 AOnly->addRegularFile("//root/a/f");3097 3098 auto BOnly = makeIntrusiveRefCnt<DummyFileSystem>();3099 BOnly->addDirectory("//root/b");3100 BOnly->addRegularFile("//root/b/f");3101 3102 auto BaseStr = std::string(" 'roots': [\n"3103 " {\n"3104 " 'type': 'directory-remap',\n"3105 " 'name': '//root/a',\n"3106 " 'external-contents': '//root/b'\n"3107 " }\n"3108 " ]\n"3109 "}");3110 auto FallthroughStr = "{ 'redirecting-with': 'fallthrough',\n" + BaseStr;3111 auto FallbackStr = "{ 'redirecting-with': 'fallback',\n" + BaseStr;3112 auto RedirectOnlyStr = "{ 'redirecting-with': 'redirect-only',\n" + BaseStr;3113 3114 auto ExpectPath = [&](vfs::FileSystem &FS, StringRef Expected,3115 StringRef Message) {3116 auto AF = FS.openFileForRead("//root/a/f");3117 ASSERT_FALSE(AF.getError()) << Message;3118 auto AFName = (*AF)->getName();3119 ASSERT_FALSE(AFName.getError()) << Message;3120 EXPECT_EQ(Expected.str(), AFName.get()) << Message;3121 3122 auto AS = FS.status("//root/a/f");3123 ASSERT_FALSE(AS.getError()) << Message;3124 EXPECT_EQ(Expected.str(), AS->getName()) << Message;3125 };3126 3127 auto ExpectFailure = [&](vfs::FileSystem &FS, StringRef Message) {3128 EXPECT_TRUE(FS.openFileForRead("//root/a/f").getError()) << Message;3129 EXPECT_TRUE(FS.status("//root/a/f").getError()) << Message;3130 };3131 3132 {3133 // `f` in both `a` and `b`3134 3135 // `fallthrough` tries `external-name` first, so should be `b`3136 IntrusiveRefCntPtr<vfs::FileSystem> Fallthrough =3137 getFromYAMLString(FallthroughStr, Both);3138 ASSERT_TRUE(Fallthrough.get() != nullptr);3139 ExpectPath(*Fallthrough, "//root/b/f", "fallthrough, both exist");3140 3141 // `fallback` tries the original name first, so should be `a`3142 IntrusiveRefCntPtr<vfs::FileSystem> Fallback =3143 getFromYAMLString(FallbackStr, Both);3144 ASSERT_TRUE(Fallback.get() != nullptr);3145 ExpectPath(*Fallback, "//root/a/f", "fallback, both exist");3146 3147 // `redirect-only` is the same as `fallthrough` but doesn't try the3148 // original on failure, so no change here (ie. `b`)3149 IntrusiveRefCntPtr<vfs::FileSystem> Redirect =3150 getFromYAMLString(RedirectOnlyStr, Both);3151 ASSERT_TRUE(Redirect.get() != nullptr);3152 ExpectPath(*Redirect, "//root/b/f", "redirect-only, both exist");3153 }3154 3155 {3156 // `f` in `a` only3157 3158 // Fallthrough to the original path, `a`3159 IntrusiveRefCntPtr<vfs::FileSystem> Fallthrough =3160 getFromYAMLString(FallthroughStr, AOnly);3161 ASSERT_TRUE(Fallthrough.get() != nullptr);3162 ExpectPath(*Fallthrough, "//root/a/f", "fallthrough, a only");3163 3164 // Original first, so still `a`3165 IntrusiveRefCntPtr<vfs::FileSystem> Fallback =3166 getFromYAMLString(FallbackStr, AOnly);3167 ASSERT_TRUE(Fallback.get() != nullptr);3168 ExpectPath(*Fallback, "//root/a/f", "fallback, a only");3169 3170 // Fails since no fallthrough3171 IntrusiveRefCntPtr<vfs::FileSystem> Redirect =3172 getFromYAMLString(RedirectOnlyStr, AOnly);3173 ASSERT_TRUE(Redirect.get() != nullptr);3174 ExpectFailure(*Redirect, "redirect-only, a only");3175 }3176 3177 {3178 // `f` in `b` only3179 3180 // Tries `b` first (no fallthrough)3181 IntrusiveRefCntPtr<vfs::FileSystem> Fallthrough =3182 getFromYAMLString(FallthroughStr, BOnly);3183 ASSERT_TRUE(Fallthrough.get() != nullptr);3184 ExpectPath(*Fallthrough, "//root/b/f", "fallthrough, b only");3185 3186 // Tries original first but then fallsback to `b`3187 IntrusiveRefCntPtr<vfs::FileSystem> Fallback =3188 getFromYAMLString(FallbackStr, BOnly);3189 ASSERT_TRUE(Fallback.get() != nullptr);3190 ExpectPath(*Fallback, "//root/b/f", "fallback, b only");3191 3192 // Redirect exists, so uses it (`b`)3193 IntrusiveRefCntPtr<vfs::FileSystem> Redirect =3194 getFromYAMLString(RedirectOnlyStr, BOnly);3195 ASSERT_TRUE(Redirect.get() != nullptr);3196 ExpectPath(*Redirect, "//root/b/f", "redirect-only, b only");3197 }3198 3199 EXPECT_EQ(0, NumDiagnostics);3200}3201 3202TEST(VFSFromRemappedFilesTest, Basic) {3203 auto BaseFS = makeIntrusiveRefCnt<vfs::InMemoryFileSystem>();3204 BaseFS->addFile("//root/b", 0, MemoryBuffer::getMemBuffer("contents of b"));3205 BaseFS->addFile("//root/c", 0, MemoryBuffer::getMemBuffer("contents of c"));3206 3207 std::vector<std::pair<std::string, std::string>> RemappedFiles = {3208 {"//root/a/a", "//root/b"},3209 {"//root/a/b/c", "//root/c"},3210 };3211 auto RemappedFS = vfs::RedirectingFileSystem::create(3212 RemappedFiles, /*UseExternalNames=*/false, BaseFS);3213 3214 auto StatA = RemappedFS->status("//root/a/a");3215 auto StatB = RemappedFS->status("//root/a/b/c");3216 ASSERT_TRUE(StatA);3217 ASSERT_TRUE(StatB);3218 EXPECT_EQ("//root/a/a", StatA->getName());3219 EXPECT_EQ("//root/a/b/c", StatB->getName());3220 3221 auto BufferA = RemappedFS->getBufferForFile("//root/a/a");3222 auto BufferB = RemappedFS->getBufferForFile("//root/a/b/c");3223 ASSERT_TRUE(BufferA);3224 ASSERT_TRUE(BufferB);3225 EXPECT_EQ("contents of b", (*BufferA)->getBuffer());3226 EXPECT_EQ("contents of c", (*BufferB)->getBuffer());3227}3228 3229TEST(VFSFromRemappedFilesTest, UseExternalNames) {3230 auto BaseFS = makeIntrusiveRefCnt<vfs::InMemoryFileSystem>();3231 BaseFS->addFile("//root/b", 0, MemoryBuffer::getMemBuffer("contents of b"));3232 BaseFS->addFile("//root/c", 0, MemoryBuffer::getMemBuffer("contents of c"));3233 3234 std::vector<std::pair<std::string, std::string>> RemappedFiles = {3235 {"//root/a/a", "//root/b"},3236 {"//root/a/b/c", "//root/c"},3237 };3238 auto RemappedFS = vfs::RedirectingFileSystem::create(3239 RemappedFiles, /*UseExternalNames=*/true, BaseFS);3240 3241 auto StatA = RemappedFS->status("//root/a/a");3242 auto StatB = RemappedFS->status("//root/a/b/c");3243 ASSERT_TRUE(StatA);3244 ASSERT_TRUE(StatB);3245 EXPECT_EQ("//root/b", StatA->getName());3246 EXPECT_EQ("//root/c", StatB->getName());3247 3248 auto BufferA = RemappedFS->getBufferForFile("//root/a/a");3249 auto BufferB = RemappedFS->getBufferForFile("//root/a/b/c");3250 ASSERT_TRUE(BufferA);3251 ASSERT_TRUE(BufferB);3252 EXPECT_EQ("contents of b", (*BufferA)->getBuffer());3253 EXPECT_EQ("contents of c", (*BufferB)->getBuffer());3254}3255 3256TEST(VFSFromRemappedFilesTest, LastMappingWins) {3257 auto BaseFS = makeIntrusiveRefCnt<vfs::InMemoryFileSystem>();3258 BaseFS->addFile("//root/b", 0, MemoryBuffer::getMemBuffer("contents of b"));3259 BaseFS->addFile("//root/c", 0, MemoryBuffer::getMemBuffer("contents of c"));3260 3261 std::vector<std::pair<std::string, std::string>> RemappedFiles = {3262 {"//root/a", "//root/b"},3263 {"//root/a", "//root/c"},3264 };3265 auto RemappedFSKeepName = vfs::RedirectingFileSystem::create(3266 RemappedFiles, /*UseExternalNames=*/false, BaseFS);3267 auto RemappedFSExternalName = vfs::RedirectingFileSystem::create(3268 RemappedFiles, /*UseExternalNames=*/true, BaseFS);3269 3270 auto StatKeepA = RemappedFSKeepName->status("//root/a");3271 auto StatExternalA = RemappedFSExternalName->status("//root/a");3272 ASSERT_TRUE(StatKeepA);3273 ASSERT_TRUE(StatExternalA);3274 EXPECT_EQ("//root/a", StatKeepA->getName());3275 EXPECT_EQ("//root/c", StatExternalA->getName());3276 3277 auto BufferKeepA = RemappedFSKeepName->getBufferForFile("//root/a");3278 auto BufferExternalA = RemappedFSExternalName->getBufferForFile("//root/a");3279 ASSERT_TRUE(BufferKeepA);3280 ASSERT_TRUE(BufferExternalA);3281 EXPECT_EQ("contents of c", (*BufferKeepA)->getBuffer());3282 EXPECT_EQ("contents of c", (*BufferExternalA)->getBuffer());3283}3284 3285TEST(RedirectingFileSystemTest, PrintOutput) {3286 auto Buffer =3287 MemoryBuffer::getMemBuffer("{\n"3288 " 'version': 0,\n"3289 " 'roots': [\n"3290 " {\n"3291 " 'type': 'directory-remap',\n"3292 " 'name': '/dremap',\n"3293 " 'external-contents': '/a',\n"3294 " },"3295 " {\n"3296 " 'type': 'directory',\n"3297 " 'name': '/vdir',\n"3298 " 'contents': ["3299 " {\n"3300 " 'type': 'directory-remap',\n"3301 " 'name': 'dremap',\n"3302 " 'external-contents': '/b'\n"3303 " 'use-external-name': 'true'\n"3304 " },\n"3305 " {\n"3306 " 'type': 'file',\n"3307 " 'name': 'vfile',\n"3308 " 'external-contents': '/c'\n"3309 " 'use-external-name': 'false'\n"3310 " }]\n"3311 " }]\n"3312 "}");3313 3314 auto Dummy = makeIntrusiveRefCnt<DummyFileSystem>();3315 auto Redirecting = vfs::RedirectingFileSystem::create(3316 std::move(Buffer), nullptr, "", nullptr, Dummy);3317 3318 SmallString<0> Output;3319 raw_svector_ostream OuputStream{Output};3320 3321 Redirecting->print(OuputStream, vfs::FileSystem::PrintType::Summary);3322 ASSERT_EQ("RedirectingFileSystem (UseExternalNames: true)\n", Output);3323 3324 Output.clear();3325 Redirecting->print(OuputStream, vfs::FileSystem::PrintType::Contents);3326 ASSERT_EQ("RedirectingFileSystem (UseExternalNames: true)\n"3327 "'/'\n"3328 " 'dremap' -> '/a'\n"3329 " 'vdir'\n"3330 " 'dremap' -> '/b' (UseExternalName: true)\n"3331 " 'vfile' -> '/c' (UseExternalName: false)\n"3332 "ExternalFS:\n"3333 " DummyFileSystem (Summary)\n",3334 Output);3335 3336 Output.clear();3337 Redirecting->print(OuputStream, vfs::FileSystem::PrintType::Contents, 1);3338 ASSERT_EQ(" RedirectingFileSystem (UseExternalNames: true)\n"3339 " '/'\n"3340 " 'dremap' -> '/a'\n"3341 " 'vdir'\n"3342 " 'dremap' -> '/b' (UseExternalName: true)\n"3343 " 'vfile' -> '/c' (UseExternalName: false)\n"3344 " ExternalFS:\n"3345 " DummyFileSystem (Summary)\n",3346 Output);3347 3348 Output.clear();3349 Redirecting->print(OuputStream,3350 vfs::FileSystem::PrintType::RecursiveContents);3351 ASSERT_EQ("RedirectingFileSystem (UseExternalNames: true)\n"3352 "'/'\n"3353 " 'dremap' -> '/a'\n"3354 " 'vdir'\n"3355 " 'dremap' -> '/b' (UseExternalName: true)\n"3356 " 'vfile' -> '/c' (UseExternalName: false)\n"3357 "ExternalFS:\n"3358 " DummyFileSystem (RecursiveContents)\n",3359 Output);3360}3361 3362TEST(RedirectingFileSystemTest, Used) {3363 auto Dummy = makeIntrusiveRefCnt<DummyFileSystem>();3364 auto YAML1 =3365 MemoryBuffer::getMemBuffer("{\n"3366 " 'version': 0,\n"3367 " 'redirecting-with': 'fallthrough',\n"3368 " 'roots': [\n"3369 " {\n"3370 " 'type': 'file',\n"3371 " 'name': '/vfile1',\n"3372 " 'external-contents': '/a',\n"3373 " },"3374 " ]\n"3375 "}");3376 auto YAML2 =3377 MemoryBuffer::getMemBuffer("{\n"3378 " 'version': 0,\n"3379 " 'redirecting-with': 'fallthrough',\n"3380 " 'roots': [\n"3381 " {\n"3382 " 'type': 'file',\n"3383 " 'name': '/vfile2',\n"3384 " 'external-contents': '/b',\n"3385 " },"3386 " ]\n"3387 "}");3388 3389 Dummy->addRegularFile("/a");3390 Dummy->addRegularFile("/b");3391 3392 IntrusiveRefCntPtr<vfs::RedirectingFileSystem> Redirecting1 =3393 vfs::RedirectingFileSystem::create(std::move(YAML1), nullptr, "", nullptr,3394 Dummy)3395 .release();3396 auto Redirecting2 = vfs::RedirectingFileSystem::create(3397 std::move(YAML2), nullptr, "", nullptr, Redirecting1);3398 3399 Redirecting1->setUsageTrackingActive(true);3400 Redirecting2->setUsageTrackingActive(true);3401 EXPECT_TRUE(Redirecting2->exists("/vfile1"));3402 EXPECT_TRUE(Redirecting2->exists("/b"));3403 EXPECT_TRUE(Redirecting1->hasBeenUsed());3404 EXPECT_FALSE(Redirecting2->hasBeenUsed());3405}3406 3407// Check that paths looked up in the external filesystem are unmodified, except3408// potentially to add the working directory. We cannot canonicalize away ..3409// in the presence of symlinks in the external filesystem.3410TEST(RedirectingFileSystemTest, ExternalPaths) {3411 struct InterceptorFS : llvm::vfs::ProxyFileSystem {3412 std::vector<std::string> SeenPaths;3413 3414 InterceptorFS(IntrusiveRefCntPtr<FileSystem> UnderlyingFS)3415 : ProxyFileSystem(UnderlyingFS) {}3416 3417 llvm::ErrorOr<llvm::vfs::Status> status(const Twine &Path) override {3418 SeenPaths.push_back(Path.str());3419 return ProxyFileSystem::status(Path);3420 }3421 3422 llvm::ErrorOr<std::unique_ptr<llvm::vfs::File>>3423 openFileForRead(const Twine &Path) override {3424 SeenPaths.push_back(Path.str());3425 return ProxyFileSystem::openFileForRead(Path);3426 }3427 3428 std::error_code isLocal(const Twine &Path, bool &Result) override {3429 SeenPaths.push_back(Path.str());3430 return ProxyFileSystem::isLocal(Path, Result);3431 }3432 3433 vfs::directory_iterator dir_begin(const Twine &Dir,3434 std::error_code &EC) override {3435 SeenPaths.push_back(Dir.str());3436 return ProxyFileSystem::dir_begin(Dir, EC);3437 }3438 3439 bool exists(const Twine &Path) override {3440 SeenPaths.push_back(Path.str());3441 return ProxyFileSystem::exists(Path);3442 }3443 };3444 3445 std::error_code EC;3446 auto BaseFS = makeIntrusiveRefCnt<DummyFileSystem>();3447 BaseFS->setCurrentWorkingDirectory("/cwd");3448 auto CheckFS = makeIntrusiveRefCnt<InterceptorFS>(BaseFS);3449 auto FS = vfs::RedirectingFileSystem::create({}, /*UseExternalNames=*/false,3450 CheckFS);3451 3452 FS->status("/a/../b");3453 FS->openFileForRead("c");3454 FS->exists("./d");3455 bool IsLocal = false;3456 FS->isLocal("/e/./../f", IsLocal);3457 FS->dir_begin(".././g", EC);3458 3459 std::vector<std::string> Expected{"/a/../b", "/cwd/c", "/cwd/./d",3460 "/e/./../f", "/cwd/.././g"};3461 3462 EXPECT_EQ(CheckFS->SeenPaths, Expected);3463 3464 CheckFS->SeenPaths.clear();3465 FS->setRedirection(vfs::RedirectingFileSystem::RedirectKind::Fallback);3466 FS->status("/a/../b");3467 FS->openFileForRead("c");3468 FS->exists("./d");3469 FS->isLocal("/e/./../f", IsLocal);3470 FS->dir_begin(".././g", EC);3471 3472 EXPECT_EQ(CheckFS->SeenPaths, Expected);3473}3474 3475TEST(RedirectingFileSystemTest, Exists) {3476 auto Dummy = makeIntrusiveRefCnt<NoStatusDummyFileSystem>();3477 auto YAML =3478 MemoryBuffer::getMemBuffer("{\n"3479 " 'version': 0,\n"3480 " 'roots': [\n"3481 " {\n"3482 " 'type': 'directory-remap',\n"3483 " 'name': '/dremap',\n"3484 " 'external-contents': '/a',\n"3485 " },"3486 " {\n"3487 " 'type': 'directory-remap',\n"3488 " 'name': '/dmissing',\n"3489 " 'external-contents': '/dmissing',\n"3490 " },"3491 " {\n"3492 " 'type': 'directory',\n"3493 " 'name': '/both',\n"3494 " 'contents': [\n"3495 " {\n"3496 " 'type': 'file',\n"3497 " 'name': 'vfile',\n"3498 " 'external-contents': '/c'\n"3499 " }\n"3500 " ]\n"3501 " },\n"3502 " {\n"3503 " 'type': 'directory',\n"3504 " 'name': '/vdir',\n"3505 " 'contents': ["3506 " {\n"3507 " 'type': 'directory-remap',\n"3508 " 'name': 'dremap',\n"3509 " 'external-contents': '/b'\n"3510 " },\n"3511 " {\n"3512 " 'type': 'file',\n"3513 " 'name': 'missing',\n"3514 " 'external-contents': '/missing'\n"3515 " },\n"3516 " {\n"3517 " 'type': 'file',\n"3518 " 'name': 'vfile',\n"3519 " 'external-contents': '/c'\n"3520 " }]\n"3521 " }]\n"3522 "}");3523 3524 Dummy->addDirectory("/a");3525 Dummy->addRegularFile("/a/foo");3526 Dummy->addDirectory("/b");3527 Dummy->addRegularFile("/c");3528 Dummy->addRegularFile("/both/foo");3529 3530 auto Redirecting = vfs::RedirectingFileSystem::create(3531 std::move(YAML), nullptr, "", nullptr, Dummy);3532 3533 EXPECT_TRUE(Redirecting->exists("/dremap"));3534 EXPECT_FALSE(Redirecting->exists("/dmissing"));3535 EXPECT_FALSE(Redirecting->exists("/unknown"));3536 EXPECT_TRUE(Redirecting->exists("/both"));3537 EXPECT_TRUE(Redirecting->exists("/both/foo"));3538 EXPECT_TRUE(Redirecting->exists("/both/vfile"));3539 EXPECT_TRUE(Redirecting->exists("/vdir"));3540 EXPECT_TRUE(Redirecting->exists("/vdir/dremap"));3541 EXPECT_FALSE(Redirecting->exists("/vdir/missing"));3542 EXPECT_TRUE(Redirecting->exists("/vdir/vfile"));3543 EXPECT_FALSE(Redirecting->exists("/vdir/unknown"));3544}3545 3546TEST(RedirectingFileSystemTest, ExistsFallback) {3547 auto Dummy = makeIntrusiveRefCnt<NoStatusDummyFileSystem>();3548 auto YAML =3549 MemoryBuffer::getMemBuffer("{\n"3550 " 'version': 0,\n"3551 " 'redirecting-with': 'fallback',\n"3552 " 'roots': [\n"3553 " {\n"3554 " 'type': 'file',\n"3555 " 'name': '/fallback',\n"3556 " 'external-contents': '/missing',\n"3557 " },"3558 " ]\n"3559 "}");3560 3561 Dummy->addRegularFile("/fallback");3562 3563 auto Redirecting = vfs::RedirectingFileSystem::create(3564 std::move(YAML), nullptr, "", nullptr, Dummy);3565 3566 EXPECT_TRUE(Redirecting->exists("/fallback"));3567 EXPECT_FALSE(Redirecting->exists("/missing"));3568}3569 3570TEST(RedirectingFileSystemTest, ExistsRedirectOnly) {3571 auto Dummy = makeIntrusiveRefCnt<NoStatusDummyFileSystem>();3572 auto YAML =3573 MemoryBuffer::getMemBuffer("{\n"3574 " 'version': 0,\n"3575 " 'redirecting-with': 'redirect-only',\n"3576 " 'roots': [\n"3577 " {\n"3578 " 'type': 'file',\n"3579 " 'name': '/vfile',\n"3580 " 'external-contents': '/a',\n"3581 " },"3582 " ]\n"3583 "}");3584 3585 Dummy->addRegularFile("/a");3586 Dummy->addRegularFile("/b");3587 3588 auto Redirecting = vfs::RedirectingFileSystem::create(3589 std::move(YAML), nullptr, "", nullptr, Dummy);3590 3591 EXPECT_FALSE(Redirecting->exists("/a"));3592 EXPECT_FALSE(Redirecting->exists("/b"));3593 EXPECT_TRUE(Redirecting->exists("/vfile"));3594}3595 3596TEST(TracingFileSystemTest, TracingWorks) {3597 auto InMemoryFS = makeIntrusiveRefCnt<vfs::InMemoryFileSystem>();3598 auto TracingFS =3599 makeIntrusiveRefCnt<vfs::TracingFileSystem>(std::move(InMemoryFS));3600 3601 EXPECT_EQ(TracingFS->NumStatusCalls, 0u);3602 EXPECT_EQ(TracingFS->NumOpenFileForReadCalls, 0u);3603 EXPECT_EQ(TracingFS->NumDirBeginCalls, 0u);3604 EXPECT_EQ(TracingFS->NumGetRealPathCalls, 0u);3605 EXPECT_EQ(TracingFS->NumExistsCalls, 0u);3606 EXPECT_EQ(TracingFS->NumIsLocalCalls, 0u);3607 3608 (void)TracingFS->status("/foo");3609 EXPECT_EQ(TracingFS->NumStatusCalls, 1u);3610 EXPECT_EQ(TracingFS->NumOpenFileForReadCalls, 0u);3611 EXPECT_EQ(TracingFS->NumDirBeginCalls, 0u);3612 EXPECT_EQ(TracingFS->NumGetRealPathCalls, 0u);3613 EXPECT_EQ(TracingFS->NumExistsCalls, 0u);3614 EXPECT_EQ(TracingFS->NumIsLocalCalls, 0u);3615 3616 (void)TracingFS->openFileForRead("/foo");3617 EXPECT_EQ(TracingFS->NumStatusCalls, 1u);3618 EXPECT_EQ(TracingFS->NumOpenFileForReadCalls, 1u);3619 EXPECT_EQ(TracingFS->NumDirBeginCalls, 0u);3620 EXPECT_EQ(TracingFS->NumGetRealPathCalls, 0u);3621 EXPECT_EQ(TracingFS->NumExistsCalls, 0u);3622 EXPECT_EQ(TracingFS->NumIsLocalCalls, 0u);3623 3624 std::error_code EC;3625 (void)TracingFS->dir_begin("/foo", EC);3626 EXPECT_EQ(TracingFS->NumStatusCalls, 1u);3627 EXPECT_EQ(TracingFS->NumOpenFileForReadCalls, 1u);3628 EXPECT_EQ(TracingFS->NumDirBeginCalls, 1u);3629 EXPECT_EQ(TracingFS->NumGetRealPathCalls, 0u);3630 EXPECT_EQ(TracingFS->NumExistsCalls, 0u);3631 EXPECT_EQ(TracingFS->NumIsLocalCalls, 0u);3632 3633 SmallString<128> RealPath;3634 (void)TracingFS->getRealPath("/foo", RealPath);3635 EXPECT_EQ(TracingFS->NumStatusCalls, 1u);3636 EXPECT_EQ(TracingFS->NumOpenFileForReadCalls, 1u);3637 EXPECT_EQ(TracingFS->NumDirBeginCalls, 1u);3638 EXPECT_EQ(TracingFS->NumGetRealPathCalls, 1u);3639 EXPECT_EQ(TracingFS->NumExistsCalls, 0u);3640 EXPECT_EQ(TracingFS->NumIsLocalCalls, 0u);3641 3642 (void)TracingFS->exists("/foo");3643 EXPECT_EQ(TracingFS->NumStatusCalls, 1u);3644 EXPECT_EQ(TracingFS->NumOpenFileForReadCalls, 1u);3645 EXPECT_EQ(TracingFS->NumDirBeginCalls, 1u);3646 EXPECT_EQ(TracingFS->NumGetRealPathCalls, 1u);3647 EXPECT_EQ(TracingFS->NumExistsCalls, 1u);3648 EXPECT_EQ(TracingFS->NumIsLocalCalls, 0u);3649 3650 bool IsLocal;3651 (void)TracingFS->isLocal("/foo", IsLocal);3652 EXPECT_EQ(TracingFS->NumStatusCalls, 1u);3653 EXPECT_EQ(TracingFS->NumOpenFileForReadCalls, 1u);3654 EXPECT_EQ(TracingFS->NumDirBeginCalls, 1u);3655 EXPECT_EQ(TracingFS->NumGetRealPathCalls, 1u);3656 EXPECT_EQ(TracingFS->NumExistsCalls, 1u);3657 EXPECT_EQ(TracingFS->NumIsLocalCalls, 1u);3658}3659 3660TEST(TracingFileSystemTest, PrintOutput) {3661 auto InMemoryFS = makeIntrusiveRefCnt<vfs::InMemoryFileSystem>();3662 auto TracingFS =3663 makeIntrusiveRefCnt<vfs::TracingFileSystem>(std::move(InMemoryFS));3664 3665 (void)TracingFS->status("/foo");3666 3667 (void)TracingFS->openFileForRead("/foo");3668 (void)TracingFS->openFileForRead("/foo");3669 3670 std::error_code EC;3671 (void)TracingFS->dir_begin("/foo", EC);3672 (void)TracingFS->dir_begin("/foo", EC);3673 (void)TracingFS->dir_begin("/foo", EC);3674 3675 llvm::SmallString<128> RealPath;3676 (void)TracingFS->getRealPath("/foo", RealPath);3677 (void)TracingFS->getRealPath("/foo", RealPath);3678 (void)TracingFS->getRealPath("/foo", RealPath);3679 (void)TracingFS->getRealPath("/foo", RealPath);3680 3681 (void)TracingFS->exists("/foo");3682 (void)TracingFS->exists("/foo");3683 (void)TracingFS->exists("/foo");3684 (void)TracingFS->exists("/foo");3685 (void)TracingFS->exists("/foo");3686 3687 bool IsLocal;3688 (void)TracingFS->isLocal("/foo", IsLocal);3689 (void)TracingFS->isLocal("/foo", IsLocal);3690 (void)TracingFS->isLocal("/foo", IsLocal);3691 (void)TracingFS->isLocal("/foo", IsLocal);3692 (void)TracingFS->isLocal("/foo", IsLocal);3693 (void)TracingFS->isLocal("/foo", IsLocal);3694 3695 std::string Output;3696 llvm::raw_string_ostream OS(Output);3697 TracingFS->print(OS);3698 3699 ASSERT_EQ("TracingFileSystem\n"3700 "NumStatusCalls=1\n"3701 "NumOpenFileForReadCalls=2\n"3702 "NumDirBeginCalls=3\n"3703 "NumGetRealPathCalls=4\n"3704 "NumExistsCalls=5\n"3705 "NumIsLocalCalls=6\n"3706 " InMemoryFileSystem\n",3707 Output);3708}3709