brintos

brintos / llvm-project-archived public Read only

0
0
Text · 132.2 KiB · f52f25f Raw
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