diff options
author | dim <dim@FreeBSD.org> | 2014-11-24 09:15:30 +0000 |
---|---|---|
committer | dim <dim@FreeBSD.org> | 2014-11-24 09:15:30 +0000 |
commit | 173a4f43a911175643bda81ee675e8d9269056ea (patch) | |
tree | 47df2c12b57214af6c31e47404b005675b8b7ffc /unittests/Basic | |
parent | 88f7a7d5251a2d813460274c92decc143a11569b (diff) | |
download | FreeBSD-src-173a4f43a911175643bda81ee675e8d9269056ea.zip FreeBSD-src-173a4f43a911175643bda81ee675e8d9269056ea.tar.gz |
Vendor import of clang RELEASE_350/final tag r216957 (effectively, 3.5.0 release):
https://llvm.org/svn/llvm-project/cfe/tags/RELEASE_350/final@216957
Diffstat (limited to 'unittests/Basic')
-rw-r--r-- | unittests/Basic/CMakeLists.txt | 5 | ||||
-rw-r--r-- | unittests/Basic/FileManagerTest.cpp | 57 | ||||
-rw-r--r-- | unittests/Basic/SourceManagerTest.cpp | 70 | ||||
-rw-r--r-- | unittests/Basic/VirtualFileSystemTest.cpp | 934 |
4 files changed, 1009 insertions, 57 deletions
diff --git a/unittests/Basic/CMakeLists.txt b/unittests/Basic/CMakeLists.txt index 51db6ce..b8f69bf 100644 --- a/unittests/Basic/CMakeLists.txt +++ b/unittests/Basic/CMakeLists.txt @@ -1,7 +1,12 @@ +set(LLVM_LINK_COMPONENTS + Support + ) + add_clang_unittest(BasicTests CharInfoTest.cpp FileManagerTest.cpp SourceManagerTest.cpp + VirtualFileSystemTest.cpp ) target_link_libraries(BasicTests diff --git a/unittests/Basic/FileManagerTest.cpp b/unittests/Basic/FileManagerTest.cpp index f8ce50d..b3bc767 100644 --- a/unittests/Basic/FileManagerTest.cpp +++ b/unittests/Basic/FileManagerTest.cpp @@ -11,6 +11,7 @@ #include "clang/Basic/FileSystemOptions.h" #include "clang/Basic/FileSystemStatCache.h" #include "gtest/gtest.h" +#include "llvm/Config/llvm-config.h" using namespace llvm; using namespace clang; @@ -28,10 +29,13 @@ private: void InjectFileOrDirectory(const char *Path, ino_t INode, bool IsFile) { FileData Data; - memset(&Data, 0, sizeof(FileData)); - llvm::sys::fs::UniqueID ID(1, INode); - Data.UniqueID = ID; + Data.Name = Path; + Data.Size = 0; + Data.ModTime = 0; + Data.UniqueID = llvm::sys::fs::UniqueID(1, INode); Data.IsDirectory = !IsFile; + Data.IsNamedPipe = false; + Data.InPCH = false; StatCalls[Path] = Data; } @@ -47,8 +51,9 @@ public: } // Implement FileSystemStatCache::getStat(). - virtual LookupResult getStat(const char *Path, FileData &Data, bool isFile, - int *FileDescriptor) { + LookupResult getStat(const char *Path, FileData &Data, bool isFile, + std::unique_ptr<vfs::File> *F, + vfs::FileSystem &FS) override { if (StatCalls.count(Path) != 0) { Data = StatCalls[Path]; return CacheExists; @@ -72,17 +77,17 @@ class FileManagerTest : public ::testing::Test { // (not NULL, correct name). TEST_F(FileManagerTest, getVirtualFileSetsTheDirFieldCorrectly) { const FileEntry *file = manager.getVirtualFile("foo.cpp", 42, 0); - ASSERT_TRUE(file != NULL); + ASSERT_TRUE(file != nullptr); const DirectoryEntry *dir = file->getDir(); - ASSERT_TRUE(dir != NULL); + ASSERT_TRUE(dir != nullptr); EXPECT_STREQ(".", dir->getName()); file = manager.getVirtualFile("x/y/z.cpp", 42, 0); - ASSERT_TRUE(file != NULL); + ASSERT_TRUE(file != nullptr); dir = file->getDir(); - ASSERT_TRUE(dir != NULL); + ASSERT_TRUE(dir != nullptr); EXPECT_STREQ("x/y", dir->getName()); } @@ -94,9 +99,9 @@ TEST_F(FileManagerTest, NoVirtualDirectoryExistsBeforeAVirtualFileIsAdded) { // by what's in the real file system. manager.addStatCache(new FakeStatCache); - EXPECT_EQ(NULL, manager.getDirectory("virtual/dir/foo")); - EXPECT_EQ(NULL, manager.getDirectory("virtual/dir")); - EXPECT_EQ(NULL, manager.getDirectory("virtual")); + EXPECT_EQ(nullptr, manager.getDirectory("virtual/dir/foo")); + EXPECT_EQ(nullptr, manager.getDirectory("virtual/dir")); + EXPECT_EQ(nullptr, manager.getDirectory("virtual")); } // When a virtual file is added, all of its ancestors should be created. @@ -105,14 +110,14 @@ TEST_F(FileManagerTest, getVirtualFileCreatesDirectoryEntriesForAncestors) { manager.addStatCache(new FakeStatCache); manager.getVirtualFile("virtual/dir/bar.h", 100, 0); - EXPECT_EQ(NULL, manager.getDirectory("virtual/dir/foo")); + EXPECT_EQ(nullptr, manager.getDirectory("virtual/dir/foo")); const DirectoryEntry *dir = manager.getDirectory("virtual/dir"); - ASSERT_TRUE(dir != NULL); + ASSERT_TRUE(dir != nullptr); EXPECT_STREQ("virtual/dir", dir->getName()); dir = manager.getDirectory("virtual"); - ASSERT_TRUE(dir != NULL); + ASSERT_TRUE(dir != nullptr); EXPECT_STREQ("virtual", dir->getName()); } @@ -123,7 +128,7 @@ TEST_F(FileManagerTest, getFileReturnsValidFileEntryForExistingRealFile) { statCache->InjectDirectory("/tmp", 42); statCache->InjectFile("/tmp/test", 43); -#ifdef _WIN32 +#ifdef LLVM_ON_WIN32 const char *DirName = "C:."; const char *FileName = "C:test"; statCache->InjectDirectory(DirName, 44); @@ -133,14 +138,14 @@ TEST_F(FileManagerTest, getFileReturnsValidFileEntryForExistingRealFile) { manager.addStatCache(statCache); const FileEntry *file = manager.getFile("/tmp/test"); - ASSERT_TRUE(file != NULL); + ASSERT_TRUE(file != nullptr); EXPECT_STREQ("/tmp/test", file->getName()); const DirectoryEntry *dir = file->getDir(); - ASSERT_TRUE(dir != NULL); + ASSERT_TRUE(dir != nullptr); EXPECT_STREQ("/tmp", dir->getName()); -#ifdef _WIN32 +#ifdef LLVM_ON_WIN32 file = manager.getFile(FileName); ASSERT_TRUE(file != NULL); @@ -157,11 +162,11 @@ TEST_F(FileManagerTest, getFileReturnsValidFileEntryForExistingVirtualFile) { manager.getVirtualFile("virtual/dir/bar.h", 100, 0); const FileEntry *file = manager.getFile("virtual/dir/bar.h"); - ASSERT_TRUE(file != NULL); + ASSERT_TRUE(file != nullptr); EXPECT_STREQ("virtual/dir/bar.h", file->getName()); const DirectoryEntry *dir = file->getDir(); - ASSERT_TRUE(dir != NULL); + ASSERT_TRUE(dir != nullptr); EXPECT_STREQ("virtual/dir", dir->getName()); } @@ -178,8 +183,8 @@ TEST_F(FileManagerTest, getFileReturnsDifferentFileEntriesForDifferentFiles) { const FileEntry *fileFoo = manager.getFile("foo.cpp"); const FileEntry *fileBar = manager.getFile("bar.cpp"); - ASSERT_TRUE(fileFoo != NULL); - ASSERT_TRUE(fileBar != NULL); + ASSERT_TRUE(fileFoo != nullptr); + ASSERT_TRUE(fileBar != nullptr); EXPECT_NE(fileFoo, fileBar); } @@ -196,12 +201,12 @@ TEST_F(FileManagerTest, getFileReturnsNULLForNonexistentFile) { manager.getVirtualFile("bar.cpp", 200, 0); const FileEntry *file = manager.getFile("xyz.txt"); - EXPECT_EQ(NULL, file); + EXPECT_EQ(nullptr, file); } // The following tests apply to Unix-like system only. -#ifndef _WIN32 +#ifndef LLVM_ON_WIN32 // getFile() returns the same FileEntry for real files that are aliases. TEST_F(FileManagerTest, getFileReturnsSameFileEntryForAliasedRealFiles) { @@ -231,6 +236,6 @@ TEST_F(FileManagerTest, getFileReturnsSameFileEntryForAliasedVirtualFiles) { EXPECT_EQ(manager.getFile("abc/foo.cpp"), manager.getFile("abc/bar.cpp")); } -#endif // !_WIN32 +#endif // !LLVM_ON_WIN32 } // anonymous namespace diff --git a/unittests/Basic/SourceManagerTest.cpp b/unittests/Basic/SourceManagerTest.cpp index d94cfe9..9ea093c 100644 --- a/unittests/Basic/SourceManagerTest.cpp +++ b/unittests/Basic/SourceManagerTest.cpp @@ -20,7 +20,7 @@ #include "clang/Lex/Preprocessor.h" #include "clang/Lex/PreprocessorOptions.h" #include "llvm/ADT/SmallString.h" -#include "llvm/Config/config.h" +#include "llvm/Config/llvm-config.h" #include "gtest/gtest.h" using namespace llvm; @@ -38,7 +38,7 @@ protected: SourceMgr(Diags, FileMgr), TargetOpts(new TargetOptions) { TargetOpts->Triple = "x86_64-apple-darwin11.1.0"; - Target = TargetInfo::CreateTargetInfo(Diags, &*TargetOpts); + Target = TargetInfo::CreateTargetInfo(Diags, TargetOpts); } FileSystemOptions FileMgrOpts; @@ -47,22 +47,27 @@ protected: DiagnosticsEngine Diags; SourceManager SourceMgr; LangOptions LangOpts; - IntrusiveRefCntPtr<TargetOptions> TargetOpts; + std::shared_ptr<TargetOptions> TargetOpts; IntrusiveRefCntPtr<TargetInfo> Target; }; class VoidModuleLoader : public ModuleLoader { - virtual ModuleLoadResult loadModule(SourceLocation ImportLoc, - ModuleIdPath Path, - Module::NameVisibilityKind Visibility, - bool IsInclusionDirective) { + ModuleLoadResult loadModule(SourceLocation ImportLoc, + ModuleIdPath Path, + Module::NameVisibilityKind Visibility, + bool IsInclusionDirective) override { return ModuleLoadResult(); } - virtual void makeModuleVisible(Module *Mod, - Module::NameVisibilityKind Visibility, - SourceLocation ImportLoc, - bool Complain) { } + void makeModuleVisible(Module *Mod, + Module::NameVisibilityKind Visibility, + SourceLocation ImportLoc, + bool Complain) override { } + + GlobalModuleIndex *loadGlobalModuleIndex(SourceLocation TriggerLoc) override + { return nullptr; } + bool lookupMissingImports(StringRef Name, SourceLocation TriggerLoc) override + { return 0; }; }; TEST_F(SourceManagerTest, isBeforeInTranslationUnit) { @@ -70,16 +75,17 @@ TEST_F(SourceManagerTest, isBeforeInTranslationUnit) { "#define M(x) [x]\n" "M(foo)"; MemoryBuffer *buf = MemoryBuffer::getMemBuffer(source); - FileID mainFileID = SourceMgr.createMainFileIDForMemBuffer(buf); + FileID mainFileID = SourceMgr.createFileID(buf); + SourceMgr.setMainFileID(mainFileID); VoidModuleLoader ModLoader; HeaderSearch HeaderInfo(new HeaderSearchOptions, SourceMgr, Diags, LangOpts, &*Target); - Preprocessor PP(new PreprocessorOptions(), Diags, LangOpts, Target.getPtr(), - SourceMgr, HeaderInfo, ModLoader, - /*IILookup =*/ 0, - /*OwnsHeaderSearch =*/false, - /*DelayInitialization =*/ false); + Preprocessor PP(new PreprocessorOptions(), Diags, LangOpts, SourceMgr, + HeaderInfo, ModLoader, + /*IILookup =*/nullptr, + /*OwnsHeaderSearch =*/false); + PP.Initialize(*Target); PP.EnterMainSourceFile(); std::vector<Token> toks; @@ -122,7 +128,8 @@ TEST_F(SourceManagerTest, getColumnNumber) { "int y;"; MemoryBuffer *Buf = MemoryBuffer::getMemBuffer(Source); - FileID MainFileID = SourceMgr.createMainFileIDForMemBuffer(Buf); + FileID MainFileID = SourceMgr.createFileID(Buf); + SourceMgr.setMainFileID(MainFileID); bool Invalid; @@ -161,7 +168,7 @@ TEST_F(SourceManagerTest, getColumnNumber) { EXPECT_TRUE(Invalid); // Test with no invalid flag. - EXPECT_EQ(1U, SourceMgr.getColumnNumber(MainFileID, 0, NULL)); + EXPECT_EQ(1U, SourceMgr.getColumnNumber(MainFileID, 0, nullptr)); } #if defined(LLVM_ON_UNIX) @@ -181,7 +188,8 @@ TEST_F(SourceManagerTest, getMacroArgExpandedLocation) { MemoryBuffer *headerBuf = MemoryBuffer::getMemBuffer(header); MemoryBuffer *mainBuf = MemoryBuffer::getMemBuffer(main); - FileID mainFileID = SourceMgr.createMainFileIDForMemBuffer(mainBuf); + FileID mainFileID = SourceMgr.createFileID(mainBuf); + SourceMgr.setMainFileID(mainFileID); const FileEntry *headerFile = FileMgr.getVirtualFile("/test-header.h", headerBuf->getBufferSize(), 0); @@ -190,11 +198,11 @@ TEST_F(SourceManagerTest, getMacroArgExpandedLocation) { VoidModuleLoader ModLoader; HeaderSearch HeaderInfo(new HeaderSearchOptions, SourceMgr, Diags, LangOpts, &*Target); - Preprocessor PP(new PreprocessorOptions(), Diags, LangOpts, Target.getPtr(), - SourceMgr, HeaderInfo, ModLoader, - /*IILookup =*/ 0, - /*OwnsHeaderSearch =*/false, - /*DelayInitialization =*/ false); + Preprocessor PP(new PreprocessorOptions(), Diags, LangOpts, SourceMgr, + HeaderInfo, ModLoader, + /*IILookup =*/nullptr, + /*OwnsHeaderSearch =*/false); + PP.Initialize(*Target); PP.EnterMainSourceFile(); std::vector<Token> toks; @@ -279,7 +287,7 @@ TEST_F(SourceManagerTest, isBeforeInTranslationUnitWithMacroInInclude) { MemoryBuffer *headerBuf = MemoryBuffer::getMemBuffer(header); MemoryBuffer *mainBuf = MemoryBuffer::getMemBuffer(main); - SourceMgr.createMainFileIDForMemBuffer(mainBuf); + SourceMgr.setMainFileID(SourceMgr.createFileID(mainBuf)); const FileEntry *headerFile = FileMgr.getVirtualFile("/test-header.h", headerBuf->getBufferSize(), 0); @@ -288,11 +296,11 @@ TEST_F(SourceManagerTest, isBeforeInTranslationUnitWithMacroInInclude) { VoidModuleLoader ModLoader; HeaderSearch HeaderInfo(new HeaderSearchOptions, SourceMgr, Diags, LangOpts, &*Target); - Preprocessor PP(new PreprocessorOptions(), Diags, LangOpts, Target.getPtr(), - SourceMgr, HeaderInfo, ModLoader, - /*IILookup =*/ 0, - /*OwnsHeaderSearch =*/false, - /*DelayInitialization =*/ false); + Preprocessor PP(new PreprocessorOptions(), Diags, LangOpts, SourceMgr, + HeaderInfo, ModLoader, + /*IILookup =*/nullptr, + /*OwnsHeaderSearch =*/false); + PP.Initialize(*Target); std::vector<MacroAction> Macros; PP.addPPCallbacks(new MacroTracker(Macros)); diff --git a/unittests/Basic/VirtualFileSystemTest.cpp b/unittests/Basic/VirtualFileSystemTest.cpp new file mode 100644 index 0000000..e7d361e --- /dev/null +++ b/unittests/Basic/VirtualFileSystemTest.cpp @@ -0,0 +1,934 @@ +//===- unittests/Basic/VirtualFileSystem.cpp ---------------- VFS tests ---===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/Basic/VirtualFileSystem.h" +#include "llvm/Support/Errc.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/SourceMgr.h" +#include "gtest/gtest.h" +#include <map> +using namespace clang; +using namespace llvm; +using llvm::sys::fs::UniqueID; + +namespace { +class DummyFileSystem : public vfs::FileSystem { + int FSID; // used to produce UniqueIDs + int FileID; // used to produce UniqueIDs + std::map<std::string, vfs::Status> FilesAndDirs; + + static int getNextFSID() { + static int Count = 0; + return Count++; + } + +public: + DummyFileSystem() : FSID(getNextFSID()), FileID(0) {} + + ErrorOr<vfs::Status> status(const Twine &Path) { + std::map<std::string, vfs::Status>::iterator I = + FilesAndDirs.find(Path.str()); + if (I == FilesAndDirs.end()) + return make_error_code(llvm::errc::no_such_file_or_directory); + return I->second; + } + std::error_code openFileForRead(const Twine &Path, + std::unique_ptr<vfs::File> &Result) { + llvm_unreachable("unimplemented"); + } + std::error_code getBufferForFile(const Twine &Name, + std::unique_ptr<MemoryBuffer> &Result, + int64_t FileSize = -1, + bool RequiresNullTerminator = true) { + llvm_unreachable("unimplemented"); + } + + struct DirIterImpl : public clang::vfs::detail::DirIterImpl { + std::map<std::string, vfs::Status> &FilesAndDirs; + std::map<std::string, vfs::Status>::iterator I; + std::string Path; + bool isInPath(StringRef S) { + if (Path.size() < S.size() && S.find(Path) == 0) { + auto LastSep = S.find_last_of('/'); + if (LastSep == Path.size() || LastSep == Path.size()-1) + return true; + } + return false; + } + DirIterImpl(std::map<std::string, vfs::Status> &FilesAndDirs, + const Twine &_Path) + : FilesAndDirs(FilesAndDirs), I(FilesAndDirs.begin()), + Path(_Path.str()) { + for ( ; I != FilesAndDirs.end(); ++I) { + if (isInPath(I->first)) { + CurrentEntry = I->second; + break; + } + } + } + std::error_code increment() override { + ++I; + for ( ; I != FilesAndDirs.end(); ++I) { + if (isInPath(I->first)) { + CurrentEntry = I->second; + break; + } + } + if (I == FilesAndDirs.end()) + CurrentEntry = vfs::Status(); + return std::error_code(); + } + }; + + vfs::directory_iterator dir_begin(const Twine &Dir, + std::error_code &EC) override { + return vfs::directory_iterator( + std::make_shared<DirIterImpl>(FilesAndDirs, Dir)); + } + + void addEntry(StringRef Path, const vfs::Status &Status) { + FilesAndDirs[Path] = Status; + } + + void addRegularFile(StringRef Path, sys::fs::perms Perms = sys::fs::all_all) { + vfs::Status S(Path, Path, UniqueID(FSID, FileID++), sys::TimeValue::now(), + 0, 0, 1024, sys::fs::file_type::regular_file, Perms); + addEntry(Path, S); + } + + void addDirectory(StringRef Path, sys::fs::perms Perms = sys::fs::all_all) { + vfs::Status S(Path, Path, UniqueID(FSID, FileID++), sys::TimeValue::now(), + 0, 0, 0, sys::fs::file_type::directory_file, Perms); + addEntry(Path, S); + } + + void addSymlink(StringRef Path) { + vfs::Status S(Path, Path, UniqueID(FSID, FileID++), sys::TimeValue::now(), + 0, 0, 0, sys::fs::file_type::symlink_file, sys::fs::all_all); + addEntry(Path, S); + } +}; +} // end anonymous namespace + +TEST(VirtualFileSystemTest, StatusQueries) { + IntrusiveRefCntPtr<DummyFileSystem> D(new DummyFileSystem()); + ErrorOr<vfs::Status> Status((std::error_code())); + + D->addRegularFile("/foo"); + Status = D->status("/foo"); + ASSERT_FALSE(Status.getError()); + EXPECT_TRUE(Status->isStatusKnown()); + EXPECT_FALSE(Status->isDirectory()); + EXPECT_TRUE(Status->isRegularFile()); + EXPECT_FALSE(Status->isSymlink()); + EXPECT_FALSE(Status->isOther()); + EXPECT_TRUE(Status->exists()); + + D->addDirectory("/bar"); + Status = D->status("/bar"); + ASSERT_FALSE(Status.getError()); + EXPECT_TRUE(Status->isStatusKnown()); + EXPECT_TRUE(Status->isDirectory()); + EXPECT_FALSE(Status->isRegularFile()); + EXPECT_FALSE(Status->isSymlink()); + EXPECT_FALSE(Status->isOther()); + EXPECT_TRUE(Status->exists()); + + D->addSymlink("/baz"); + Status = D->status("/baz"); + ASSERT_FALSE(Status.getError()); + EXPECT_TRUE(Status->isStatusKnown()); + EXPECT_FALSE(Status->isDirectory()); + EXPECT_FALSE(Status->isRegularFile()); + EXPECT_TRUE(Status->isSymlink()); + EXPECT_FALSE(Status->isOther()); + EXPECT_TRUE(Status->exists()); + + EXPECT_TRUE(Status->equivalent(*Status)); + ErrorOr<vfs::Status> Status2 = D->status("/foo"); + ASSERT_FALSE(Status2.getError()); + EXPECT_FALSE(Status->equivalent(*Status2)); +} + +TEST(VirtualFileSystemTest, BaseOnlyOverlay) { + IntrusiveRefCntPtr<DummyFileSystem> D(new DummyFileSystem()); + ErrorOr<vfs::Status> Status((std::error_code())); + EXPECT_FALSE(Status = D->status("/foo")); + + IntrusiveRefCntPtr<vfs::OverlayFileSystem> O(new vfs::OverlayFileSystem(D)); + EXPECT_FALSE(Status = O->status("/foo")); + + D->addRegularFile("/foo"); + Status = D->status("/foo"); + EXPECT_FALSE(Status.getError()); + + ErrorOr<vfs::Status> Status2((std::error_code())); + Status2 = O->status("/foo"); + EXPECT_FALSE(Status2.getError()); + EXPECT_TRUE(Status->equivalent(*Status2)); +} + +TEST(VirtualFileSystemTest, OverlayFiles) { + IntrusiveRefCntPtr<DummyFileSystem> Base(new DummyFileSystem()); + IntrusiveRefCntPtr<DummyFileSystem> Middle(new DummyFileSystem()); + IntrusiveRefCntPtr<DummyFileSystem> Top(new DummyFileSystem()); + IntrusiveRefCntPtr<vfs::OverlayFileSystem> O( + new vfs::OverlayFileSystem(Base)); + O->pushOverlay(Middle); + O->pushOverlay(Top); + + ErrorOr<vfs::Status> Status1((std::error_code())), + Status2((std::error_code())), Status3((std::error_code())), + StatusB((std::error_code())), StatusM((std::error_code())), + StatusT((std::error_code())); + + Base->addRegularFile("/foo"); + StatusB = Base->status("/foo"); + ASSERT_FALSE(StatusB.getError()); + Status1 = O->status("/foo"); + ASSERT_FALSE(Status1.getError()); + Middle->addRegularFile("/foo"); + StatusM = Middle->status("/foo"); + ASSERT_FALSE(StatusM.getError()); + Status2 = O->status("/foo"); + ASSERT_FALSE(Status2.getError()); + Top->addRegularFile("/foo"); + StatusT = Top->status("/foo"); + ASSERT_FALSE(StatusT.getError()); + Status3 = O->status("/foo"); + ASSERT_FALSE(Status3.getError()); + + EXPECT_TRUE(Status1->equivalent(*StatusB)); + EXPECT_TRUE(Status2->equivalent(*StatusM)); + EXPECT_TRUE(Status3->equivalent(*StatusT)); + + EXPECT_FALSE(Status1->equivalent(*Status2)); + EXPECT_FALSE(Status2->equivalent(*Status3)); + EXPECT_FALSE(Status1->equivalent(*Status3)); +} + +TEST(VirtualFileSystemTest, OverlayDirsNonMerged) { + IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); + IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem()); + IntrusiveRefCntPtr<vfs::OverlayFileSystem> O( + new vfs::OverlayFileSystem(Lower)); + O->pushOverlay(Upper); + + Lower->addDirectory("/lower-only"); + Upper->addDirectory("/upper-only"); + + // non-merged paths should be the same + ErrorOr<vfs::Status> Status1 = Lower->status("/lower-only"); + ASSERT_FALSE(Status1.getError()); + ErrorOr<vfs::Status> Status2 = O->status("/lower-only"); + ASSERT_FALSE(Status2.getError()); + EXPECT_TRUE(Status1->equivalent(*Status2)); + + Status1 = Upper->status("/upper-only"); + ASSERT_FALSE(Status1.getError()); + Status2 = O->status("/upper-only"); + ASSERT_FALSE(Status2.getError()); + EXPECT_TRUE(Status1->equivalent(*Status2)); +} + +TEST(VirtualFileSystemTest, MergedDirPermissions) { + // merged directories get the permissions of the upper dir + IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); + IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem()); + IntrusiveRefCntPtr<vfs::OverlayFileSystem> O( + new vfs::OverlayFileSystem(Lower)); + O->pushOverlay(Upper); + + ErrorOr<vfs::Status> Status((std::error_code())); + Lower->addDirectory("/both", sys::fs::owner_read); + Upper->addDirectory("/both", sys::fs::owner_all | sys::fs::group_read); + Status = O->status("/both"); + ASSERT_FALSE(Status.getError()); + EXPECT_EQ(0740, Status->getPermissions()); + + // permissions (as usual) are not recursively applied + Lower->addRegularFile("/both/foo", sys::fs::owner_read); + Upper->addRegularFile("/both/bar", sys::fs::owner_write); + Status = O->status("/both/foo"); + ASSERT_FALSE( Status.getError()); + EXPECT_EQ(0400, Status->getPermissions()); + Status = O->status("/both/bar"); + ASSERT_FALSE(Status.getError()); + EXPECT_EQ(0200, Status->getPermissions()); +} + +namespace { +struct ScopedDir { + SmallString<128> Path; + ScopedDir(const Twine &Name, bool Unique=false) { + std::error_code EC; + if (Unique) { + EC = llvm::sys::fs::createUniqueDirectory(Name, Path); + } else { + Path = Name.str(); + EC = llvm::sys::fs::create_directory(Twine(Path)); + } + if (EC) + Path = ""; + EXPECT_FALSE(EC); + } + ~ScopedDir() { + if (Path != "") + EXPECT_FALSE(llvm::sys::fs::remove(Path.str())); + } + operator StringRef() { return Path.str(); } +}; +} + +TEST(VirtualFileSystemTest, BasicRealFSIteration) { + ScopedDir TestDirectory("virtual-file-system-test", /*Unique*/true); + IntrusiveRefCntPtr<vfs::FileSystem> FS = vfs::getRealFileSystem(); + + std::error_code EC; + vfs::directory_iterator I = FS->dir_begin(Twine(TestDirectory), EC); + ASSERT_FALSE(EC); + EXPECT_EQ(vfs::directory_iterator(), I); // empty directory is empty + + ScopedDir _a(TestDirectory+"/a"); + ScopedDir _ab(TestDirectory+"/a/b"); + ScopedDir _c(TestDirectory+"/c"); + ScopedDir _cd(TestDirectory+"/c/d"); + + I = FS->dir_begin(Twine(TestDirectory), EC); + ASSERT_FALSE(EC); + ASSERT_NE(vfs::directory_iterator(), I); + // Check either a or c, since we can't rely on the iteration order. + EXPECT_TRUE(I->getName().endswith("a") || I->getName().endswith("c")); + I.increment(EC); + ASSERT_FALSE(EC); + ASSERT_NE(vfs::directory_iterator(), I); + EXPECT_TRUE(I->getName().endswith("a") || I->getName().endswith("c")); + I.increment(EC); + EXPECT_EQ(vfs::directory_iterator(), I); +} + +TEST(VirtualFileSystemTest, BasicRealFSRecursiveIteration) { + ScopedDir TestDirectory("virtual-file-system-test", /*Unique*/true); + IntrusiveRefCntPtr<vfs::FileSystem> FS = vfs::getRealFileSystem(); + + std::error_code EC; + auto I = vfs::recursive_directory_iterator(*FS, Twine(TestDirectory), EC); + ASSERT_FALSE(EC); + EXPECT_EQ(vfs::recursive_directory_iterator(), I); // empty directory is empty + + ScopedDir _a(TestDirectory+"/a"); + ScopedDir _ab(TestDirectory+"/a/b"); + ScopedDir _c(TestDirectory+"/c"); + ScopedDir _cd(TestDirectory+"/c/d"); + + I = vfs::recursive_directory_iterator(*FS, Twine(TestDirectory), EC); + ASSERT_FALSE(EC); + ASSERT_NE(vfs::recursive_directory_iterator(), I); + + + std::vector<std::string> Contents; + for (auto E = vfs::recursive_directory_iterator(); !EC && I != E; + I.increment(EC)) { + Contents.push_back(I->getName()); + } + + // Check contents, which may be in any order + EXPECT_EQ(4U, Contents.size()); + int Counts[4] = { 0, 0, 0, 0 }; + for (const std::string &Name : Contents) { + ASSERT_FALSE(Name.empty()); + int Index = Name[Name.size()-1] - 'a'; + ASSERT_TRUE(Index >= 0 && Index < 4); + Counts[Index]++; + } + EXPECT_EQ(1, Counts[0]); // a + EXPECT_EQ(1, Counts[1]); // b + EXPECT_EQ(1, Counts[2]); // c + EXPECT_EQ(1, Counts[3]); // d +} + +template <typename T, size_t N> +std::vector<StringRef> makeStringRefVector(const T (&Arr)[N]) { + std::vector<StringRef> Vec; + for (size_t i = 0; i != N; ++i) + Vec.push_back(Arr[i]); + return Vec; +} + +template <typename DirIter> +static void checkContents(DirIter I, ArrayRef<StringRef> Expected) { + std::error_code EC; + auto ExpectedIter = Expected.begin(), ExpectedEnd = Expected.end(); + for (DirIter E; + !EC && I != E && ExpectedIter != ExpectedEnd; + I.increment(EC), ++ExpectedIter) + EXPECT_EQ(*ExpectedIter, I->getName()); + + EXPECT_EQ(ExpectedEnd, ExpectedIter); + EXPECT_EQ(DirIter(), I); +} + +TEST(VirtualFileSystemTest, OverlayIteration) { + IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); + IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem()); + IntrusiveRefCntPtr<vfs::OverlayFileSystem> O( + new vfs::OverlayFileSystem(Lower)); + O->pushOverlay(Upper); + + std::error_code EC; + checkContents(O->dir_begin("/", EC), ArrayRef<StringRef>()); + + Lower->addRegularFile("/file1"); + checkContents(O->dir_begin("/", EC), ArrayRef<StringRef>("/file1")); + + Upper->addRegularFile("/file2"); + { + const char *Contents[] = {"/file2", "/file1"}; + checkContents(O->dir_begin("/", EC), makeStringRefVector(Contents)); + } + + Lower->addDirectory("/dir1"); + Lower->addRegularFile("/dir1/foo"); + Upper->addDirectory("/dir2"); + Upper->addRegularFile("/dir2/foo"); + checkContents(O->dir_begin("/dir2", EC), ArrayRef<StringRef>("/dir2/foo")); + { + const char *Contents[] = {"/dir2", "/file2", "/dir1", "/file1"}; + checkContents(O->dir_begin("/", EC), makeStringRefVector(Contents)); + } +} + +TEST(VirtualFileSystemTest, OverlayRecursiveIteration) { + IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); + IntrusiveRefCntPtr<DummyFileSystem> Middle(new DummyFileSystem()); + IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem()); + IntrusiveRefCntPtr<vfs::OverlayFileSystem> O( + new vfs::OverlayFileSystem(Lower)); + O->pushOverlay(Middle); + O->pushOverlay(Upper); + + std::error_code EC; + checkContents(vfs::recursive_directory_iterator(*O, "/", EC), + ArrayRef<StringRef>()); + + Lower->addRegularFile("/file1"); + checkContents(vfs::recursive_directory_iterator(*O, "/", EC), + ArrayRef<StringRef>("/file1")); + + Upper->addDirectory("/dir"); + Upper->addRegularFile("/dir/file2"); + { + const char *Contents[] = {"/dir", "/dir/file2", "/file1"}; + checkContents(vfs::recursive_directory_iterator(*O, "/", EC), + makeStringRefVector(Contents)); + } + + Lower->addDirectory("/dir1"); + Lower->addRegularFile("/dir1/foo"); + Lower->addDirectory("/dir1/a"); + Lower->addRegularFile("/dir1/a/b"); + Middle->addDirectory("/a"); + Middle->addDirectory("/a/b"); + Middle->addDirectory("/a/b/c"); + Middle->addRegularFile("/a/b/c/d"); + Middle->addRegularFile("/hiddenByUp"); + Upper->addDirectory("/dir2"); + Upper->addRegularFile("/dir2/foo"); + Upper->addRegularFile("/hiddenByUp"); + checkContents(vfs::recursive_directory_iterator(*O, "/dir2", EC), + ArrayRef<StringRef>("/dir2/foo")); + { + const char *Contents[] = { "/dir", "/dir/file2", "/dir2", "/dir2/foo", + "/hiddenByUp", "/a", "/a/b", "/a/b/c", "/a/b/c/d", "/dir1", "/dir1/a", + "/dir1/a/b", "/dir1/foo", "/file1" }; + checkContents(vfs::recursive_directory_iterator(*O, "/", EC), + makeStringRefVector(Contents)); + } +} + +TEST(VirtualFileSystemTest, ThreeLevelIteration) { + IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); + IntrusiveRefCntPtr<DummyFileSystem> Middle(new DummyFileSystem()); + IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem()); + IntrusiveRefCntPtr<vfs::OverlayFileSystem> O( + new vfs::OverlayFileSystem(Lower)); + O->pushOverlay(Middle); + O->pushOverlay(Upper); + + std::error_code EC; + checkContents(O->dir_begin("/", EC), ArrayRef<StringRef>()); + + Middle->addRegularFile("/file2"); + checkContents(O->dir_begin("/", EC), ArrayRef<StringRef>("/file2")); + + Lower->addRegularFile("/file1"); + Upper->addRegularFile("/file3"); + { + const char *Contents[] = {"/file3", "/file2", "/file1"}; + checkContents(O->dir_begin("/", EC), makeStringRefVector(Contents)); + } +} + +TEST(VirtualFileSystemTest, HiddenInIteration) { + IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); + IntrusiveRefCntPtr<DummyFileSystem> Middle(new DummyFileSystem()); + IntrusiveRefCntPtr<DummyFileSystem> Upper(new DummyFileSystem()); + IntrusiveRefCntPtr<vfs::OverlayFileSystem> O( + new vfs::OverlayFileSystem(Lower)); + O->pushOverlay(Middle); + O->pushOverlay(Upper); + + std::error_code EC; + Lower->addRegularFile("/onlyInLow", sys::fs::owner_read); + Lower->addRegularFile("/hiddenByMid", sys::fs::owner_read); + Lower->addRegularFile("/hiddenByUp", sys::fs::owner_read); + Middle->addRegularFile("/onlyInMid", sys::fs::owner_write); + Middle->addRegularFile("/hiddenByMid", sys::fs::owner_write); + Middle->addRegularFile("/hiddenByUp", sys::fs::owner_write); + Upper->addRegularFile("/onlyInUp", sys::fs::owner_all); + Upper->addRegularFile("/hiddenByUp", sys::fs::owner_all); + { + const char *Contents[] = {"/hiddenByUp", "/onlyInUp", "/hiddenByMid", + "/onlyInMid", "/onlyInLow"}; + checkContents(O->dir_begin("/", EC), makeStringRefVector(Contents)); + } + + // Make sure we get the top-most entry + { + std::error_code EC; + vfs::directory_iterator I = O->dir_begin("/", EC), E; + for ( ; !EC && I != E; I.increment(EC)) + if (I->getName() == "/hiddenByUp") + break; + ASSERT_NE(E, I); + EXPECT_EQ(sys::fs::owner_all, I->getPermissions()); + } + { + std::error_code EC; + vfs::directory_iterator I = O->dir_begin("/", EC), E; + for ( ; !EC && I != E; I.increment(EC)) + if (I->getName() == "/hiddenByMid") + break; + ASSERT_NE(E, I); + EXPECT_EQ(sys::fs::owner_write, I->getPermissions()); + } +} + +// NOTE: in the tests below, we use '//root/' as our root directory, since it is +// a legal *absolute* path on Windows as well as *nix. +class VFSFromYAMLTest : public ::testing::Test { +public: + int NumDiagnostics; + + void SetUp() { + NumDiagnostics = 0; + } + + static void CountingDiagHandler(const SMDiagnostic &, void *Context) { + VFSFromYAMLTest *Test = static_cast<VFSFromYAMLTest *>(Context); + ++Test->NumDiagnostics; + } + + IntrusiveRefCntPtr<vfs::FileSystem> + getFromYAMLRawString(StringRef Content, + IntrusiveRefCntPtr<vfs::FileSystem> ExternalFS) { + MemoryBuffer *Buffer = MemoryBuffer::getMemBuffer(Content); + return getVFSFromYAML(Buffer, CountingDiagHandler, this, ExternalFS); + } + + IntrusiveRefCntPtr<vfs::FileSystem> getFromYAMLString( + StringRef Content, + IntrusiveRefCntPtr<vfs::FileSystem> ExternalFS = new DummyFileSystem()) { + std::string VersionPlusContent("{\n 'version':0,\n"); + VersionPlusContent += Content.slice(Content.find('{') + 1, StringRef::npos); + return getFromYAMLRawString(VersionPlusContent, ExternalFS); + } +}; + +TEST_F(VFSFromYAMLTest, BasicVFSFromYAML) { + IntrusiveRefCntPtr<vfs::FileSystem> FS; + FS = getFromYAMLString(""); + EXPECT_EQ(nullptr, FS.get()); + FS = getFromYAMLString("[]"); + EXPECT_EQ(nullptr, FS.get()); + FS = getFromYAMLString("'string'"); + EXPECT_EQ(nullptr, FS.get()); + EXPECT_EQ(3, NumDiagnostics); +} + +TEST_F(VFSFromYAMLTest, MappedFiles) { + IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); + Lower->addRegularFile("//root/foo/bar/a"); + IntrusiveRefCntPtr<vfs::FileSystem> FS = + getFromYAMLString("{ 'roots': [\n" + "{\n" + " 'type': 'directory',\n" + " 'name': '//root/',\n" + " 'contents': [ {\n" + " 'type': 'file',\n" + " 'name': 'file1',\n" + " 'external-contents': '//root/foo/bar/a'\n" + " },\n" + " {\n" + " 'type': 'file',\n" + " 'name': 'file2',\n" + " 'external-contents': '//root/foo/b'\n" + " }\n" + " ]\n" + "}\n" + "]\n" + "}", + Lower); + ASSERT_TRUE(FS.get() != nullptr); + + IntrusiveRefCntPtr<vfs::OverlayFileSystem> O( + new vfs::OverlayFileSystem(Lower)); + O->pushOverlay(FS); + + // file + ErrorOr<vfs::Status> S = O->status("//root/file1"); + ASSERT_FALSE(S.getError()); + EXPECT_EQ("//root/foo/bar/a", S->getName()); + + ErrorOr<vfs::Status> SLower = O->status("//root/foo/bar/a"); + EXPECT_EQ("//root/foo/bar/a", SLower->getName()); + EXPECT_TRUE(S->equivalent(*SLower)); + + // directory + S = O->status("//root/"); + ASSERT_FALSE(S.getError()); + EXPECT_TRUE(S->isDirectory()); + EXPECT_TRUE(S->equivalent(*O->status("//root/"))); // non-volatile UniqueID + + // broken mapping + EXPECT_EQ(O->status("//root/file2").getError(), + llvm::errc::no_such_file_or_directory); + EXPECT_EQ(0, NumDiagnostics); +} + +TEST_F(VFSFromYAMLTest, CaseInsensitive) { + IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); + Lower->addRegularFile("//root/foo/bar/a"); + IntrusiveRefCntPtr<vfs::FileSystem> FS = + getFromYAMLString("{ 'case-sensitive': 'false',\n" + " 'roots': [\n" + "{\n" + " 'type': 'directory',\n" + " 'name': '//root/',\n" + " 'contents': [ {\n" + " 'type': 'file',\n" + " 'name': 'XX',\n" + " 'external-contents': '//root/foo/bar/a'\n" + " }\n" + " ]\n" + "}]}", + Lower); + ASSERT_TRUE(FS.get() != nullptr); + + IntrusiveRefCntPtr<vfs::OverlayFileSystem> O( + new vfs::OverlayFileSystem(Lower)); + O->pushOverlay(FS); + + ErrorOr<vfs::Status> S = O->status("//root/XX"); + ASSERT_FALSE(S.getError()); + + ErrorOr<vfs::Status> SS = O->status("//root/xx"); + ASSERT_FALSE(SS.getError()); + EXPECT_TRUE(S->equivalent(*SS)); + SS = O->status("//root/xX"); + EXPECT_TRUE(S->equivalent(*SS)); + SS = O->status("//root/Xx"); + EXPECT_TRUE(S->equivalent(*SS)); + EXPECT_EQ(0, NumDiagnostics); +} + +TEST_F(VFSFromYAMLTest, CaseSensitive) { + IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); + Lower->addRegularFile("//root/foo/bar/a"); + IntrusiveRefCntPtr<vfs::FileSystem> FS = + getFromYAMLString("{ 'case-sensitive': 'true',\n" + " 'roots': [\n" + "{\n" + " 'type': 'directory',\n" + " 'name': '//root/',\n" + " 'contents': [ {\n" + " 'type': 'file',\n" + " 'name': 'XX',\n" + " 'external-contents': '//root/foo/bar/a'\n" + " }\n" + " ]\n" + "}]}", + Lower); + ASSERT_TRUE(FS.get() != nullptr); + + IntrusiveRefCntPtr<vfs::OverlayFileSystem> O( + new vfs::OverlayFileSystem(Lower)); + O->pushOverlay(FS); + + ErrorOr<vfs::Status> SS = O->status("//root/xx"); + EXPECT_EQ(SS.getError(), llvm::errc::no_such_file_or_directory); + SS = O->status("//root/xX"); + EXPECT_EQ(SS.getError(), llvm::errc::no_such_file_or_directory); + SS = O->status("//root/Xx"); + EXPECT_EQ(SS.getError(), llvm::errc::no_such_file_or_directory); + EXPECT_EQ(0, NumDiagnostics); +} + +TEST_F(VFSFromYAMLTest, IllegalVFSFile) { + IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); + + // invalid YAML at top-level + IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString("{]", Lower); + EXPECT_EQ(nullptr, FS.get()); + // invalid YAML in roots + FS = getFromYAMLString("{ 'roots':[}", Lower); + // invalid YAML in directory + FS = getFromYAMLString( + "{ 'roots':[ { 'name': 'foo', 'type': 'directory', 'contents': [}", + Lower); + EXPECT_EQ(nullptr, FS.get()); + + // invalid configuration + FS = getFromYAMLString("{ 'knobular': 'true', 'roots':[] }", Lower); + EXPECT_EQ(nullptr, FS.get()); + FS = getFromYAMLString("{ 'case-sensitive': 'maybe', 'roots':[] }", Lower); + EXPECT_EQ(nullptr, FS.get()); + + // invalid roots + FS = getFromYAMLString("{ 'roots':'' }", Lower); + EXPECT_EQ(nullptr, FS.get()); + FS = getFromYAMLString("{ 'roots':{} }", Lower); + EXPECT_EQ(nullptr, FS.get()); + + // invalid entries + FS = getFromYAMLString( + "{ 'roots':[ { 'type': 'other', 'name': 'me', 'contents': '' }", Lower); + EXPECT_EQ(nullptr, FS.get()); + FS = getFromYAMLString("{ 'roots':[ { 'type': 'file', 'name': [], " + "'external-contents': 'other' }", + Lower); + EXPECT_EQ(nullptr, FS.get()); + FS = getFromYAMLString( + "{ 'roots':[ { 'type': 'file', 'name': 'me', 'external-contents': [] }", + Lower); + EXPECT_EQ(nullptr, FS.get()); + FS = getFromYAMLString( + "{ 'roots':[ { 'type': 'file', 'name': 'me', 'external-contents': {} }", + Lower); + EXPECT_EQ(nullptr, FS.get()); + FS = getFromYAMLString( + "{ 'roots':[ { 'type': 'directory', 'name': 'me', 'contents': {} }", + Lower); + EXPECT_EQ(nullptr, FS.get()); + FS = getFromYAMLString( + "{ 'roots':[ { 'type': 'directory', 'name': 'me', 'contents': '' }", + Lower); + EXPECT_EQ(nullptr, FS.get()); + FS = getFromYAMLString( + "{ 'roots':[ { 'thingy': 'directory', 'name': 'me', 'contents': [] }", + Lower); + EXPECT_EQ(nullptr, FS.get()); + + // missing mandatory fields + FS = getFromYAMLString("{ 'roots':[ { 'type': 'file', 'name': 'me' }", Lower); + EXPECT_EQ(nullptr, FS.get()); + FS = getFromYAMLString( + "{ 'roots':[ { 'type': 'file', 'external-contents': 'other' }", Lower); + EXPECT_EQ(nullptr, FS.get()); + FS = getFromYAMLString("{ 'roots':[ { 'name': 'me', 'contents': [] }", Lower); + EXPECT_EQ(nullptr, FS.get()); + + // duplicate keys + FS = getFromYAMLString("{ 'roots':[], 'roots':[] }", Lower); + EXPECT_EQ(nullptr, FS.get()); + FS = getFromYAMLString( + "{ 'case-sensitive':'true', 'case-sensitive':'true', 'roots':[] }", + Lower); + EXPECT_EQ(nullptr, FS.get()); + FS = + getFromYAMLString("{ 'roots':[{'name':'me', 'name':'you', 'type':'file', " + "'external-contents':'blah' } ] }", + Lower); + EXPECT_EQ(nullptr, FS.get()); + + // missing version + FS = getFromYAMLRawString("{ 'roots':[] }", Lower); + EXPECT_EQ(nullptr, FS.get()); + + // bad version number + FS = getFromYAMLRawString("{ 'version':'foo', 'roots':[] }", Lower); + EXPECT_EQ(nullptr, FS.get()); + FS = getFromYAMLRawString("{ 'version':-1, 'roots':[] }", Lower); + EXPECT_EQ(nullptr, FS.get()); + FS = getFromYAMLRawString("{ 'version':100000, 'roots':[] }", Lower); + EXPECT_EQ(nullptr, FS.get()); + EXPECT_EQ(24, NumDiagnostics); +} + +TEST_F(VFSFromYAMLTest, UseExternalName) { + IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); + Lower->addRegularFile("//root/external/file"); + + IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString( + "{ 'roots': [\n" + " { 'type': 'file', 'name': '//root/A',\n" + " 'external-contents': '//root/external/file'\n" + " },\n" + " { 'type': 'file', 'name': '//root/B',\n" + " 'use-external-name': true,\n" + " 'external-contents': '//root/external/file'\n" + " },\n" + " { 'type': 'file', 'name': '//root/C',\n" + " 'use-external-name': false,\n" + " 'external-contents': '//root/external/file'\n" + " }\n" + "] }", Lower); + ASSERT_TRUE(nullptr != FS.get()); + + // default true + EXPECT_EQ("//root/external/file", FS->status("//root/A")->getName()); + // explicit + EXPECT_EQ("//root/external/file", FS->status("//root/B")->getName()); + EXPECT_EQ("//root/C", FS->status("//root/C")->getName()); + + // global configuration + FS = getFromYAMLString( + "{ 'use-external-names': false,\n" + " 'roots': [\n" + " { 'type': 'file', 'name': '//root/A',\n" + " 'external-contents': '//root/external/file'\n" + " },\n" + " { 'type': 'file', 'name': '//root/B',\n" + " 'use-external-name': true,\n" + " 'external-contents': '//root/external/file'\n" + " },\n" + " { 'type': 'file', 'name': '//root/C',\n" + " 'use-external-name': false,\n" + " 'external-contents': '//root/external/file'\n" + " }\n" + "] }", Lower); + ASSERT_TRUE(nullptr != FS.get()); + + // default + EXPECT_EQ("//root/A", FS->status("//root/A")->getName()); + // explicit + EXPECT_EQ("//root/external/file", FS->status("//root/B")->getName()); + EXPECT_EQ("//root/C", FS->status("//root/C")->getName()); +} + +TEST_F(VFSFromYAMLTest, MultiComponentPath) { + IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); + Lower->addRegularFile("//root/other"); + + // file in roots + IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString( + "{ 'roots': [\n" + " { 'type': 'file', 'name': '//root/path/to/file',\n" + " 'external-contents': '//root/other' }]\n" + "}", Lower); + ASSERT_TRUE(nullptr != FS.get()); + EXPECT_FALSE(FS->status("//root/path/to/file").getError()); + EXPECT_FALSE(FS->status("//root/path/to").getError()); + EXPECT_FALSE(FS->status("//root/path").getError()); + EXPECT_FALSE(FS->status("//root/").getError()); + + // at the start + FS = getFromYAMLString( + "{ 'roots': [\n" + " { 'type': 'directory', 'name': '//root/path/to',\n" + " 'contents': [ { 'type': 'file', 'name': 'file',\n" + " 'external-contents': '//root/other' }]}]\n" + "}", Lower); + ASSERT_TRUE(nullptr != FS.get()); + EXPECT_FALSE(FS->status("//root/path/to/file").getError()); + EXPECT_FALSE(FS->status("//root/path/to").getError()); + EXPECT_FALSE(FS->status("//root/path").getError()); + EXPECT_FALSE(FS->status("//root/").getError()); + + // at the end + FS = getFromYAMLString( + "{ 'roots': [\n" + " { 'type': 'directory', 'name': '//root/',\n" + " 'contents': [ { 'type': 'file', 'name': 'path/to/file',\n" + " 'external-contents': '//root/other' }]}]\n" + "}", Lower); + ASSERT_TRUE(nullptr != FS.get()); + EXPECT_FALSE(FS->status("//root/path/to/file").getError()); + EXPECT_FALSE(FS->status("//root/path/to").getError()); + EXPECT_FALSE(FS->status("//root/path").getError()); + EXPECT_FALSE(FS->status("//root/").getError()); +} + +TEST_F(VFSFromYAMLTest, TrailingSlashes) { + IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); + Lower->addRegularFile("//root/other"); + + // file in roots + IntrusiveRefCntPtr<vfs::FileSystem> FS = getFromYAMLString( + "{ 'roots': [\n" + " { 'type': 'directory', 'name': '//root/path/to////',\n" + " 'contents': [ { 'type': 'file', 'name': 'file',\n" + " 'external-contents': '//root/other' }]}]\n" + "}", Lower); + ASSERT_TRUE(nullptr != FS.get()); + EXPECT_FALSE(FS->status("//root/path/to/file").getError()); + EXPECT_FALSE(FS->status("//root/path/to").getError()); + EXPECT_FALSE(FS->status("//root/path").getError()); + EXPECT_FALSE(FS->status("//root/").getError()); +} + +TEST_F(VFSFromYAMLTest, DirectoryIteration) { + IntrusiveRefCntPtr<DummyFileSystem> Lower(new DummyFileSystem()); + Lower->addDirectory("//root/"); + Lower->addDirectory("//root/foo"); + Lower->addDirectory("//root/foo/bar"); + Lower->addRegularFile("//root/foo/bar/a"); + Lower->addRegularFile("//root/foo/bar/b"); + Lower->addRegularFile("//root/file3"); + IntrusiveRefCntPtr<vfs::FileSystem> FS = + getFromYAMLString("{ 'use-external-names': false,\n" + " 'roots': [\n" + "{\n" + " 'type': 'directory',\n" + " 'name': '//root/',\n" + " 'contents': [ {\n" + " 'type': 'file',\n" + " 'name': 'file1',\n" + " 'external-contents': '//root/foo/bar/a'\n" + " },\n" + " {\n" + " 'type': 'file',\n" + " 'name': 'file2',\n" + " 'external-contents': '//root/foo/bar/b'\n" + " }\n" + " ]\n" + "}\n" + "]\n" + "}", + Lower); + ASSERT_TRUE(FS.get() != NULL); + + IntrusiveRefCntPtr<vfs::OverlayFileSystem> O( + new vfs::OverlayFileSystem(Lower)); + O->pushOverlay(FS); + + std::error_code EC; + { + const char *Contents[] = {"//root/file1", "//root/file2", "//root/file3", + "//root/foo"}; + checkContents(O->dir_begin("//root/", EC), makeStringRefVector(Contents)); + } + + { + const char *Contents[] = {"//root/foo/bar/a", "//root/foo/bar/b"}; + checkContents(O->dir_begin("//root/foo/bar", EC), + makeStringRefVector(Contents)); + } +} |