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