From d51ba0b39f210da53d4edbf386d4df6e3e8b18e2 Mon Sep 17 00:00:00 2001 From: Ben Langmuir Date: Fri, 21 Feb 2014 23:39:37 +0000 Subject: Add a VFSFromYAML class and a parser to create it Provides a way to create a virtual file system using a YAML file that supports mapping a file to a path on an 'external' file system. The external file system will typically be the 'real' file system, but for testing it can be changed. A future patch will add a clang option to allow the user to specify such a file and overlay it, but for now this code is only exercised by the unit tests. Differential Revision: http://llvm-reviews.chandlerc.com/D2835 llvm-svn: 201905 --- clang/unittests/Basic/VirtualFileSystemTest.cpp | 278 ++++++++++++++++++++++-- 1 file changed, 263 insertions(+), 15 deletions(-) (limited to 'clang/unittests/Basic/VirtualFileSystemTest.cpp') diff --git a/clang/unittests/Basic/VirtualFileSystemTest.cpp b/clang/unittests/Basic/VirtualFileSystemTest.cpp index e545e9c0597..50d11019f30 100644 --- a/clang/unittests/Basic/VirtualFileSystemTest.cpp +++ b/clang/unittests/Basic/VirtualFileSystemTest.cpp @@ -8,7 +8,9 @@ //===----------------------------------------------------------------------===// #include "clang/Basic/VirtualFileSystem.h" +#include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Path.h" +#include "llvm/Support/SourceMgr.h" #include "gtest/gtest.h" #include using namespace clang; @@ -31,7 +33,7 @@ public: ErrorOr status(const Twine &Path) { std::map::iterator I = - FilesAndDirs.find(Path.str()); + FilesAndDirs.find(Path.str()); if (I == FilesAndDirs.end()) return error_code(errc::no_such_file_or_directory, posix_category()); return I->second; @@ -50,13 +52,13 @@ public: FilesAndDirs[Path] = Status; } - void addRegularFile(StringRef Path, sys::fs::perms Perms=sys::fs::all_all) { + 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) { + 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); @@ -70,7 +72,7 @@ public: }; } // end anonymous namespace -TEST(VirtualFileSystemTest, statusQueries) { +TEST(VirtualFileSystemTest, StatusQueries) { IntrusiveRefCntPtr D(new DummyFileSystem()); ErrorOr Status((error_code())); @@ -110,7 +112,7 @@ TEST(VirtualFileSystemTest, statusQueries) { EXPECT_FALSE(Status->equivalent(*Status2)); } -TEST(VirtualFileSystemTest, baseOnlyOverlay) { +TEST(VirtualFileSystemTest, BaseOnlyOverlay) { IntrusiveRefCntPtr D(new DummyFileSystem()); ErrorOr Status((error_code())); EXPECT_FALSE(Status = D->status("/foo")); @@ -128,17 +130,18 @@ TEST(VirtualFileSystemTest, baseOnlyOverlay) { EXPECT_TRUE(Status->equivalent(*Status2)); } -TEST(VirtualFileSystemTest, overlayFiles) { +TEST(VirtualFileSystemTest, OverlayFiles) { IntrusiveRefCntPtr Base(new DummyFileSystem()); IntrusiveRefCntPtr Middle(new DummyFileSystem()); IntrusiveRefCntPtr Top(new DummyFileSystem()); - IntrusiveRefCntPtr O(new vfs::OverlayFileSystem(Base)); + IntrusiveRefCntPtr O( + new vfs::OverlayFileSystem(Base)); O->pushOverlay(Middle); O->pushOverlay(Top); ErrorOr Status1((error_code())), Status2((error_code())), - Status3((error_code())), StatusB((error_code())), - StatusM((error_code())), StatusT((error_code())); + Status3((error_code())), StatusB((error_code())), StatusM((error_code())), + StatusT((error_code())); Base->addRegularFile("/foo"); StatusB = Base->status("/foo"); @@ -165,11 +168,11 @@ TEST(VirtualFileSystemTest, overlayFiles) { EXPECT_FALSE(Status1->equivalent(*Status3)); } -TEST(VirtualFileSystemTest, overlayDirsNonMerged) { +TEST(VirtualFileSystemTest, OverlayDirsNonMerged) { IntrusiveRefCntPtr Lower(new DummyFileSystem()); IntrusiveRefCntPtr Upper(new DummyFileSystem()); - IntrusiveRefCntPtr - O(new vfs::OverlayFileSystem(Lower)); + IntrusiveRefCntPtr O( + new vfs::OverlayFileSystem(Lower)); O->pushOverlay(Upper); Lower->addDirectory("/lower-only"); @@ -189,12 +192,12 @@ TEST(VirtualFileSystemTest, overlayDirsNonMerged) { EXPECT_TRUE(Status1->equivalent(*Status2)); } -TEST(VirtualFileSystemTest, mergedDirPermissions) { +TEST(VirtualFileSystemTest, MergedDirPermissions) { // merged directories get the permissions of the upper dir IntrusiveRefCntPtr Lower(new DummyFileSystem()); IntrusiveRefCntPtr Upper(new DummyFileSystem()); - IntrusiveRefCntPtr - O(new vfs::OverlayFileSystem(Lower)); + IntrusiveRefCntPtr O( + new vfs::OverlayFileSystem(Lower)); O->pushOverlay(Upper); ErrorOr Status((error_code())); @@ -214,3 +217,248 @@ TEST(VirtualFileSystemTest, mergedDirPermissions) { ASSERT_EQ(errc::success, Status.getError()); EXPECT_EQ(0200, Status->getPermissions()); } + +static int NumDiagnostics = 0; +static void CountingDiagHandler(const SMDiagnostic &, void *) { + ++NumDiagnostics; +} + +static IntrusiveRefCntPtr +getFromYAMLRawString(StringRef Content, + IntrusiveRefCntPtr ExternalFS) { + MemoryBuffer *Buffer = MemoryBuffer::getMemBuffer(Content); + return getVFSFromYAML(Buffer, CountingDiagHandler, ExternalFS); +} + +static IntrusiveRefCntPtr getFromYAMLString( + StringRef Content, + IntrusiveRefCntPtr ExternalFS = new DummyFileSystem()) { + std::string VersionPlusContent("{\n 'version':0,\n"); + VersionPlusContent += Content.slice(Content.find('{') + 1, StringRef::npos); + return getFromYAMLRawString(VersionPlusContent, ExternalFS); +} + +TEST(VirtualFileSystemTest, BasicVFSFromYAML) { + NumDiagnostics = 0; + IntrusiveRefCntPtr FS; + FS = getFromYAMLString(""); + EXPECT_EQ(NULL, FS.getPtr()); + FS = getFromYAMLString("[]"); + EXPECT_EQ(NULL, FS.getPtr()); + FS = getFromYAMLString("'string'"); + EXPECT_EQ(NULL, FS.getPtr()); + EXPECT_EQ(3, NumDiagnostics); +} + +TEST(VirtualFileSystemTest, MappedFiles) { + NumDiagnostics = 0; + IntrusiveRefCntPtr Lower(new DummyFileSystem()); + Lower->addRegularFile("/foo/bar/a"); + IntrusiveRefCntPtr FS = + getFromYAMLString("{ 'roots': [\n" + "{\n" + " 'type': 'directory',\n" + " 'name': '/',\n" + " 'contents': [ {\n" + " 'type': 'file',\n" + " 'name': 'file1',\n" + " 'external-contents': '/foo/bar/a'\n" + " },\n" + " {\n" + " 'type': 'file',\n" + " 'name': 'file2',\n" + " 'external-contents': '/foo/b'\n" + " }\n" + " ]\n" + "}\n" + "]\n" + "}", + Lower); + ASSERT_TRUE(FS.getPtr()); + + IntrusiveRefCntPtr O( + new vfs::OverlayFileSystem(Lower)); + O->pushOverlay(FS); + + // file + ErrorOr S = O->status("/file1"); + ASSERT_EQ(errc::success, S.getError()); + EXPECT_EQ("/file1", S->getName()); + EXPECT_EQ("/foo/bar/a", S->getExternalName()); + + ErrorOr SLower = O->status("/foo/bar/a"); + EXPECT_EQ("/foo/bar/a", SLower->getName()); + EXPECT_TRUE(S->equivalent(*SLower)); + + // directory + S = O->status("/"); + ASSERT_EQ(errc::success, S.getError()); + EXPECT_TRUE(S->isDirectory()); + EXPECT_TRUE(S->equivalent(*O->status("/"))); // non-volatile UniqueID + + // broken mapping + EXPECT_EQ(errc::no_such_file_or_directory, O->status("/file2").getError()); + EXPECT_EQ(0, NumDiagnostics); +} + +TEST(VirtualFileSystemTest, CaseInsensitive) { + NumDiagnostics = 0; + IntrusiveRefCntPtr Lower(new DummyFileSystem()); + Lower->addRegularFile("/foo/bar/a"); + IntrusiveRefCntPtr FS = + getFromYAMLString("{ 'case-sensitive': 'false',\n" + " 'roots': [\n" + "{\n" + " 'type': 'directory',\n" + " 'name': '/',\n" + " 'contents': [ {\n" + " 'type': 'file',\n" + " 'name': 'XX',\n" + " 'external-contents': '/foo/bar/a'\n" + " }\n" + " ]\n" + "}]}", + Lower); + ASSERT_TRUE(FS.getPtr()); + + IntrusiveRefCntPtr O( + new vfs::OverlayFileSystem(Lower)); + O->pushOverlay(FS); + + ErrorOr S = O->status("/XX"); + ASSERT_EQ(errc::success, S.getError()); + + ErrorOr SS = O->status("/xx"); + ASSERT_EQ(errc::success, SS.getError()); + EXPECT_TRUE(S->equivalent(*SS)); + SS = O->status("/xX"); + EXPECT_TRUE(S->equivalent(*SS)); + SS = O->status("/Xx"); + EXPECT_TRUE(S->equivalent(*SS)); + EXPECT_EQ(0, NumDiagnostics); +} + +TEST(VirtualFileSystemTest, CaseSensitive) { + NumDiagnostics = 0; + IntrusiveRefCntPtr Lower(new DummyFileSystem()); + Lower->addRegularFile("/foo/bar/a"); + IntrusiveRefCntPtr FS = + getFromYAMLString("{ 'case-sensitive': 'true',\n" + " 'roots': [\n" + "{\n" + " 'type': 'directory',\n" + " 'name': '/',\n" + " 'contents': [ {\n" + " 'type': 'file',\n" + " 'name': 'XX',\n" + " 'external-contents': '/foo/bar/a'\n" + " }\n" + " ]\n" + "}]}", + Lower); + ASSERT_TRUE(FS.getPtr()); + + IntrusiveRefCntPtr O( + new vfs::OverlayFileSystem(Lower)); + O->pushOverlay(FS); + + ErrorOr SS = O->status("/xx"); + EXPECT_EQ(errc::no_such_file_or_directory, SS.getError()); + SS = O->status("/xX"); + EXPECT_EQ(errc::no_such_file_or_directory, SS.getError()); + SS = O->status("/Xx"); + EXPECT_EQ(errc::no_such_file_or_directory, SS.getError()); + EXPECT_EQ(0, NumDiagnostics); +} + +TEST(VirtualFileSystemTest, IllegalVFSFile) { + NumDiagnostics = 0; + IntrusiveRefCntPtr Lower(new DummyFileSystem()); + + // invalid YAML at top-level + IntrusiveRefCntPtr FS = getFromYAMLString("{]", Lower); + EXPECT_FALSE(FS.getPtr()); + // invalid YAML in roots + FS = getFromYAMLString("{ 'roots':[}", Lower); + // invalid YAML in directory + FS = getFromYAMLString( + "{ 'roots':[ { 'name': 'foo', 'type': 'directory', 'contents': [}", + Lower); + EXPECT_FALSE(FS.getPtr()); + + // invalid configuration + FS = getFromYAMLString("{ 'knobular': 'true', 'roots':[] }", Lower); + EXPECT_FALSE(FS.getPtr()); + FS = getFromYAMLString("{ 'case-sensitive': 'maybe', 'roots':[] }", Lower); + EXPECT_FALSE(FS.getPtr()); + + // invalid roots + FS = getFromYAMLString("{ 'roots':'' }", Lower); + EXPECT_FALSE(FS.getPtr()); + FS = getFromYAMLString("{ 'roots':{} }", Lower); + EXPECT_FALSE(FS.getPtr()); + + // invalid entries + FS = getFromYAMLString( + "{ 'roots':[ { 'type': 'other', 'name': 'me', 'contents': '' }", Lower); + EXPECT_FALSE(FS.getPtr()); + FS = getFromYAMLString("{ 'roots':[ { 'type': 'file', 'name': [], " + "'external-contents': 'other' }", + Lower); + EXPECT_FALSE(FS.getPtr()); + FS = getFromYAMLString( + "{ 'roots':[ { 'type': 'file', 'name': 'me', 'external-contents': [] }", + Lower); + EXPECT_FALSE(FS.getPtr()); + FS = getFromYAMLString( + "{ 'roots':[ { 'type': 'file', 'name': 'me', 'external-contents': {} }", + Lower); + EXPECT_FALSE(FS.getPtr()); + FS = getFromYAMLString( + "{ 'roots':[ { 'type': 'directory', 'name': 'me', 'contents': {} }", + Lower); + EXPECT_FALSE(FS.getPtr()); + FS = getFromYAMLString( + "{ 'roots':[ { 'type': 'directory', 'name': 'me', 'contents': '' }", + Lower); + EXPECT_FALSE(FS.getPtr()); + FS = getFromYAMLString( + "{ 'roots':[ { 'thingy': 'directory', 'name': 'me', 'contents': [] }", + Lower); + EXPECT_FALSE(FS.getPtr()); + + // missing mandatory fields + FS = getFromYAMLString("{ 'roots':[ { 'type': 'file', 'name': 'me' }", Lower); + EXPECT_FALSE(FS.getPtr()); + FS = getFromYAMLString( + "{ 'roots':[ { 'type': 'file', 'external-contents': 'other' }", Lower); + EXPECT_FALSE(FS.getPtr()); + FS = getFromYAMLString("{ 'roots':[ { 'name': 'me', 'contents': [] }", Lower); + EXPECT_FALSE(FS.getPtr()); + + // duplicate keys + FS = getFromYAMLString("{ 'roots':[], 'roots':[] }", Lower); + EXPECT_FALSE(FS.getPtr()); + FS = getFromYAMLString( + "{ 'case-sensitive':'true', 'case-sensitive':'true', 'roots':[] }", + Lower); + EXPECT_FALSE(FS.getPtr()); + FS = + getFromYAMLString("{ 'roots':[{'name':'me', 'name':'you', 'type':'file', " + "'external-contents':'blah' } ] }", + Lower); + EXPECT_FALSE(FS.getPtr()); + + // missing version + FS = getFromYAMLRawString("{ 'roots':[] }", Lower); + EXPECT_FALSE(FS.getPtr()); + + // bad version number + FS = getFromYAMLRawString("{ 'version':'foo', 'roots':[] }", Lower); + EXPECT_FALSE(FS.getPtr()); + FS = getFromYAMLRawString("{ 'version':-1, 'roots':[] }", Lower); + EXPECT_FALSE(FS.getPtr()); + FS = getFromYAMLRawString("{ 'version':100000, 'roots':[] }", Lower); + EXPECT_FALSE(FS.getPtr()); + EXPECT_EQ(24, NumDiagnostics); +} -- cgit v1.2.3