Home | History | Annotate | Download | only in Basic
      1 //===- unittests/Basic/VirtualFileSystem.cpp ---------------- VFS tests ---===//
      2 //
      3 //                     The LLVM Compiler Infrastructure
      4 //
      5 // This file is distributed under the University of Illinois Open Source
      6 // License. See LICENSE.TXT for details.
      7 //
      8 //===----------------------------------------------------------------------===//
      9 
     10 #include "clang/Basic/VirtualFileSystem.h"
     11 #include "llvm/ADT/Triple.h"
     12 #include "llvm/Support/Errc.h"
     13 #include "llvm/Support/Host.h"
     14 #include "llvm/Support/MemoryBuffer.h"
     15 #include "llvm/Support/Path.h"
     16 #include "llvm/Support/SourceMgr.h"
     17 #include "gtest/gtest.h"
     18 #include <map>
     19 
     20 using namespace clang;
     21 using namespace llvm;
     22 using llvm::sys::fs::UniqueID;
     23 
     24 namespace {
     25 struct DummyFile : public vfs::File {
     26   vfs::Status S;
     27   explicit DummyFile(vfs::Status S) : S(S) {}
     28   llvm::ErrorOr<vfs::Status> status() override { return S; }
     29   llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
     30   getBuffer(const Twine &Name, int64_t FileSize, bool RequiresNullTerminator,
     31             bool IsVolatile) override {
     32     llvm_unreachable("unimplemented");
     33   }
     34   std::error_code close() override { return std::error_code(); }
     35 };
     36 
     37 class DummyFileSystem : public vfs::FileSystem {
     38   int FSID;   // used to produce UniqueIDs
     39   int FileID; // used to produce UniqueIDs
     40   std::map<std::string, vfs::Status> FilesAndDirs;
     41 
     42   static int getNextFSID() {
     43     static int Count = 0;
     44     return Count++;
     45   }
     46 
     47 public:
     48   DummyFileSystem() : FSID(getNextFSID()), FileID(0) {}
     49 
     50   ErrorOr<vfs::Status> status(const Twine &Path) override {
     51     std::map<std::string, vfs::Status>::iterator I =
     52         FilesAndDirs.find(Path.str());
     53     if (I == FilesAndDirs.end())
     54       return make_error_code(llvm::errc::no_such_file_or_directory);
     55     return I->second;
     56   }
     57   ErrorOr<std::unique_ptr<vfs::File>>
     58   openFileForRead(const Twine &Path) override {
     59     auto S = status(Path);
     60     if (S)
     61       return std::unique_ptr<vfs::File>(new DummyFile{*S});
     62     return S.getError();
     63   }
     64   llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const override {
     65     return std::string();
     66   }
     67   std::error_code setCurrentWorkingDirectory(const Twine &Path) override {
     68     return std::error_code();
     69   }
     70 
     71   struct DirIterImpl : public clang::vfs::detail::DirIterImpl {
     72     std::map<std::string, vfs::Status> &FilesAndDirs;
     73     std::map<std::string, vfs::Status>::iterator I;
     74     std::string Path;
     75     bool isInPath(StringRef S) {
     76       if (Path.size() < S.size() && S.find(Path) == 0) {
     77         auto LastSep = S.find_last_of('/');
     78         if (LastSep == Path.size() || LastSep == Path.size()-1)
     79           return true;
     80       }
     81       return false;
     82     }
     83     DirIterImpl(std::map<std::string, vfs::Status> &FilesAndDirs,
     84                 const Twine &_Path)
     85         : FilesAndDirs(FilesAndDirs), I(FilesAndDirs.begin()),
     86           Path(_Path.str()) {
     87       for ( ; I != FilesAndDirs.end(); ++I) {
     88         if (isInPath(I->first)) {
     89           CurrentEntry = I->second;
     90           break;
     91         }
     92       }
     93     }
     94     std::error_code increment() override {
     95       ++I;
     96       for ( ; I != FilesAndDirs.end(); ++I) {
     97         if (isInPath(I->first)) {
     98           CurrentEntry = I->second;
     99           break;
    100         }
    101       }
    102       if (I == FilesAndDirs.end())
    103         CurrentEntry = vfs::Status();
    104       return std::error_code();
    105     }
    106   };
    107 
    108   vfs::directory_iterator dir_begin(const Twine &Dir,
    109                                     std::error_code &EC) override {
    110     return vfs::directory_iterator(
    111         std::make_shared<DirIterImpl>(FilesAndDirs, Dir));
    112   }
    113 
    114   void addEntry(StringRef Path, const vfs::Status &Status) {
    115     FilesAndDirs[Path] = Status;
    116   }
    117 
    118   void addRegularFile(StringRef Path, sys::fs::perms Perms = sys::fs::all_all) {
    119     vfs::Status S(Path, UniqueID(FSID, FileID++), sys::TimeValue::now(), 0, 0,
    120                   1024, sys::fs::file_type::regular_file, Perms);
    121     addEntry(Path, S);
    122   }
    123 
    124   void addDirectory(StringRef Path, sys::fs::perms Perms = sys::fs::all_all) {
    125     vfs::Status S(Path, UniqueID(FSID, FileID++), sys::TimeValue::now(), 0, 0,
    126                   0, sys::fs::file_type::directory_file, Perms);
    127     addEntry(Path, S);
    128   }
    129 
    130   void addSymlink(StringRef Path) {
    131     vfs::Status S(Path, UniqueID(FSID, FileID++), sys::TimeValue::now(), 0, 0,
    132                   0, sys::fs::file_type::symlink_file, sys::fs::all_all);
    133     addEntry(Path, S);
    134   }
    135 };
    136 } // end anonymous namespace
    137 
    138 TEST(VirtualFileSystemTest, StatusQueries) {
    139   IntrusiveRefCntPtr<DummyFileSystem> D(new DummyFileSystem());
    140   ErrorOr<vfs::Status> Status((std::error_code()));
    141 
    142   D->addRegularFile("/foo");
    143   Status = D->status("/foo");
    144   ASSERT_FALSE(Status.getError());
    145   EXPECT_TRUE(Status->isStatusKnown());
    146   EXPECT_FALSE(Status->isDirectory());
    147   EXPECT_TRUE(Status->isRegularFile());
    148   EXPECT_FALSE(Status->isSymlink());
    149   EXPECT_FALSE(Status->isOther());
    150   EXPECT_TRUE(Status->exists());
    151 
    152   D->addDirectory("/bar");
    153   Status = D->status("/bar");
    154   ASSERT_FALSE(Status.getError());
    155   EXPECT_TRUE(Status->isStatusKnown());
    156   EXPECT_TRUE(Status->isDirectory());
    157   EXPECT_FALSE(Status->isRegularFile());
    158   EXPECT_FALSE(Status->isSymlink());
    159   EXPECT_FALSE(Status->isOther());
    160   EXPECT_TRUE(Status->exists());
    161 
    162   D->addSymlink("/baz");
    163   Status = D->status("/baz");
    164   ASSERT_FALSE(Status.getError());
    165   EXPECT_TRUE(Status->isStatusKnown());
    166   EXPECT_FALSE(Status->isDirectory());
    167   EXPECT_FALSE(Status->isRegularFile());
    168   EXPECT_TRUE(Status->isSymlink());
    169   EXPECT_FALSE(Status->isOther());
    170   EXPECT_TRUE(Status->exists());
    171 
    172   EXPECT_TRUE(Status->equivalent(*Status));
    173   ErrorOr<vfs::Status> Status2 = D->status("/foo");
    174   ASSERT_FALSE(Status2.getError());
    175   EXPECT_FALSE(Status->equivalent(*Status2));
    176 }
    177 
    178 TEST(VirtualFileSystemTest, BaseOnlyOverlay) {
    179   IntrusiveRefCntPtr<DummyFileSystem> D(new DummyFileSystem());
    180   ErrorOr<vfs::Status> Status((std::error_code()));
    181   EXPECT_FALSE(Status = D->status("/foo"));
    182 
    183   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(new vfs::OverlayFileSystem(D));
    184   EXPECT_FALSE(Status = O->status("/foo"));
    185 
    186   D->addRegularFile("/foo");
    187   Status = D->status("/foo");
    188   EXPECT_FALSE(Status.getError());
    189 
    190   ErrorOr<vfs::Status> Status2((std::error_code()));
    191   Status2 = O->status("/foo");
    192   EXPECT_FALSE(Status2.getError());
    193   EXPECT_TRUE(Status->equivalent(*Status2));
    194 }
    195 
    196 TEST(VirtualFileSystemTest, OverlayFiles) {
    197   IntrusiveRefCntPtr<DummyFileSystem> Base(new DummyFileSystem());
    198   IntrusiveRefCntPtr<DummyFileSystem> Middle(new DummyFileSystem());
    199   IntrusiveRefCntPtr<DummyFileSystem> Top(new DummyFileSystem());
    200   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
    201       new vfs::OverlayFileSystem(Base));
    202   O->pushOverlay(Middle);
    203   O->pushOverlay(Top);
    204 
    205   ErrorOr<vfs::Status> Status1((std::error_code())),
    206       Status2((std::error_code())), Status3((std::error_code())),
    207       StatusB((std::error_code())), StatusM((std::error_code())),
    208       StatusT((std::error_code()));
    209 
    210   Base->addRegularFile("/foo");
    211   StatusB = Base->status("/foo");
    212   ASSERT_FALSE(StatusB.getError());
    213   Status1 = O->status("/foo");
    214   ASSERT_FALSE(Status1.getError());
    215   Middle->addRegularFile("/foo");
    216   StatusM = Middle->status("/foo");
    217   ASSERT_FALSE(StatusM.getError());
    218   Status2 = O->status("/foo");
    219   ASSERT_FALSE(Status2.getError());
    220   Top->addRegularFile("/foo");
    221   StatusT = Top->status("/foo");
    222   ASSERT_FALSE(StatusT.getError());
    223   Status3 = O->status("/foo");
    224   ASSERT_FALSE(Status3.getError());
    225 
    226   EXPECT_TRUE(Status1->equivalent(*StatusB));
    227   EXPECT_TRUE(Status2->equivalent(*StatusM));
    228   EXPECT_TRUE(Status3->equivalent(*StatusT));
    229 
    230   EXPECT_FALSE(Status1->equivalent(*Status2));
    231   EXPECT_FALSE(Status2->equivalent(*Status3));
    232   EXPECT_FALSE(Status1->equivalent(*Status3));
    233 }
    234 
    235 TEST(VirtualFileSystemTest, OverlayDirsNonMerged) {
    236   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
    237   IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem());
    238   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
    239       new vfs::OverlayFileSystem(Lower));
    240   O->pushOverlay(Upper);
    241 
    242   Lower->addDirectory("/lower-only");
    243   Upper->addDirectory("/upper-only");
    244 
    245   // non-merged paths should be the same
    246   ErrorOr<vfs::Status> Status1 = Lower->status("/lower-only");
    247   ASSERT_FALSE(Status1.getError());
    248   ErrorOr<vfs::Status> Status2 = O->status("/lower-only");
    249   ASSERT_FALSE(Status2.getError());
    250   EXPECT_TRUE(Status1->equivalent(*Status2));
    251 
    252   Status1 = Upper->status("/upper-only");
    253   ASSERT_FALSE(Status1.getError());
    254   Status2 = O->status("/upper-only");
    255   ASSERT_FALSE(Status2.getError());
    256   EXPECT_TRUE(Status1->equivalent(*Status2));
    257 }
    258 
    259 TEST(VirtualFileSystemTest, MergedDirPermissions) {
    260   // merged directories get the permissions of the upper dir
    261   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
    262   IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem());
    263   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
    264       new vfs::OverlayFileSystem(Lower));
    265   O->pushOverlay(Upper);
    266 
    267   ErrorOr<vfs::Status> Status((std::error_code()));
    268   Lower->addDirectory("/both", sys::fs::owner_read);
    269   Upper->addDirectory("/both", sys::fs::owner_all | sys::fs::group_read);
    270   Status = O->status("/both");
    271   ASSERT_FALSE(Status.getError());
    272   EXPECT_EQ(0740, Status->getPermissions());
    273 
    274   // permissions (as usual) are not recursively applied
    275   Lower->addRegularFile("/both/foo", sys::fs::owner_read);
    276   Upper->addRegularFile("/both/bar", sys::fs::owner_write);
    277   Status = O->status("/both/foo");
    278   ASSERT_FALSE( Status.getError());
    279   EXPECT_EQ(0400, Status->getPermissions());
    280   Status = O->status("/both/bar");
    281   ASSERT_FALSE(Status.getError());
    282   EXPECT_EQ(0200, Status->getPermissions());
    283 }
    284 
    285 namespace {
    286 struct ScopedDir {
    287   SmallString<128> Path;
    288   ScopedDir(const Twine &Name, bool Unique=false) {
    289     std::error_code EC;
    290     if (Unique) {
    291       EC =  llvm::sys::fs::createUniqueDirectory(Name, Path);
    292     } else {
    293       Path = Name.str();
    294       EC = llvm::sys::fs::create_directory(Twine(Path));
    295     }
    296     if (EC)
    297       Path = "";
    298     EXPECT_FALSE(EC);
    299   }
    300   ~ScopedDir() {
    301     if (Path != "")
    302       EXPECT_FALSE(llvm::sys::fs::remove(Path.str()));
    303   }
    304   operator StringRef() { return Path.str(); }
    305 };
    306 } // end anonymous namespace
    307 
    308 TEST(VirtualFileSystemTest, BasicRealFSIteration) {
    309   ScopedDir TestDirectory("virtual-file-system-test", /*Unique*/true);
    310   IntrusiveRefCntPtr<vfs::FileSystem> FS = vfs::getRealFileSystem();
    311 
    312   std::error_code EC;
    313   vfs::directory_iterator I = FS->dir_begin(Twine(TestDirectory), EC);
    314   ASSERT_FALSE(EC);
    315   EXPECT_EQ(vfs::directory_iterator(), I); // empty directory is empty
    316 
    317   ScopedDir _a(TestDirectory+"/a");
    318   ScopedDir _ab(TestDirectory+"/a/b");
    319   ScopedDir _c(TestDirectory+"/c");
    320   ScopedDir _cd(TestDirectory+"/c/d");
    321 
    322   I = FS->dir_begin(Twine(TestDirectory), EC);
    323   ASSERT_FALSE(EC);
    324   ASSERT_NE(vfs::directory_iterator(), I);
    325   // Check either a or c, since we can't rely on the iteration order.
    326   EXPECT_TRUE(I->getName().endswith("a") || I->getName().endswith("c"));
    327   I.increment(EC);
    328   ASSERT_FALSE(EC);
    329   ASSERT_NE(vfs::directory_iterator(), I);
    330   EXPECT_TRUE(I->getName().endswith("a") || I->getName().endswith("c"));
    331   I.increment(EC);
    332   EXPECT_EQ(vfs::directory_iterator(), I);
    333 }
    334 
    335 TEST(VirtualFileSystemTest, BasicRealFSRecursiveIteration) {
    336   ScopedDir TestDirectory("virtual-file-system-test", /*Unique*/true);
    337   IntrusiveRefCntPtr<vfs::FileSystem> FS = vfs::getRealFileSystem();
    338 
    339   std::error_code EC;
    340   auto I = vfs::recursive_directory_iterator(*FS, Twine(TestDirectory), EC);
    341   ASSERT_FALSE(EC);
    342   EXPECT_EQ(vfs::recursive_directory_iterator(), I); // empty directory is empty
    343 
    344   ScopedDir _a(TestDirectory+"/a");
    345   ScopedDir _ab(TestDirectory+"/a/b");
    346   ScopedDir _c(TestDirectory+"/c");
    347   ScopedDir _cd(TestDirectory+"/c/d");
    348 
    349   I = vfs::recursive_directory_iterator(*FS, Twine(TestDirectory), EC);
    350   ASSERT_FALSE(EC);
    351   ASSERT_NE(vfs::recursive_directory_iterator(), I);
    352 
    353   std::vector<std::string> Contents;
    354   for (auto E = vfs::recursive_directory_iterator(); !EC && I != E;
    355        I.increment(EC)) {
    356     Contents.push_back(I->getName());
    357   }
    358 
    359   // Check contents, which may be in any order
    360   EXPECT_EQ(4U, Contents.size());
    361   int Counts[4] = { 0, 0, 0, 0 };
    362   for (const std::string &Name : Contents) {
    363     ASSERT_FALSE(Name.empty());
    364     int Index = Name[Name.size()-1] - 'a';
    365     ASSERT_TRUE(Index >= 0 && Index < 4);
    366     Counts[Index]++;
    367   }
    368   EXPECT_EQ(1, Counts[0]); // a
    369   EXPECT_EQ(1, Counts[1]); // b
    370   EXPECT_EQ(1, Counts[2]); // c
    371   EXPECT_EQ(1, Counts[3]); // d
    372 }
    373 
    374 template <typename DirIter>
    375 static void checkContents(DirIter I, ArrayRef<StringRef> ExpectedOut) {
    376   std::error_code EC;
    377   SmallVector<StringRef, 4> Expected(ExpectedOut.begin(), ExpectedOut.end());
    378   SmallVector<std::string, 4> InputToCheck;
    379 
    380   // Do not rely on iteration order to check for contents, sort both
    381   // content vectors before comparison.
    382   for (DirIter E; !EC && I != E; I.increment(EC))
    383     InputToCheck.push_back(I->getName());
    384 
    385   std::sort(InputToCheck.begin(), InputToCheck.end());
    386   std::sort(Expected.begin(), Expected.end());
    387   EXPECT_EQ(InputToCheck.size(), Expected.size());
    388 
    389   unsigned LastElt = std::min(InputToCheck.size(), Expected.size());
    390   for (unsigned Idx = 0; Idx != LastElt; ++Idx)
    391     EXPECT_EQ(StringRef(InputToCheck[Idx]), Expected[Idx]);
    392 }
    393 
    394 TEST(VirtualFileSystemTest, OverlayIteration) {
    395   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
    396   IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem());
    397   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
    398       new vfs::OverlayFileSystem(Lower));
    399   O->pushOverlay(Upper);
    400 
    401   std::error_code EC;
    402   checkContents(O->dir_begin("/", EC), ArrayRef<StringRef>());
    403 
    404   Lower->addRegularFile("/file1");
    405   checkContents(O->dir_begin("/", EC), ArrayRef<StringRef>("/file1"));
    406 
    407   Upper->addRegularFile("/file2");
    408   checkContents(O->dir_begin("/", EC), {"/file2", "/file1"});
    409 
    410   Lower->addDirectory("/dir1");
    411   Lower->addRegularFile("/dir1/foo");
    412   Upper->addDirectory("/dir2");
    413   Upper->addRegularFile("/dir2/foo");
    414   checkContents(O->dir_begin("/dir2", EC), ArrayRef<StringRef>("/dir2/foo"));
    415   checkContents(O->dir_begin("/", EC), {"/dir2", "/file2", "/dir1", "/file1"});
    416 }
    417 
    418 TEST(VirtualFileSystemTest, OverlayRecursiveIteration) {
    419   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
    420   IntrusiveRefCntPtr<DummyFileSystem> Middle(new DummyFileSystem());
    421   IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem());
    422   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
    423       new vfs::OverlayFileSystem(Lower));
    424   O->pushOverlay(Middle);
    425   O->pushOverlay(Upper);
    426 
    427   std::error_code EC;
    428   checkContents(vfs::recursive_directory_iterator(*O, "/", EC),
    429                 ArrayRef<StringRef>());
    430 
    431   Lower->addRegularFile("/file1");
    432   checkContents(vfs::recursive_directory_iterator(*O, "/", EC),
    433                 ArrayRef<StringRef>("/file1"));
    434 
    435   Upper->addDirectory("/dir");
    436   Upper->addRegularFile("/dir/file2");
    437   checkContents(vfs::recursive_directory_iterator(*O, "/", EC),
    438                 {"/dir", "/dir/file2", "/file1"});
    439 
    440   Lower->addDirectory("/dir1");
    441   Lower->addRegularFile("/dir1/foo");
    442   Lower->addDirectory("/dir1/a");
    443   Lower->addRegularFile("/dir1/a/b");
    444   Middle->addDirectory("/a");
    445   Middle->addDirectory("/a/b");
    446   Middle->addDirectory("/a/b/c");
    447   Middle->addRegularFile("/a/b/c/d");
    448   Middle->addRegularFile("/hiddenByUp");
    449   Upper->addDirectory("/dir2");
    450   Upper->addRegularFile("/dir2/foo");
    451   Upper->addRegularFile("/hiddenByUp");
    452   checkContents(vfs::recursive_directory_iterator(*O, "/dir2", EC),
    453                 ArrayRef<StringRef>("/dir2/foo"));
    454   checkContents(vfs::recursive_directory_iterator(*O, "/", EC),
    455                 {"/dir", "/dir/file2", "/dir2", "/dir2/foo", "/hiddenByUp",
    456                  "/a", "/a/b", "/a/b/c", "/a/b/c/d", "/dir1", "/dir1/a",
    457                  "/dir1/a/b", "/dir1/foo", "/file1"});
    458 }
    459 
    460 TEST(VirtualFileSystemTest, ThreeLevelIteration) {
    461   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
    462   IntrusiveRefCntPtr<DummyFileSystem> Middle(new DummyFileSystem());
    463   IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem());
    464   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
    465       new vfs::OverlayFileSystem(Lower));
    466   O->pushOverlay(Middle);
    467   O->pushOverlay(Upper);
    468 
    469   std::error_code EC;
    470   checkContents(O->dir_begin("/", EC), ArrayRef<StringRef>());
    471 
    472   Middle->addRegularFile("/file2");
    473   checkContents(O->dir_begin("/", EC), ArrayRef<StringRef>("/file2"));
    474 
    475   Lower->addRegularFile("/file1");
    476   Upper->addRegularFile("/file3");
    477   checkContents(O->dir_begin("/", EC), {"/file3", "/file2", "/file1"});
    478 }
    479 
    480 TEST(VirtualFileSystemTest, HiddenInIteration) {
    481   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
    482   IntrusiveRefCntPtr<DummyFileSystem> Middle(new DummyFileSystem());
    483   IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem());
    484   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
    485       new vfs::OverlayFileSystem(Lower));
    486   O->pushOverlay(Middle);
    487   O->pushOverlay(Upper);
    488 
    489   std::error_code EC;
    490   Lower->addRegularFile("/onlyInLow", sys::fs::owner_read);
    491   Lower->addRegularFile("/hiddenByMid", sys::fs::owner_read);
    492   Lower->addRegularFile("/hiddenByUp", sys::fs::owner_read);
    493   Middle->addRegularFile("/onlyInMid", sys::fs::owner_write);
    494   Middle->addRegularFile("/hiddenByMid", sys::fs::owner_write);
    495   Middle->addRegularFile("/hiddenByUp", sys::fs::owner_write);
    496   Upper->addRegularFile("/onlyInUp", sys::fs::owner_all);
    497   Upper->addRegularFile("/hiddenByUp", sys::fs::owner_all);
    498   checkContents(
    499       O->dir_begin("/", EC),
    500       {"/hiddenByUp", "/onlyInUp", "/hiddenByMid", "/onlyInMid", "/onlyInLow"});
    501 
    502   // Make sure we get the top-most entry
    503   {
    504     std::error_code EC;
    505     vfs::directory_iterator I = O->dir_begin("/", EC), E;
    506     for ( ; !EC && I != E; I.increment(EC))
    507       if (I->getName() == "/hiddenByUp")
    508         break;
    509     ASSERT_NE(E, I);
    510     EXPECT_EQ(sys::fs::owner_all, I->getPermissions());
    511   }
    512   {
    513     std::error_code EC;
    514     vfs::directory_iterator I = O->dir_begin("/", EC), E;
    515     for ( ; !EC && I != E; I.increment(EC))
    516       if (I->getName() == "/hiddenByMid")
    517         break;
    518     ASSERT_NE(E, I);
    519     EXPECT_EQ(sys::fs::owner_write, I->getPermissions());
    520   }
    521 }
    522 
    523 class InMemoryFileSystemTest : public ::testing::Test {
    524 protected:
    525   clang::vfs::InMemoryFileSystem FS;
    526   clang::vfs::InMemoryFileSystem NormalizedFS;
    527 
    528   InMemoryFileSystemTest()
    529       : FS(/*UseNormalizedPaths=*/false),
    530         NormalizedFS(/*UseNormalizedPaths=*/true) {}
    531 };
    532 
    533 TEST_F(InMemoryFileSystemTest, IsEmpty) {
    534   auto Stat = FS.status("/a");
    535   ASSERT_EQ(Stat.getError(),errc::no_such_file_or_directory) << FS.toString();
    536   Stat = FS.status("/");
    537   ASSERT_EQ(Stat.getError(), errc::no_such_file_or_directory) << FS.toString();
    538 }
    539 
    540 TEST_F(InMemoryFileSystemTest, WindowsPath) {
    541   FS.addFile("c:/windows/system128/foo.cpp", 0, MemoryBuffer::getMemBuffer(""));
    542   auto Stat = FS.status("c:");
    543 #if !defined(_WIN32)
    544   ASSERT_FALSE(Stat.getError()) << Stat.getError() << FS.toString();
    545 #endif
    546   Stat = FS.status("c:/windows/system128/foo.cpp");
    547   ASSERT_FALSE(Stat.getError()) << Stat.getError() << FS.toString();
    548   FS.addFile("d:/windows/foo.cpp", 0, MemoryBuffer::getMemBuffer(""));
    549   Stat = FS.status("d:/windows/foo.cpp");
    550   ASSERT_FALSE(Stat.getError()) << Stat.getError() << FS.toString();
    551 }
    552 
    553 TEST_F(InMemoryFileSystemTest, OverlayFile) {
    554   FS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a"));
    555   NormalizedFS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a"));
    556   auto Stat = FS.status("/");
    557   ASSERT_FALSE(Stat.getError()) << Stat.getError() << FS.toString();
    558   Stat = FS.status("/.");
    559   ASSERT_FALSE(Stat);
    560   Stat = NormalizedFS.status("/.");
    561   ASSERT_FALSE(Stat.getError()) << Stat.getError() << FS.toString();
    562   Stat = FS.status("/a");
    563   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
    564   ASSERT_EQ("/a", Stat->getName());
    565 }
    566 
    567 TEST_F(InMemoryFileSystemTest, OverlayFileNoOwn) {
    568   auto Buf = MemoryBuffer::getMemBuffer("a");
    569   FS.addFileNoOwn("/a", 0, Buf.get());
    570   auto Stat = FS.status("/a");
    571   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
    572   ASSERT_EQ("/a", Stat->getName());
    573 }
    574 
    575 TEST_F(InMemoryFileSystemTest, OpenFileForRead) {
    576   FS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a"));
    577   FS.addFile("././c", 0, MemoryBuffer::getMemBuffer("c"));
    578   FS.addFile("./d/../d", 0, MemoryBuffer::getMemBuffer("d"));
    579   NormalizedFS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a"));
    580   NormalizedFS.addFile("././c", 0, MemoryBuffer::getMemBuffer("c"));
    581   NormalizedFS.addFile("./d/../d", 0, MemoryBuffer::getMemBuffer("d"));
    582   auto File = FS.openFileForRead("/a");
    583   ASSERT_EQ("a", (*(*File)->getBuffer("ignored"))->getBuffer());
    584   File = FS.openFileForRead("/a"); // Open again.
    585   ASSERT_EQ("a", (*(*File)->getBuffer("ignored"))->getBuffer());
    586   File = NormalizedFS.openFileForRead("/././a"); // Open again.
    587   ASSERT_EQ("a", (*(*File)->getBuffer("ignored"))->getBuffer());
    588   File = FS.openFileForRead("/");
    589   ASSERT_EQ(File.getError(), errc::invalid_argument) << FS.toString();
    590   File = FS.openFileForRead("/b");
    591   ASSERT_EQ(File.getError(), errc::no_such_file_or_directory) << FS.toString();
    592   File = FS.openFileForRead("./c");
    593   ASSERT_FALSE(File);
    594   File = FS.openFileForRead("e/../d");
    595   ASSERT_FALSE(File);
    596   File = NormalizedFS.openFileForRead("./c");
    597   ASSERT_EQ("c", (*(*File)->getBuffer("ignored"))->getBuffer());
    598   File = NormalizedFS.openFileForRead("e/../d");
    599   ASSERT_EQ("d", (*(*File)->getBuffer("ignored"))->getBuffer());
    600 }
    601 
    602 TEST_F(InMemoryFileSystemTest, DuplicatedFile) {
    603   ASSERT_TRUE(FS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a")));
    604   ASSERT_FALSE(FS.addFile("/a/b", 0, MemoryBuffer::getMemBuffer("a")));
    605   ASSERT_TRUE(FS.addFile("/a", 0, MemoryBuffer::getMemBuffer("a")));
    606   ASSERT_FALSE(FS.addFile("/a", 0, MemoryBuffer::getMemBuffer("b")));
    607 }
    608 
    609 TEST_F(InMemoryFileSystemTest, DirectoryIteration) {
    610   FS.addFile("/a", 0, MemoryBuffer::getMemBuffer(""));
    611   FS.addFile("/b/c", 0, MemoryBuffer::getMemBuffer(""));
    612 
    613   std::error_code EC;
    614   vfs::directory_iterator I = FS.dir_begin("/", EC);
    615   ASSERT_FALSE(EC);
    616   ASSERT_EQ("/a", I->getName());
    617   I.increment(EC);
    618   ASSERT_FALSE(EC);
    619   ASSERT_EQ("/b", I->getName());
    620   I.increment(EC);
    621   ASSERT_FALSE(EC);
    622   ASSERT_EQ(vfs::directory_iterator(), I);
    623 
    624   I = FS.dir_begin("/b", EC);
    625   ASSERT_FALSE(EC);
    626   ASSERT_EQ("/b/c", I->getName());
    627   I.increment(EC);
    628   ASSERT_FALSE(EC);
    629   ASSERT_EQ(vfs::directory_iterator(), I);
    630 }
    631 
    632 TEST_F(InMemoryFileSystemTest, WorkingDirectory) {
    633   FS.setCurrentWorkingDirectory("/b");
    634   FS.addFile("c", 0, MemoryBuffer::getMemBuffer(""));
    635 
    636   auto Stat = FS.status("/b/c");
    637   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
    638   ASSERT_EQ("c", Stat->getName());
    639   ASSERT_EQ("/b", *FS.getCurrentWorkingDirectory());
    640 
    641   Stat = FS.status("c");
    642   ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString();
    643 
    644   auto ReplaceBackslashes = [](std::string S) {
    645     std::replace(S.begin(), S.end(), '\\', '/');
    646     return S;
    647   };
    648   NormalizedFS.setCurrentWorkingDirectory("/b/c");
    649   NormalizedFS.setCurrentWorkingDirectory(".");
    650   ASSERT_EQ("/b/c", ReplaceBackslashes(
    651                         NormalizedFS.getCurrentWorkingDirectory().get()));
    652   NormalizedFS.setCurrentWorkingDirectory("..");
    653   ASSERT_EQ("/b", ReplaceBackslashes(
    654                       NormalizedFS.getCurrentWorkingDirectory().get()));
    655 }
    656 
    657 // NOTE: in the tests below, we use '//root/' as our root directory, since it is
    658 // a legal *absolute* path on Windows as well as *nix.
    659 class VFSFromYAMLTest : public ::testing::Test {
    660 public:
    661   int NumDiagnostics;
    662 
    663   void SetUp() override { NumDiagnostics = 0; }
    664 
    665   static void CountingDiagHandler(const SMDiagnostic &, void *Context) {
    666     VFSFromYAMLTest *Test = static_cast<VFSFromYAMLTest *>(Context);
    667     ++Test->NumDiagnostics;
    668   }
    669 
    670   IntrusiveRefCntPtr<vfs::FileSystem>
    671   getFromYAMLRawString(StringRef Content,
    672                        IntrusiveRefCntPtr<vfs::FileSystem> ExternalFS) {
    673     std::unique_ptr<MemoryBuffer> Buffer = MemoryBuffer::getMemBuffer(Content);
    674     return getVFSFromYAML(std::move(Buffer), CountingDiagHandler, "", this,
    675                           ExternalFS);
    676   }
    677 
    678   IntrusiveRefCntPtr<vfs::FileSystem> getFromYAMLString(
    679       StringRef Content,
    680       IntrusiveRefCntPtr<vfs::FileSystem> ExternalFS = new DummyFileSystem()) {
    681     std::string VersionPlusContent("{\n  'version':0,\n");
    682     VersionPlusContent += Content.slice(Content.find('{') + 1, StringRef::npos);
    683     return getFromYAMLRawString(VersionPlusContent, ExternalFS);
    684   }
    685 
    686   // This is intended as a "XFAIL" for windows hosts.
    687   bool supportsSameDirMultipleYAMLEntries() {
    688     Triple Host(Triple::normalize(sys::getProcessTriple()));
    689     return !Host.isOSWindows();
    690   }
    691 };
    692 
    693 TEST_F(VFSFromYAMLTest, BasicVFSFromYAML) {
    694   IntrusiveRefCntPtr<vfs::FileSystem> FS;
    695   FS = getFromYAMLString("");
    696   EXPECT_EQ(nullptr, FS.get());
    697   FS = getFromYAMLString("[]");
    698   EXPECT_EQ(nullptr, FS.get());
    699   FS = getFromYAMLString("'string'");
    700   EXPECT_EQ(nullptr, FS.get());
    701   EXPECT_EQ(3, NumDiagnostics);
    702 }
    703 
    704 TEST_F(VFSFromYAMLTest, MappedFiles) {
    705   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
    706   Lower->addRegularFile("//root/foo/bar/a");
    707   IntrusiveRefCntPtr<vfs::FileSystem> FS =
    708       getFromYAMLString("{ 'roots': [\n"
    709                         "{\n"
    710                         "  'type': 'directory',\n"
    711                         "  'name': '//root/',\n"
    712                         "  'contents': [ {\n"
    713                         "                  'type': 'file',\n"
    714                         "                  'name': 'file1',\n"
    715                         "                  'external-contents': '//root/foo/bar/a'\n"
    716                         "                },\n"
    717                         "                {\n"
    718                         "                  'type': 'file',\n"
    719                         "                  'name': 'file2',\n"
    720                         "                  'external-contents': '//root/foo/b'\n"
    721                         "                }\n"
    722                         "              ]\n"
    723                         "}\n"
    724                         "]\n"
    725                         "}",
    726                         Lower);
    727   ASSERT_TRUE(FS.get() != nullptr);
    728 
    729   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
    730       new vfs::OverlayFileSystem(Lower));
    731   O->pushOverlay(FS);
    732 
    733   // file
    734   ErrorOr<vfs::Status> S = O->status("//root/file1");
    735   ASSERT_FALSE(S.getError());
    736   EXPECT_EQ("//root/foo/bar/a", S->getName());
    737   EXPECT_TRUE(S->IsVFSMapped);
    738 
    739   ErrorOr<vfs::Status> SLower = O->status("//root/foo/bar/a");
    740   EXPECT_EQ("//root/foo/bar/a", SLower->getName());
    741   EXPECT_TRUE(S->equivalent(*SLower));
    742   EXPECT_FALSE(SLower->IsVFSMapped);
    743 
    744   // file after opening
    745   auto OpenedF = O->openFileForRead("//root/file1");
    746   ASSERT_FALSE(OpenedF.getError());
    747   auto OpenedS = (*OpenedF)->status();
    748   ASSERT_FALSE(OpenedS.getError());
    749   EXPECT_EQ("//root/foo/bar/a", OpenedS->getName());
    750   EXPECT_TRUE(OpenedS->IsVFSMapped);
    751 
    752   // directory
    753   S = O->status("//root/");
    754   ASSERT_FALSE(S.getError());
    755   EXPECT_TRUE(S->isDirectory());
    756   EXPECT_TRUE(S->equivalent(*O->status("//root/"))); // non-volatile UniqueID
    757 
    758   // broken mapping
    759   EXPECT_EQ(O->status("//root/file2").getError(),
    760             llvm::errc::no_such_file_or_directory);
    761   EXPECT_EQ(0, NumDiagnostics);
    762 }
    763 
    764 TEST_F(VFSFromYAMLTest, CaseInsensitive) {
    765   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
    766   Lower->addRegularFile("//root/foo/bar/a");
    767   IntrusiveRefCntPtr<vfs::FileSystem> FS =
    768       getFromYAMLString("{ 'case-sensitive': 'false',\n"
    769                         "  'roots': [\n"
    770                         "{\n"
    771                         "  'type': 'directory',\n"
    772                         "  'name': '//root/',\n"
    773                         "  'contents': [ {\n"
    774                         "                  'type': 'file',\n"
    775                         "                  'name': 'XX',\n"
    776                         "                  'external-contents': '//root/foo/bar/a'\n"
    777                         "                }\n"
    778                         "              ]\n"
    779                         "}]}",
    780                         Lower);
    781   ASSERT_TRUE(FS.get() != nullptr);
    782 
    783   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
    784       new vfs::OverlayFileSystem(Lower));
    785   O->pushOverlay(FS);
    786 
    787   ErrorOr<vfs::Status> S = O->status("//root/XX");
    788   ASSERT_FALSE(S.getError());
    789 
    790   ErrorOr<vfs::Status> SS = O->status("//root/xx");
    791   ASSERT_FALSE(SS.getError());
    792   EXPECT_TRUE(S->equivalent(*SS));
    793   SS = O->status("//root/xX");
    794   EXPECT_TRUE(S->equivalent(*SS));
    795   SS = O->status("//root/Xx");
    796   EXPECT_TRUE(S->equivalent(*SS));
    797   EXPECT_EQ(0, NumDiagnostics);
    798 }
    799 
    800 TEST_F(VFSFromYAMLTest, CaseSensitive) {
    801   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
    802   Lower->addRegularFile("//root/foo/bar/a");
    803   IntrusiveRefCntPtr<vfs::FileSystem> FS =
    804       getFromYAMLString("{ 'case-sensitive': 'true',\n"
    805                         "  'roots': [\n"
    806                         "{\n"
    807                         "  'type': 'directory',\n"
    808                         "  'name': '//root/',\n"
    809                         "  'contents': [ {\n"
    810                         "                  'type': 'file',\n"
    811                         "                  'name': 'XX',\n"
    812                         "                  'external-contents': '//root/foo/bar/a'\n"
    813                         "                }\n"
    814                         "              ]\n"
    815                         "}]}",
    816                         Lower);
    817   ASSERT_TRUE(FS.get() != nullptr);
    818 
    819   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
    820       new vfs::OverlayFileSystem(Lower));
    821   O->pushOverlay(FS);
    822 
    823   ErrorOr<vfs::Status> SS = O->status("//root/xx");
    824   EXPECT_EQ(SS.getError(), llvm::errc::no_such_file_or_directory);
    825   SS = O->status("//root/xX");
    826   EXPECT_EQ(SS.getError(), llvm::errc::no_such_file_or_directory);
    827   SS = O->status("//root/Xx");
    828   EXPECT_EQ(SS.getError(), llvm::errc::no_such_file_or_directory);
    829   EXPECT_EQ(0, NumDiagnostics);
    830 }
    831 
    832 TEST_F(VFSFromYAMLTest, IllegalVFSFile) {
    833   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
    834 
    835   // invalid YAML at top-level
    836   IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString("{]", Lower);
    837   EXPECT_EQ(nullptr, FS.get());
    838   // invalid YAML in roots
    839   FS = getFromYAMLString("{ 'roots':[}", Lower);
    840   // invalid YAML in directory
    841   FS = getFromYAMLString(
    842       "{ 'roots':[ { 'name': 'foo', 'type': 'directory', 'contents': [}",
    843       Lower);
    844   EXPECT_EQ(nullptr, FS.get());
    845 
    846   // invalid configuration
    847   FS = getFromYAMLString("{ 'knobular': 'true', 'roots':[] }", Lower);
    848   EXPECT_EQ(nullptr, FS.get());
    849   FS = getFromYAMLString("{ 'case-sensitive': 'maybe', 'roots':[] }", Lower);
    850   EXPECT_EQ(nullptr, FS.get());
    851 
    852   // invalid roots
    853   FS = getFromYAMLString("{ 'roots':'' }", Lower);
    854   EXPECT_EQ(nullptr, FS.get());
    855   FS = getFromYAMLString("{ 'roots':{} }", Lower);
    856   EXPECT_EQ(nullptr, FS.get());
    857 
    858   // invalid entries
    859   FS = getFromYAMLString(
    860       "{ 'roots':[ { 'type': 'other', 'name': 'me', 'contents': '' }", Lower);
    861   EXPECT_EQ(nullptr, FS.get());
    862   FS = getFromYAMLString("{ 'roots':[ { 'type': 'file', 'name': [], "
    863                          "'external-contents': 'other' }",
    864                          Lower);
    865   EXPECT_EQ(nullptr, FS.get());
    866   FS = getFromYAMLString(
    867       "{ 'roots':[ { 'type': 'file', 'name': 'me', 'external-contents': [] }",
    868       Lower);
    869   EXPECT_EQ(nullptr, FS.get());
    870   FS = getFromYAMLString(
    871       "{ 'roots':[ { 'type': 'file', 'name': 'me', 'external-contents': {} }",
    872       Lower);
    873   EXPECT_EQ(nullptr, FS.get());
    874   FS = getFromYAMLString(
    875       "{ 'roots':[ { 'type': 'directory', 'name': 'me', 'contents': {} }",
    876       Lower);
    877   EXPECT_EQ(nullptr, FS.get());
    878   FS = getFromYAMLString(
    879       "{ 'roots':[ { 'type': 'directory', 'name': 'me', 'contents': '' }",
    880       Lower);
    881   EXPECT_EQ(nullptr, FS.get());
    882   FS = getFromYAMLString(
    883       "{ 'roots':[ { 'thingy': 'directory', 'name': 'me', 'contents': [] }",
    884       Lower);
    885   EXPECT_EQ(nullptr, FS.get());
    886 
    887   // missing mandatory fields
    888   FS = getFromYAMLString("{ 'roots':[ { 'type': 'file', 'name': 'me' }", Lower);
    889   EXPECT_EQ(nullptr, FS.get());
    890   FS = getFromYAMLString(
    891       "{ 'roots':[ { 'type': 'file', 'external-contents': 'other' }", Lower);
    892   EXPECT_EQ(nullptr, FS.get());
    893   FS = getFromYAMLString("{ 'roots':[ { 'name': 'me', 'contents': [] }", Lower);
    894   EXPECT_EQ(nullptr, FS.get());
    895 
    896   // duplicate keys
    897   FS = getFromYAMLString("{ 'roots':[], 'roots':[] }", Lower);
    898   EXPECT_EQ(nullptr, FS.get());
    899   FS = getFromYAMLString(
    900       "{ 'case-sensitive':'true', 'case-sensitive':'true', 'roots':[] }",
    901       Lower);
    902   EXPECT_EQ(nullptr, FS.get());
    903   FS =
    904       getFromYAMLString("{ 'roots':[{'name':'me', 'name':'you', 'type':'file', "
    905                         "'external-contents':'blah' } ] }",
    906                         Lower);
    907   EXPECT_EQ(nullptr, FS.get());
    908 
    909   // missing version
    910   FS = getFromYAMLRawString("{ 'roots':[] }", Lower);
    911   EXPECT_EQ(nullptr, FS.get());
    912 
    913   // bad version number
    914   FS = getFromYAMLRawString("{ 'version':'foo', 'roots':[] }", Lower);
    915   EXPECT_EQ(nullptr, FS.get());
    916   FS = getFromYAMLRawString("{ 'version':-1, 'roots':[] }", Lower);
    917   EXPECT_EQ(nullptr, FS.get());
    918   FS = getFromYAMLRawString("{ 'version':100000, 'roots':[] }", Lower);
    919   EXPECT_EQ(nullptr, FS.get());
    920   EXPECT_EQ(24, NumDiagnostics);
    921 }
    922 
    923 TEST_F(VFSFromYAMLTest, UseExternalName) {
    924   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
    925   Lower->addRegularFile("//root/external/file");
    926 
    927   IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
    928       "{ 'roots': [\n"
    929       "  { 'type': 'file', 'name': '//root/A',\n"
    930       "    'external-contents': '//root/external/file'\n"
    931       "  },\n"
    932       "  { 'type': 'file', 'name': '//root/B',\n"
    933       "    'use-external-name': true,\n"
    934       "    'external-contents': '//root/external/file'\n"
    935       "  },\n"
    936       "  { 'type': 'file', 'name': '//root/C',\n"
    937       "    'use-external-name': false,\n"
    938       "    'external-contents': '//root/external/file'\n"
    939       "  }\n"
    940       "] }", Lower);
    941   ASSERT_TRUE(nullptr != FS.get());
    942 
    943   // default true
    944   EXPECT_EQ("//root/external/file", FS->status("//root/A")->getName());
    945   // explicit
    946   EXPECT_EQ("//root/external/file", FS->status("//root/B")->getName());
    947   EXPECT_EQ("//root/C", FS->status("//root/C")->getName());
    948 
    949   // global configuration
    950   FS = getFromYAMLString(
    951       "{ 'use-external-names': false,\n"
    952       "  'roots': [\n"
    953       "  { 'type': 'file', 'name': '//root/A',\n"
    954       "    'external-contents': '//root/external/file'\n"
    955       "  },\n"
    956       "  { 'type': 'file', 'name': '//root/B',\n"
    957       "    'use-external-name': true,\n"
    958       "    'external-contents': '//root/external/file'\n"
    959       "  },\n"
    960       "  { 'type': 'file', 'name': '//root/C',\n"
    961       "    'use-external-name': false,\n"
    962       "    'external-contents': '//root/external/file'\n"
    963       "  }\n"
    964       "] }", Lower);
    965   ASSERT_TRUE(nullptr != FS.get());
    966 
    967   // default
    968   EXPECT_EQ("//root/A", FS->status("//root/A")->getName());
    969   // explicit
    970   EXPECT_EQ("//root/external/file", FS->status("//root/B")->getName());
    971   EXPECT_EQ("//root/C", FS->status("//root/C")->getName());
    972 }
    973 
    974 TEST_F(VFSFromYAMLTest, MultiComponentPath) {
    975   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
    976   Lower->addRegularFile("//root/other");
    977 
    978   // file in roots
    979   IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
    980       "{ 'roots': [\n"
    981       "  { 'type': 'file', 'name': '//root/path/to/file',\n"
    982       "    'external-contents': '//root/other' }]\n"
    983       "}", Lower);
    984   ASSERT_TRUE(nullptr != FS.get());
    985   EXPECT_FALSE(FS->status("//root/path/to/file").getError());
    986   EXPECT_FALSE(FS->status("//root/path/to").getError());
    987   EXPECT_FALSE(FS->status("//root/path").getError());
    988   EXPECT_FALSE(FS->status("//root/").getError());
    989 
    990   // at the start
    991   FS = getFromYAMLString(
    992       "{ 'roots': [\n"
    993       "  { 'type': 'directory', 'name': '//root/path/to',\n"
    994       "    'contents': [ { 'type': 'file', 'name': 'file',\n"
    995       "                    'external-contents': '//root/other' }]}]\n"
    996       "}", Lower);
    997   ASSERT_TRUE(nullptr != FS.get());
    998   EXPECT_FALSE(FS->status("//root/path/to/file").getError());
    999   EXPECT_FALSE(FS->status("//root/path/to").getError());
   1000   EXPECT_FALSE(FS->status("//root/path").getError());
   1001   EXPECT_FALSE(FS->status("//root/").getError());
   1002 
   1003   // at the end
   1004   FS = getFromYAMLString(
   1005       "{ 'roots': [\n"
   1006       "  { 'type': 'directory', 'name': '//root/',\n"
   1007       "    'contents': [ { 'type': 'file', 'name': 'path/to/file',\n"
   1008       "                    'external-contents': '//root/other' }]}]\n"
   1009       "}", Lower);
   1010   ASSERT_TRUE(nullptr != FS.get());
   1011   EXPECT_FALSE(FS->status("//root/path/to/file").getError());
   1012   EXPECT_FALSE(FS->status("//root/path/to").getError());
   1013   EXPECT_FALSE(FS->status("//root/path").getError());
   1014   EXPECT_FALSE(FS->status("//root/").getError());
   1015 }
   1016 
   1017 TEST_F(VFSFromYAMLTest, TrailingSlashes) {
   1018   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
   1019   Lower->addRegularFile("//root/other");
   1020 
   1021   // file in roots
   1022   IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
   1023       "{ 'roots': [\n"
   1024       "  { 'type': 'directory', 'name': '//root/path/to////',\n"
   1025       "    'contents': [ { 'type': 'file', 'name': 'file',\n"
   1026       "                    'external-contents': '//root/other' }]}]\n"
   1027       "}", Lower);
   1028   ASSERT_TRUE(nullptr != FS.get());
   1029   EXPECT_FALSE(FS->status("//root/path/to/file").getError());
   1030   EXPECT_FALSE(FS->status("//root/path/to").getError());
   1031   EXPECT_FALSE(FS->status("//root/path").getError());
   1032   EXPECT_FALSE(FS->status("//root/").getError());
   1033 }
   1034 
   1035 TEST_F(VFSFromYAMLTest, DirectoryIteration) {
   1036   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
   1037   Lower->addDirectory("//root/");
   1038   Lower->addDirectory("//root/foo");
   1039   Lower->addDirectory("//root/foo/bar");
   1040   Lower->addRegularFile("//root/foo/bar/a");
   1041   Lower->addRegularFile("//root/foo/bar/b");
   1042   Lower->addRegularFile("//root/file3");
   1043   IntrusiveRefCntPtr<vfs::FileSystem> FS =
   1044   getFromYAMLString("{ 'use-external-names': false,\n"
   1045                     "  'roots': [\n"
   1046                     "{\n"
   1047                     "  'type': 'directory',\n"
   1048                     "  'name': '//root/',\n"
   1049                     "  'contents': [ {\n"
   1050                     "                  'type': 'file',\n"
   1051                     "                  'name': 'file1',\n"
   1052                     "                  'external-contents': '//root/foo/bar/a'\n"
   1053                     "                },\n"
   1054                     "                {\n"
   1055                     "                  'type': 'file',\n"
   1056                     "                  'name': 'file2',\n"
   1057                     "                  'external-contents': '//root/foo/bar/b'\n"
   1058                     "                }\n"
   1059                     "              ]\n"
   1060                     "}\n"
   1061                     "]\n"
   1062                     "}",
   1063                     Lower);
   1064   ASSERT_TRUE(FS.get() != nullptr);
   1065 
   1066   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
   1067       new vfs::OverlayFileSystem(Lower));
   1068   O->pushOverlay(FS);
   1069 
   1070   std::error_code EC;
   1071   checkContents(O->dir_begin("//root/", EC),
   1072                 {"//root/file1", "//root/file2", "//root/file3", "//root/foo"});
   1073 
   1074   checkContents(O->dir_begin("//root/foo/bar", EC),
   1075                 {"//root/foo/bar/a", "//root/foo/bar/b"});
   1076 }
   1077 
   1078 TEST_F(VFSFromYAMLTest, DirectoryIterationSameDirMultipleEntries) {
   1079   // https://llvm.org/bugs/show_bug.cgi?id=27725
   1080   if (!supportsSameDirMultipleYAMLEntries())
   1081     return;
   1082 
   1083   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
   1084   Lower->addDirectory("//root/zab");
   1085   Lower->addDirectory("//root/baz");
   1086   Lower->addRegularFile("//root/zab/a");
   1087   Lower->addRegularFile("//root/zab/b");
   1088   IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
   1089       "{ 'use-external-names': false,\n"
   1090       "  'roots': [\n"
   1091       "{\n"
   1092       "  'type': 'directory',\n"
   1093       "  'name': '//root/baz/',\n"
   1094       "  'contents': [ {\n"
   1095       "                  'type': 'file',\n"
   1096       "                  'name': 'x',\n"
   1097       "                  'external-contents': '//root/zab/a'\n"
   1098       "                }\n"
   1099       "              ]\n"
   1100       "},\n"
   1101       "{\n"
   1102       "  'type': 'directory',\n"
   1103       "  'name': '//root/baz/',\n"
   1104       "  'contents': [ {\n"
   1105       "                  'type': 'file',\n"
   1106       "                  'name': 'y',\n"
   1107       "                  'external-contents': '//root/zab/b'\n"
   1108       "                }\n"
   1109       "              ]\n"
   1110       "}\n"
   1111       "]\n"
   1112       "}",
   1113       Lower);
   1114   ASSERT_TRUE(FS.get() != nullptr);
   1115 
   1116   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
   1117       new vfs::OverlayFileSystem(Lower));
   1118   O->pushOverlay(FS);
   1119 
   1120   std::error_code EC;
   1121 
   1122   checkContents(O->dir_begin("//root/baz/", EC),
   1123                 {"//root/baz/x", "//root/baz/y"});
   1124 }
   1125 
   1126 TEST_F(VFSFromYAMLTest, RecursiveDirectoryIterationLevel) {
   1127 
   1128   IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem());
   1129   Lower->addDirectory("//root/a");
   1130   Lower->addDirectory("//root/a/b");
   1131   Lower->addDirectory("//root/a/b/c");
   1132   Lower->addRegularFile("//root/a/b/c/file");
   1133   IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString(
   1134       "{ 'use-external-names': false,\n"
   1135       "  'roots': [\n"
   1136       "{\n"
   1137       "  'type': 'directory',\n"
   1138       "  'name': '//root/a/b/c/',\n"
   1139       "  'contents': [ {\n"
   1140       "                  'type': 'file',\n"
   1141       "                  'name': 'file',\n"
   1142       "                  'external-contents': '//root/a/b/c/file'\n"
   1143       "                }\n"
   1144       "              ]\n"
   1145       "},\n"
   1146       "]\n"
   1147       "}",
   1148       Lower);
   1149   ASSERT_TRUE(FS.get() != nullptr);
   1150 
   1151   IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(
   1152       new vfs::OverlayFileSystem(Lower));
   1153   O->pushOverlay(FS);
   1154 
   1155   std::error_code EC;
   1156 
   1157   // Test recursive_directory_iterator level()
   1158   vfs::recursive_directory_iterator I = vfs::recursive_directory_iterator(
   1159                                         *O, "//root", EC), E;
   1160   ASSERT_FALSE(EC);
   1161   for (int l = 0; I != E; I.increment(EC), ++l) {
   1162     ASSERT_FALSE(EC);
   1163     EXPECT_EQ(I.level(), l);
   1164   }
   1165   EXPECT_EQ(I, E);
   1166 }
   1167