diff options
Diffstat (limited to 'clang/unittests/Basic')
-rw-r--r-- | clang/unittests/Basic/FileManagerTest.cpp | 223 | ||||
-rw-r--r-- | clang/unittests/Basic/Makefile | 15 | ||||
-rw-r--r-- | clang/unittests/Basic/SourceManagerTest.cpp | 296 |
3 files changed, 534 insertions, 0 deletions
diff --git a/clang/unittests/Basic/FileManagerTest.cpp b/clang/unittests/Basic/FileManagerTest.cpp new file mode 100644 index 0000000..91998b6 --- /dev/null +++ b/clang/unittests/Basic/FileManagerTest.cpp @@ -0,0 +1,223 @@ +//===- unittests/Basic/FileMangerTest.cpp ------------ FileManger 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/FileSystemOptions.h" +#include "clang/Basic/FileSystemStatCache.h" +#include "clang/Basic/FileManager.h" + +#include "gtest/gtest.h" + +using namespace llvm; +using namespace clang; + +namespace { + +// Used to create a fake file system for running the tests with such +// that the tests are not affected by the structure/contents of the +// file system on the machine running the tests. +class FakeStatCache : public FileSystemStatCache { +private: + // Maps a file/directory path to its desired stat result. Anything + // not in this map is considered to not exist in the file system. + llvm::StringMap<struct stat, llvm::BumpPtrAllocator> StatCalls; + + void InjectFileOrDirectory(const char *Path, ino_t INode, bool IsFile) { + struct stat statBuf; + memset(&statBuf, 0, sizeof(statBuf)); + statBuf.st_dev = 1; +#ifndef _WIN32 // struct stat has no st_ino field on Windows. + statBuf.st_ino = INode; +#endif + statBuf.st_mode = IsFile ? (0777 | S_IFREG) // a regular file + : (0777 | S_IFDIR); // a directory + StatCalls[Path] = statBuf; + } + +public: + // Inject a file with the given inode value to the fake file system. + void InjectFile(const char *Path, ino_t INode) { + InjectFileOrDirectory(Path, INode, /*IsFile=*/true); + } + + // Inject a directory with the given inode value to the fake file system. + void InjectDirectory(const char *Path, ino_t INode) { + InjectFileOrDirectory(Path, INode, /*IsFile=*/false); + } + + // Implement FileSystemStatCache::getStat(). + virtual LookupResult getStat(const char *Path, struct stat &StatBuf, + int *FileDescriptor) { + if (StatCalls.count(Path) != 0) { + StatBuf = StatCalls[Path]; + return CacheExists; + } + + return CacheMissing; // This means the file/directory doesn't exist. + } +}; + +// The test fixture. +class FileManagerTest : public ::testing::Test { + protected: + FileManagerTest() : manager(options) { + } + + FileSystemOptions options; + FileManager manager; +}; + +// When a virtual file is added, its getDir() field is set correctly +// (not NULL, correct name). +TEST_F(FileManagerTest, getVirtualFileSetsTheDirFieldCorrectly) { + const FileEntry *file = manager.getVirtualFile("foo.cpp", 42, 0); + ASSERT_TRUE(file != NULL); + + const DirectoryEntry *dir = file->getDir(); + ASSERT_TRUE(dir != NULL); + EXPECT_STREQ(".", dir->getName()); + + file = manager.getVirtualFile("x/y/z.cpp", 42, 0); + ASSERT_TRUE(file != NULL); + + dir = file->getDir(); + ASSERT_TRUE(dir != NULL); + EXPECT_STREQ("x/y", dir->getName()); +} + +// Before any virtual file is added, no virtual directory exists. +TEST_F(FileManagerTest, NoVirtualDirectoryExistsBeforeAVirtualFileIsAdded) { + // An empty FakeStatCache causes all stat calls made by the + // FileManager to report "file/directory doesn't exist". This + // avoids the possibility of the result of this test being affected + // 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")); +} + +// When a virtual file is added, all of its ancestors should be created. +TEST_F(FileManagerTest, getVirtualFileCreatesDirectoryEntriesForAncestors) { + // Fake an empty real file system. + manager.addStatCache(new FakeStatCache); + + manager.getVirtualFile("virtual/dir/bar.h", 100, 0); + EXPECT_EQ(NULL, manager.getDirectory("virtual/dir/foo")); + + const DirectoryEntry *dir = manager.getDirectory("virtual/dir"); + ASSERT_TRUE(dir != NULL); + EXPECT_STREQ("virtual/dir", dir->getName()); + + dir = manager.getDirectory("virtual"); + ASSERT_TRUE(dir != NULL); + EXPECT_STREQ("virtual", dir->getName()); +} + +// getFile() returns non-NULL if a real file exists at the given path. +TEST_F(FileManagerTest, getFileReturnsValidFileEntryForExistingRealFile) { + // Inject fake files into the file system. + FakeStatCache *statCache = new FakeStatCache; + statCache->InjectDirectory("/tmp", 42); + statCache->InjectFile("/tmp/test", 43); + manager.addStatCache(statCache); + + const FileEntry *file = manager.getFile("/tmp/test"); + ASSERT_TRUE(file != NULL); + EXPECT_STREQ("/tmp/test", file->getName()); + + const DirectoryEntry *dir = file->getDir(); + ASSERT_TRUE(dir != NULL); + EXPECT_STREQ("/tmp", dir->getName()); +} + +// getFile() returns non-NULL if a virtual file exists at the given path. +TEST_F(FileManagerTest, getFileReturnsValidFileEntryForExistingVirtualFile) { + // Fake an empty real file system. + manager.addStatCache(new FakeStatCache); + + manager.getVirtualFile("virtual/dir/bar.h", 100, 0); + const FileEntry *file = manager.getFile("virtual/dir/bar.h"); + ASSERT_TRUE(file != NULL); + EXPECT_STREQ("virtual/dir/bar.h", file->getName()); + + const DirectoryEntry *dir = file->getDir(); + ASSERT_TRUE(dir != NULL); + EXPECT_STREQ("virtual/dir", dir->getName()); +} + +// getFile() returns different FileEntries for different paths when +// there's no aliasing. +TEST_F(FileManagerTest, getFileReturnsDifferentFileEntriesForDifferentFiles) { + // Inject two fake files into the file system. Different inodes + // mean the files are not symlinked together. + FakeStatCache *statCache = new FakeStatCache; + statCache->InjectDirectory(".", 41); + statCache->InjectFile("foo.cpp", 42); + statCache->InjectFile("bar.cpp", 43); + manager.addStatCache(statCache); + + const FileEntry *fileFoo = manager.getFile("foo.cpp"); + const FileEntry *fileBar = manager.getFile("bar.cpp"); + ASSERT_TRUE(fileFoo != NULL); + ASSERT_TRUE(fileBar != NULL); + EXPECT_NE(fileFoo, fileBar); +} + +// getFile() returns NULL if neither a real file nor a virtual file +// exists at the given path. +TEST_F(FileManagerTest, getFileReturnsNULLForNonexistentFile) { + // Inject a fake foo.cpp into the file system. + FakeStatCache *statCache = new FakeStatCache; + statCache->InjectDirectory(".", 41); + statCache->InjectFile("foo.cpp", 42); + manager.addStatCache(statCache); + + // Create a virtual bar.cpp file. + manager.getVirtualFile("bar.cpp", 200, 0); + + const FileEntry *file = manager.getFile("xyz.txt"); + EXPECT_EQ(NULL, file); +} + +// The following tests apply to Unix-like system only. + +#ifndef _WIN32 + +// getFile() returns the same FileEntry for real files that are aliases. +TEST_F(FileManagerTest, getFileReturnsSameFileEntryForAliasedRealFiles) { + // Inject two real files with the same inode. + FakeStatCache *statCache = new FakeStatCache; + statCache->InjectDirectory("abc", 41); + statCache->InjectFile("abc/foo.cpp", 42); + statCache->InjectFile("abc/bar.cpp", 42); + manager.addStatCache(statCache); + + EXPECT_EQ(manager.getFile("abc/foo.cpp"), manager.getFile("abc/bar.cpp")); +} + +// getFile() returns the same FileEntry for virtual files that have +// corresponding real files that are aliases. +TEST_F(FileManagerTest, getFileReturnsSameFileEntryForAliasedVirtualFiles) { + // Inject two real files with the same inode. + FakeStatCache *statCache = new FakeStatCache; + statCache->InjectDirectory("abc", 41); + statCache->InjectFile("abc/foo.cpp", 42); + statCache->InjectFile("abc/bar.cpp", 42); + manager.addStatCache(statCache); + + manager.getVirtualFile("abc/foo.cpp", 100, 0); + manager.getVirtualFile("abc/bar.cpp", 200, 0); + + EXPECT_EQ(manager.getFile("abc/foo.cpp"), manager.getFile("abc/bar.cpp")); +} + +#endif // !_WIN32 + +} // anonymous namespace diff --git a/clang/unittests/Basic/Makefile b/clang/unittests/Basic/Makefile new file mode 100644 index 0000000..82de790 --- /dev/null +++ b/clang/unittests/Basic/Makefile @@ -0,0 +1,15 @@ +##===- unittests/Basic/Makefile ----------------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +CLANG_LEVEL = ../.. +TESTNAME = Basic +LINK_COMPONENTS := support mc +USEDLIBS = clangLex.a clangBasic.a + +include $(CLANG_LEVEL)/unittests/Makefile diff --git a/clang/unittests/Basic/SourceManagerTest.cpp b/clang/unittests/Basic/SourceManagerTest.cpp new file mode 100644 index 0000000..429b58d --- /dev/null +++ b/clang/unittests/Basic/SourceManagerTest.cpp @@ -0,0 +1,296 @@ +//===- unittests/Basic/SourceManagerTest.cpp ------ SourceManager 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/SourceManager.h" +#include "clang/Basic/FileManager.h" +#include "clang/Basic/Diagnostic.h" +#include "clang/Basic/LangOptions.h" +#include "clang/Basic/TargetOptions.h" +#include "clang/Basic/TargetInfo.h" +#include "clang/Lex/ModuleLoader.h" +#include "clang/Lex/HeaderSearch.h" +#include "clang/Lex/Preprocessor.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/Config/config.h" + +#include "gtest/gtest.h" + +using namespace llvm; +using namespace clang; + +namespace { + +// The test fixture. +class SourceManagerTest : public ::testing::Test { +protected: + SourceManagerTest() + : FileMgr(FileMgrOpts), + DiagID(new DiagnosticIDs()), + Diags(DiagID, new IgnoringDiagConsumer()), + SourceMgr(Diags, FileMgr) { + TargetOpts.Triple = "x86_64-apple-darwin11.1.0"; + Target = TargetInfo::CreateTargetInfo(Diags, TargetOpts); + } + + FileSystemOptions FileMgrOpts; + FileManager FileMgr; + IntrusiveRefCntPtr<DiagnosticIDs> DiagID; + DiagnosticsEngine Diags; + SourceManager SourceMgr; + LangOptions LangOpts; + TargetOptions TargetOpts; + IntrusiveRefCntPtr<TargetInfo> Target; +}; + +class VoidModuleLoader : public ModuleLoader { + virtual Module *loadModule(SourceLocation ImportLoc, ModuleIdPath Path, + Module::NameVisibilityKind Visibility, + bool IsInclusionDirective) { + return 0; + } +}; + +TEST_F(SourceManagerTest, isBeforeInTranslationUnit) { + const char *source = + "#define M(x) [x]\n" + "M(foo)"; + MemoryBuffer *buf = MemoryBuffer::getMemBuffer(source); + FileID mainFileID = SourceMgr.createMainFileIDForMemBuffer(buf); + + VoidModuleLoader ModLoader; + HeaderSearch HeaderInfo(FileMgr, Diags, LangOpts, &*Target); + Preprocessor PP(Diags, LangOpts, + Target.getPtr(), + SourceMgr, HeaderInfo, ModLoader, + /*IILookup =*/ 0, + /*OwnsHeaderSearch =*/false, + /*DelayInitialization =*/ false); + PP.EnterMainSourceFile(); + + std::vector<Token> toks; + while (1) { + Token tok; + PP.Lex(tok); + if (tok.is(tok::eof)) + break; + toks.push_back(tok); + } + + // Make sure we got the tokens that we expected. + ASSERT_EQ(3U, toks.size()); + ASSERT_EQ(tok::l_square, toks[0].getKind()); + ASSERT_EQ(tok::identifier, toks[1].getKind()); + ASSERT_EQ(tok::r_square, toks[2].getKind()); + + SourceLocation lsqrLoc = toks[0].getLocation(); + SourceLocation idLoc = toks[1].getLocation(); + SourceLocation rsqrLoc = toks[2].getLocation(); + + SourceLocation macroExpStartLoc = SourceMgr.translateLineCol(mainFileID, 2, 1); + SourceLocation macroExpEndLoc = SourceMgr.translateLineCol(mainFileID, 2, 6); + ASSERT_TRUE(macroExpStartLoc.isFileID()); + ASSERT_TRUE(macroExpEndLoc.isFileID()); + + SmallString<32> str; + ASSERT_EQ("M", PP.getSpelling(macroExpStartLoc, str)); + ASSERT_EQ(")", PP.getSpelling(macroExpEndLoc, str)); + + EXPECT_TRUE(SourceMgr.isBeforeInTranslationUnit(lsqrLoc, idLoc)); + EXPECT_TRUE(SourceMgr.isBeforeInTranslationUnit(idLoc, rsqrLoc)); + EXPECT_TRUE(SourceMgr.isBeforeInTranslationUnit(macroExpStartLoc, idLoc)); + EXPECT_TRUE(SourceMgr.isBeforeInTranslationUnit(idLoc, macroExpEndLoc)); +} + +#if defined(LLVM_ON_UNIX) + +TEST_F(SourceManagerTest, getMacroArgExpandedLocation) { + const char *header = + "#define FM(x,y) x\n"; + + const char *main = + "#include \"/test-header.h\"\n" + "#define VAL 0\n" + "FM(VAL,0)\n" + "FM(0,VAL)\n" + "FM(FM(0,VAL),0)\n" + "#define CONCAT(X, Y) X##Y\n" + "CONCAT(1,1)\n"; + + MemoryBuffer *headerBuf = MemoryBuffer::getMemBuffer(header); + MemoryBuffer *mainBuf = MemoryBuffer::getMemBuffer(main); + FileID mainFileID = SourceMgr.createMainFileIDForMemBuffer(mainBuf); + + const FileEntry *headerFile = FileMgr.getVirtualFile("/test-header.h", + headerBuf->getBufferSize(), 0); + SourceMgr.overrideFileContents(headerFile, headerBuf); + + VoidModuleLoader ModLoader; + HeaderSearch HeaderInfo(FileMgr, Diags, LangOpts, &*Target); + Preprocessor PP(Diags, LangOpts, + Target.getPtr(), + SourceMgr, HeaderInfo, ModLoader, + /*IILookup =*/ 0, + /*OwnsHeaderSearch =*/false, + /*DelayInitialization =*/ false); + PP.EnterMainSourceFile(); + + std::vector<Token> toks; + while (1) { + Token tok; + PP.Lex(tok); + if (tok.is(tok::eof)) + break; + toks.push_back(tok); + } + + // Make sure we got the tokens that we expected. + ASSERT_EQ(4U, toks.size()); + ASSERT_EQ(tok::numeric_constant, toks[0].getKind()); + ASSERT_EQ(tok::numeric_constant, toks[1].getKind()); + ASSERT_EQ(tok::numeric_constant, toks[2].getKind()); + ASSERT_EQ(tok::numeric_constant, toks[3].getKind()); + + SourceLocation defLoc = SourceMgr.translateLineCol(mainFileID, 2, 13); + SourceLocation loc1 = SourceMgr.translateLineCol(mainFileID, 3, 8); + SourceLocation loc2 = SourceMgr.translateLineCol(mainFileID, 4, 4); + SourceLocation loc3 = SourceMgr.translateLineCol(mainFileID, 5, 7); + SourceLocation defLoc2 = SourceMgr.translateLineCol(mainFileID, 6, 22); + defLoc = SourceMgr.getMacroArgExpandedLocation(defLoc); + loc1 = SourceMgr.getMacroArgExpandedLocation(loc1); + loc2 = SourceMgr.getMacroArgExpandedLocation(loc2); + loc3 = SourceMgr.getMacroArgExpandedLocation(loc3); + defLoc2 = SourceMgr.getMacroArgExpandedLocation(defLoc2); + + EXPECT_TRUE(defLoc.isFileID()); + EXPECT_TRUE(loc1.isFileID()); + EXPECT_TRUE(SourceMgr.isMacroArgExpansion(loc2)); + EXPECT_TRUE(SourceMgr.isMacroArgExpansion(loc3)); + EXPECT_EQ(loc2, toks[1].getLocation()); + EXPECT_EQ(loc3, toks[2].getLocation()); + EXPECT_TRUE(defLoc2.isFileID()); +} + +namespace { + +struct MacroAction { + SourceLocation Loc; + std::string Name; + bool isDefinition; // if false, it is expansion. + + MacroAction(SourceLocation Loc, StringRef Name, bool isDefinition) + : Loc(Loc), Name(Name), isDefinition(isDefinition) { } +}; + +class MacroTracker : public PPCallbacks { + std::vector<MacroAction> &Macros; + +public: + explicit MacroTracker(std::vector<MacroAction> &Macros) : Macros(Macros) { } + + virtual void MacroDefined(const Token &MacroNameTok, const MacroInfo *MI) { + Macros.push_back(MacroAction(MI->getDefinitionLoc(), + MacroNameTok.getIdentifierInfo()->getName(), + true)); + } + virtual void MacroExpands(const Token &MacroNameTok, const MacroInfo* MI, + SourceRange Range) { + Macros.push_back(MacroAction(MacroNameTok.getLocation(), + MacroNameTok.getIdentifierInfo()->getName(), + false)); + } +}; + +} + +TEST_F(SourceManagerTest, isBeforeInTranslationUnitWithMacroInInclude) { + const char *header = + "#define MACRO_IN_INCLUDE 0\n"; + + const char *main = + "#define M(x) x\n" + "#define INC \"/test-header.h\"\n" + "#include M(INC)\n" + "#define INC2 </test-header.h>\n" + "#include M(INC2)\n"; + + MemoryBuffer *headerBuf = MemoryBuffer::getMemBuffer(header); + MemoryBuffer *mainBuf = MemoryBuffer::getMemBuffer(main); + SourceMgr.createMainFileIDForMemBuffer(mainBuf); + + const FileEntry *headerFile = FileMgr.getVirtualFile("/test-header.h", + headerBuf->getBufferSize(), 0); + SourceMgr.overrideFileContents(headerFile, headerBuf); + + VoidModuleLoader ModLoader; + HeaderSearch HeaderInfo(FileMgr, Diags, LangOpts, &*Target); + Preprocessor PP(Diags, LangOpts, + Target.getPtr(), + SourceMgr, HeaderInfo, ModLoader, + /*IILookup =*/ 0, + /*OwnsHeaderSearch =*/false, + /*DelayInitialization =*/ false); + + std::vector<MacroAction> Macros; + PP.addPPCallbacks(new MacroTracker(Macros)); + + PP.EnterMainSourceFile(); + + std::vector<Token> toks; + while (1) { + Token tok; + PP.Lex(tok); + if (tok.is(tok::eof)) + break; + toks.push_back(tok); + } + + // Make sure we got the tokens that we expected. + ASSERT_EQ(0U, toks.size()); + + ASSERT_EQ(9U, Macros.size()); + // #define M(x) x + ASSERT_TRUE(Macros[0].isDefinition); + ASSERT_EQ("M", Macros[0].Name); + // #define INC "/test-header.h" + ASSERT_TRUE(Macros[1].isDefinition); + ASSERT_EQ("INC", Macros[1].Name); + // M expansion in #include M(INC) + ASSERT_FALSE(Macros[2].isDefinition); + ASSERT_EQ("M", Macros[2].Name); + // INC expansion in #include M(INC) + ASSERT_FALSE(Macros[3].isDefinition); + ASSERT_EQ("INC", Macros[3].Name); + // #define MACRO_IN_INCLUDE 0 + ASSERT_TRUE(Macros[4].isDefinition); + ASSERT_EQ("MACRO_IN_INCLUDE", Macros[4].Name); + // #define INC2 </test-header.h> + ASSERT_TRUE(Macros[5].isDefinition); + ASSERT_EQ("INC2", Macros[5].Name); + // M expansion in #include M(INC2) + ASSERT_FALSE(Macros[6].isDefinition); + ASSERT_EQ("M", Macros[6].Name); + // INC2 expansion in #include M(INC2) + ASSERT_FALSE(Macros[7].isDefinition); + ASSERT_EQ("INC2", Macros[7].Name); + // #define MACRO_IN_INCLUDE 0 + ASSERT_TRUE(Macros[8].isDefinition); + ASSERT_EQ("MACRO_IN_INCLUDE", Macros[8].Name); + + // The INC expansion in #include M(INC) comes before the first + // MACRO_IN_INCLUDE definition of the included file. + EXPECT_TRUE(SourceMgr.isBeforeInTranslationUnit(Macros[3].Loc, Macros[4].Loc)); + + // The INC2 expansion in #include M(INC2) comes before the second + // MACRO_IN_INCLUDE definition of the included file. + EXPECT_TRUE(SourceMgr.isBeforeInTranslationUnit(Macros[7].Loc, Macros[8].Loc)); +} + +#endif + +} // anonymous namespace |