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