diff options
author | Zancanaro; Carlo <czan8762@plang3.cs.usyd.edu.au> | 2012-09-24 09:58:17 +1000 |
---|---|---|
committer | Zancanaro; Carlo <czan8762@plang3.cs.usyd.edu.au> | 2012-09-24 09:58:17 +1000 |
commit | 222e2a7620e6520ffaf4fc4e69d79c18da31542e (patch) | |
tree | 7bfbc05bfa3b41c8f9d2e56d53a0bc3e310df239 /clang/unittests | |
parent | 3d206f03985b50beacae843d880bccdc91a9f424 (diff) |
Add the clang library to the repo (with some of my changes, too).
Diffstat (limited to 'clang/unittests')
-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 | ||||
-rw-r--r-- | clang/unittests/CMakeLists.txt | 73 | ||||
-rw-r--r-- | clang/unittests/Frontend/FrontendActionTest.cpp | 74 | ||||
-rw-r--r-- | clang/unittests/Frontend/Makefile | 19 | ||||
-rw-r--r-- | clang/unittests/Lex/LexerTest.cpp | 177 | ||||
-rw-r--r-- | clang/unittests/Lex/Makefile | 15 | ||||
-rw-r--r-- | clang/unittests/Lex/PreprocessingRecordTest.cpp | 139 | ||||
-rw-r--r-- | clang/unittests/Makefile | 28 | ||||
-rw-r--r-- | clang/unittests/Tooling/CompilationDatabaseTest.cpp | 292 | ||||
-rw-r--r-- | clang/unittests/Tooling/Makefile | 17 | ||||
-rw-r--r-- | clang/unittests/Tooling/ToolingTest.cpp | 113 |
13 files changed, 1481 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 diff --git a/clang/unittests/CMakeLists.txt b/clang/unittests/CMakeLists.txt new file mode 100644 index 0000000..0b3eac9 --- /dev/null +++ b/clang/unittests/CMakeLists.txt @@ -0,0 +1,73 @@ +include(LLVMParseArguments) + +# add_clang_unittest(test_dirname file1.cpp file2.cpp ... +# [USED_LIBS lib1 lib2] +# [LINK_COMPONENTS component1 component2]) +# +# Will compile the list of files together and link against the clang +# libraries in the USED_LIBS list and the llvm-config components in +# the LINK_COMPONENTS list. Produces a binary named +# 'basename(test_dirname)Tests'. +function(add_clang_unittest) + PARSE_ARGUMENTS(CLANG_UNITTEST "USED_LIBS;LINK_COMPONENTS" "" ${ARGN}) + set(LLVM_LINK_COMPONENTS ${CLANG_UNITTEST_LINK_COMPONENTS}) + set(LLVM_USED_LIBS ${CLANG_UNITTEST_USED_LIBS}) + list(GET CLANG_UNITTEST_DEFAULT_ARGS 0 test_dirname) + list(REMOVE_AT CLANG_UNITTEST_DEFAULT_ARGS 0) + + string(REGEX MATCH "([^/]+)$" test_name ${test_dirname}) + if (CMAKE_BUILD_TYPE) + set(CMAKE_RUNTIME_OUTPUT_DIRECTORY + ${CLANG_BINARY_DIR}/unittests/${test_dirname}/${CMAKE_BUILD_TYPE}) + else() + set(CMAKE_RUNTIME_OUTPUT_DIRECTORY + ${CLANG_BINARY_DIR}/unittests/${test_dirname}) + endif() + if( NOT LLVM_BUILD_TESTS ) + set(EXCLUDE_FROM_ALL ON) + endif() + add_clang_executable(${test_name}Tests ${CLANG_UNITTEST_DEFAULT_ARGS}) + add_dependencies(ClangUnitTests ${test_name}Tests) + set_target_properties(${test_name}Tests PROPERTIES FOLDER "Clang tests") +endfunction() + +add_custom_target(ClangUnitTests) +set_target_properties(ClangUnitTests PROPERTIES FOLDER "Clang tests") + +include_directories(${LLVM_MAIN_SRC_DIR}/utils/unittest/googletest/include) +add_definitions(-DGTEST_HAS_RTTI=0) +if( LLVM_COMPILER_IS_GCC_COMPATIBLE ) + llvm_replace_compiler_option(CMAKE_CXX_FLAGS "-frtti" "-fno-rtti") +elseif( MSVC ) + llvm_replace_compiler_option(CMAKE_CXX_FLAGS "/GR" "/GR-") +endif() + +if (NOT LLVM_ENABLE_THREADS) + add_definitions(-DGTEST_HAS_PTHREAD=0) +endif() + +if(SUPPORTS_NO_VARIADIC_MACROS_FLAG) + add_definitions("-Wno-variadic-macros") +endif() + +add_clang_unittest(Basic + Basic/FileManagerTest.cpp + Basic/SourceManagerTest.cpp + USED_LIBS gtest gtest_main clangLex + ) + +add_clang_unittest(Lex + Lex/LexerTest.cpp + USED_LIBS gtest gtest_main clangLex + ) + +add_clang_unittest(Frontend + Frontend/FrontendActionTest.cpp + USED_LIBS gtest gtest_main clangFrontend + ) + +add_clang_unittest(Tooling + Tooling/CompilationDatabaseTest.cpp + Tooling/ToolingTest.cpp + USED_LIBS gtest gtest_main clangTooling + ) diff --git a/clang/unittests/Frontend/FrontendActionTest.cpp b/clang/unittests/Frontend/FrontendActionTest.cpp new file mode 100644 index 0000000..2d4befc --- /dev/null +++ b/clang/unittests/Frontend/FrontendActionTest.cpp @@ -0,0 +1,74 @@ +//===- unittests/Frontend/FrontendActionTest.cpp - FrontendAction tests ---===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/AST/ASTConsumer.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/CompilerInvocation.h" +#include "clang/Frontend/FrontendAction.h" + +#include "llvm/ADT/Triple.h" +#include "llvm/Support/MemoryBuffer.h" + +#include "gtest/gtest.h" + +using namespace llvm; +using namespace clang; + +namespace { + +class TestASTFrontendAction : public ASTFrontendAction { +public: + std::vector<std::string> decl_names; + + virtual ASTConsumer *CreateASTConsumer(CompilerInstance &CI, + StringRef InFile) { + return new Visitor(decl_names); + } + +private: + class Visitor : public ASTConsumer, public RecursiveASTVisitor<Visitor> { + public: + Visitor(std::vector<std::string> &decl_names) : decl_names_(decl_names) {} + + virtual void HandleTranslationUnit(ASTContext &context) { + TraverseDecl(context.getTranslationUnitDecl()); + } + + virtual bool VisitNamedDecl(NamedDecl *Decl) { + decl_names_.push_back(Decl->getQualifiedNameAsString()); + return true; + } + + private: + std::vector<std::string> &decl_names_; + }; +}; + +TEST(ASTFrontendAction, Sanity) { + CompilerInvocation *invocation = new CompilerInvocation; + invocation->getPreprocessorOpts().addRemappedFile( + "test.cc", MemoryBuffer::getMemBuffer("int main() { float x; }")); + invocation->getFrontendOpts().Inputs.push_back(FrontendInputFile("test.cc", + IK_CXX)); + invocation->getFrontendOpts().ProgramAction = frontend::ParseSyntaxOnly; + invocation->getTargetOpts().Triple = "i386-unknown-linux-gnu"; + CompilerInstance compiler; + compiler.setInvocation(invocation); + compiler.createDiagnostics(0, NULL); + + TestASTFrontendAction test_action; + ASSERT_TRUE(compiler.ExecuteAction(test_action)); + ASSERT_EQ(3U, test_action.decl_names.size()); + EXPECT_EQ("__builtin_va_list", test_action.decl_names[0]); + EXPECT_EQ("main", test_action.decl_names[1]); + EXPECT_EQ("x", test_action.decl_names[2]); +} + +} // anonymous namespace diff --git a/clang/unittests/Frontend/Makefile b/clang/unittests/Frontend/Makefile new file mode 100644 index 0000000..f3e6396 --- /dev/null +++ b/clang/unittests/Frontend/Makefile @@ -0,0 +1,19 @@ +##===- unittests/Frontend/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 = Frontend +LINK_COMPONENTS := support mc +USEDLIBS = clangFrontendTool.a clangFrontend.a clangDriver.a \ + clangSerialization.a clangCodeGen.a clangParse.a clangSema.a \ + clangStaticAnalyzerCheckers.a clangStaticAnalyzerCore.a \ + clangARCMigrate.a clangRewrite.a clangEdit.a \ + clangAnalysis.a clangAST.a clangLex.a clangBasic.a + +include $(CLANG_LEVEL)/unittests/Makefile diff --git a/clang/unittests/Lex/LexerTest.cpp b/clang/unittests/Lex/LexerTest.cpp new file mode 100644 index 0000000..e43ad86 --- /dev/null +++ b/clang/unittests/Lex/LexerTest.cpp @@ -0,0 +1,177 @@ +//===- unittests/Basic/LexerTest.cpp ------ Lexer 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/Config/config.h" + +#include "gtest/gtest.h" + +using namespace llvm; +using namespace clang; + +namespace { + +// The test fixture. +class LexerTest : public ::testing::Test { +protected: + LexerTest() + : 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(LexerTest, LexAPI) { + const char *source = + "#define M(x) [x]\n" + "#define N(x) x\n" + "#define INN(x) x\n" + "#define NOF1 INN(val)\n" + "#define NOF2 val\n" + "M(foo) N([bar])\n" + "N(INN(val)) N(NOF1) N(NOF2) N(val)"; + + MemoryBuffer *buf = MemoryBuffer::getMemBuffer(source); + (void)SourceMgr.createMainFileIDForMemBuffer(buf); + + VoidModuleLoader ModLoader; + HeaderSearch HeaderInfo(FileMgr, Diags, LangOpts, Target.getPtr()); + 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(10U, 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()); + ASSERT_EQ(tok::l_square, toks[3].getKind()); + ASSERT_EQ(tok::identifier, toks[4].getKind()); + ASSERT_EQ(tok::r_square, toks[5].getKind()); + ASSERT_EQ(tok::identifier, toks[6].getKind()); + ASSERT_EQ(tok::identifier, toks[7].getKind()); + ASSERT_EQ(tok::identifier, toks[8].getKind()); + ASSERT_EQ(tok::identifier, toks[9].getKind()); + + SourceLocation lsqrLoc = toks[0].getLocation(); + SourceLocation idLoc = toks[1].getLocation(); + SourceLocation rsqrLoc = toks[2].getLocation(); + std::pair<SourceLocation,SourceLocation> + macroPair = SourceMgr.getExpansionRange(lsqrLoc); + SourceRange macroRange = SourceRange(macroPair.first, macroPair.second); + + SourceLocation Loc; + EXPECT_TRUE(Lexer::isAtStartOfMacroExpansion(lsqrLoc, SourceMgr, LangOpts, &Loc)); + EXPECT_EQ(Loc, macroRange.getBegin()); + EXPECT_FALSE(Lexer::isAtStartOfMacroExpansion(idLoc, SourceMgr, LangOpts)); + EXPECT_FALSE(Lexer::isAtEndOfMacroExpansion(idLoc, SourceMgr, LangOpts)); + EXPECT_TRUE(Lexer::isAtEndOfMacroExpansion(rsqrLoc, SourceMgr, LangOpts, &Loc)); + EXPECT_EQ(Loc, macroRange.getEnd()); + + CharSourceRange range = Lexer::makeFileCharRange( + CharSourceRange::getTokenRange(lsqrLoc, idLoc), SourceMgr, LangOpts); + EXPECT_TRUE(range.isInvalid()); + range = Lexer::makeFileCharRange(CharSourceRange::getTokenRange(idLoc, rsqrLoc), + SourceMgr, LangOpts); + EXPECT_TRUE(range.isInvalid()); + range = Lexer::makeFileCharRange(CharSourceRange::getTokenRange(lsqrLoc, rsqrLoc), + SourceMgr, LangOpts); + EXPECT_TRUE(!range.isTokenRange()); + EXPECT_EQ(range.getAsRange(), + SourceRange(macroRange.getBegin(), + macroRange.getEnd().getLocWithOffset(1))); + + StringRef text = Lexer::getSourceText( + CharSourceRange::getTokenRange(lsqrLoc, rsqrLoc), + SourceMgr, LangOpts); + EXPECT_EQ(text, "M(foo)"); + + SourceLocation macroLsqrLoc = toks[3].getLocation(); + SourceLocation macroIdLoc = toks[4].getLocation(); + SourceLocation macroRsqrLoc = toks[5].getLocation(); + SourceLocation fileLsqrLoc = SourceMgr.getSpellingLoc(macroLsqrLoc); + SourceLocation fileIdLoc = SourceMgr.getSpellingLoc(macroIdLoc); + SourceLocation fileRsqrLoc = SourceMgr.getSpellingLoc(macroRsqrLoc); + + range = Lexer::makeFileCharRange( + CharSourceRange::getTokenRange(macroLsqrLoc, macroIdLoc), + SourceMgr, LangOpts); + EXPECT_EQ(SourceRange(fileLsqrLoc, fileIdLoc.getLocWithOffset(3)), + range.getAsRange()); + + range = Lexer::makeFileCharRange(CharSourceRange::getTokenRange(macroIdLoc, macroRsqrLoc), + SourceMgr, LangOpts); + EXPECT_EQ(SourceRange(fileIdLoc, fileRsqrLoc.getLocWithOffset(1)), + range.getAsRange()); + + macroPair = SourceMgr.getExpansionRange(macroLsqrLoc); + range = Lexer::makeFileCharRange( + CharSourceRange::getTokenRange(macroLsqrLoc, macroRsqrLoc), + SourceMgr, LangOpts); + EXPECT_EQ(SourceRange(macroPair.first, macroPair.second.getLocWithOffset(1)), + range.getAsRange()); + + text = Lexer::getSourceText( + CharSourceRange::getTokenRange(SourceRange(macroLsqrLoc, macroIdLoc)), + SourceMgr, LangOpts); + EXPECT_EQ(text, "[bar"); + + + SourceLocation idLoc1 = toks[6].getLocation(); + SourceLocation idLoc2 = toks[7].getLocation(); + SourceLocation idLoc3 = toks[8].getLocation(); + SourceLocation idLoc4 = toks[9].getLocation(); + EXPECT_EQ("INN", Lexer::getImmediateMacroName(idLoc1, SourceMgr, LangOpts)); + EXPECT_EQ("INN", Lexer::getImmediateMacroName(idLoc2, SourceMgr, LangOpts)); + EXPECT_EQ("NOF2", Lexer::getImmediateMacroName(idLoc3, SourceMgr, LangOpts)); + EXPECT_EQ("N", Lexer::getImmediateMacroName(idLoc4, SourceMgr, LangOpts)); +} + +} // anonymous namespace diff --git a/clang/unittests/Lex/Makefile b/clang/unittests/Lex/Makefile new file mode 100644 index 0000000..bb9c6bc --- /dev/null +++ b/clang/unittests/Lex/Makefile @@ -0,0 +1,15 @@ +##===- unittests/Lex/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 = Lex +LINK_COMPONENTS := support mc +USEDLIBS = clangLex.a clangBasic.a + +include $(CLANG_LEVEL)/unittests/Makefile diff --git a/clang/unittests/Lex/PreprocessingRecordTest.cpp b/clang/unittests/Lex/PreprocessingRecordTest.cpp new file mode 100644 index 0000000..5b5d933 --- /dev/null +++ b/clang/unittests/Lex/PreprocessingRecordTest.cpp @@ -0,0 +1,139 @@ +//===- unittests/Lex/PreprocessingRecordTest.cpp - PreprocessingRecord 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 "clang/Lex/PreprocessingRecord.h" +#include "llvm/Config/config.h" + +#include "gtest/gtest.h" + +using namespace llvm; +using namespace clang; + +namespace { + +// The test fixture. +class PreprocessingRecordTest : public ::testing::Test { +protected: + PreprocessingRecordTest() + : 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(PreprocessingRecordTest, PPRecAPI) { + const char *source = + "0 1\n" + "#if 1\n" + "2\n" + "#ifndef BB\n" + "3 4\n" + "#else\n" + "#endif\n" + "5\n" + "#endif\n" + "6\n" + "#if 1\n" + "7\n" + "#if 1\n" + "#endif\n" + "8\n" + "#endif\n" + "9\n"; + + MemoryBuffer *buf = MemoryBuffer::getMemBuffer(source); + SourceMgr.createMainFileIDForMemBuffer(buf); + + VoidModuleLoader ModLoader; + HeaderSearch HeaderInfo(FileMgr, Diags, LangOpts, Target.getPtr()); + Preprocessor PP(Diags, LangOpts, + Target.getPtr(), + SourceMgr, HeaderInfo, ModLoader, + /*IILookup =*/ 0, + /*OwnsHeaderSearch =*/false, + /*DelayInitialization =*/ false); + PP.createPreprocessingRecord(true); + 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(10U, toks.size()); + + PreprocessingRecord &PPRec = *PP.getPreprocessingRecord(); + EXPECT_FALSE(PPRec.rangeIntersectsConditionalDirective( + SourceRange(toks[0].getLocation(), toks[1].getLocation()))); + EXPECT_TRUE(PPRec.rangeIntersectsConditionalDirective( + SourceRange(toks[0].getLocation(), toks[2].getLocation()))); + EXPECT_FALSE(PPRec.rangeIntersectsConditionalDirective( + SourceRange(toks[3].getLocation(), toks[4].getLocation()))); + EXPECT_TRUE(PPRec.rangeIntersectsConditionalDirective( + SourceRange(toks[1].getLocation(), toks[5].getLocation()))); + EXPECT_TRUE(PPRec.rangeIntersectsConditionalDirective( + SourceRange(toks[2].getLocation(), toks[6].getLocation()))); + EXPECT_FALSE(PPRec.rangeIntersectsConditionalDirective( + SourceRange(toks[2].getLocation(), toks[5].getLocation()))); + EXPECT_FALSE(PPRec.rangeIntersectsConditionalDirective( + SourceRange(toks[0].getLocation(), toks[6].getLocation()))); + EXPECT_TRUE(PPRec.rangeIntersectsConditionalDirective( + SourceRange(toks[2].getLocation(), toks[8].getLocation()))); + EXPECT_FALSE(PPRec.rangeIntersectsConditionalDirective( + SourceRange(toks[0].getLocation(), toks[9].getLocation()))); + + EXPECT_TRUE(PPRec.areInDifferentConditionalDirectiveRegion( + toks[0].getLocation(), toks[2].getLocation())); + EXPECT_FALSE(PPRec.areInDifferentConditionalDirectiveRegion( + toks[3].getLocation(), toks[4].getLocation())); + EXPECT_TRUE(PPRec.areInDifferentConditionalDirectiveRegion( + toks[1].getLocation(), toks[5].getLocation())); + EXPECT_TRUE(PPRec.areInDifferentConditionalDirectiveRegion( + toks[2].getLocation(), toks[0].getLocation())); + EXPECT_FALSE(PPRec.areInDifferentConditionalDirectiveRegion( + toks[4].getLocation(), toks[3].getLocation())); + EXPECT_TRUE(PPRec.areInDifferentConditionalDirectiveRegion( + toks[5].getLocation(), toks[1].getLocation())); +} + +} // anonymous namespace diff --git a/clang/unittests/Makefile b/clang/unittests/Makefile new file mode 100644 index 0000000..05449d8 --- /dev/null +++ b/clang/unittests/Makefile @@ -0,0 +1,28 @@ +##===- unittests/Makefile ----------------------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +# If CLANG_LEVEL is not set, then we are the top-level Makefile. Otherwise, we +# are being included from a subdirectory makefile. + +ifndef CLANG_LEVEL + +IS_UNITTEST_LEVEL := 1 +CLANG_LEVEL := .. +PARALLEL_DIRS = Basic Frontend Lex Tooling + +endif # CLANG_LEVEL + +include $(CLANG_LEVEL)/Makefile + +ifndef IS_UNITTEST_LEVEL + +MAKEFILE_UNITTEST_NO_INCLUDE_COMMON := 1 +include $(LLVM_SRC_ROOT)/unittests/Makefile.unittest + +endif # IS_UNITTEST_LEVEL diff --git a/clang/unittests/Tooling/CompilationDatabaseTest.cpp b/clang/unittests/Tooling/CompilationDatabaseTest.cpp new file mode 100644 index 0000000..68d2896 --- /dev/null +++ b/clang/unittests/Tooling/CompilationDatabaseTest.cpp @@ -0,0 +1,292 @@ +//===- unittest/Tooling/CompilationDatabaseTest.cpp -----------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/DeclGroup.h" +#include "clang/Frontend/FrontendAction.h" +#include "clang/Tooling/CompilationDatabase.h" +#include "clang/Tooling/Tooling.h" +#include "gtest/gtest.h" + +namespace clang { +namespace tooling { + +static CompileCommand findCompileArgsInJsonDatabase(StringRef FileName, + StringRef JSONDatabase, + std::string &ErrorMessage) { + llvm::OwningPtr<CompilationDatabase> Database( + JSONCompilationDatabase::loadFromBuffer(JSONDatabase, ErrorMessage)); + if (!Database) + return CompileCommand(); + std::vector<CompileCommand> Commands = Database->getCompileCommands(FileName); + EXPECT_LE(Commands.size(), 1u); + if (Commands.empty()) + return CompileCommand(); + return Commands[0]; +} + +TEST(findCompileArgsInJsonDatabase, FindsNothingIfEmpty) { + std::string ErrorMessage; + CompileCommand NotFound = findCompileArgsInJsonDatabase( + "a-file.cpp", "", ErrorMessage); + EXPECT_TRUE(NotFound.CommandLine.empty()) << ErrorMessage; + EXPECT_TRUE(NotFound.Directory.empty()) << ErrorMessage; +} + +TEST(findCompileArgsInJsonDatabase, ReadsSingleEntry) { + StringRef Directory("/some/directory"); + StringRef FileName("/path/to/a-file.cpp"); + StringRef Command("/path/to/compiler and some arguments"); + std::string ErrorMessage; + CompileCommand FoundCommand = findCompileArgsInJsonDatabase( + FileName, + ("[{\"directory\":\"" + Directory + "\"," + + "\"command\":\"" + Command + "\"," + "\"file\":\"" + FileName + "\"}]").str(), + ErrorMessage); + EXPECT_EQ(Directory, FoundCommand.Directory) << ErrorMessage; + ASSERT_EQ(4u, FoundCommand.CommandLine.size()) << ErrorMessage; + EXPECT_EQ("/path/to/compiler", FoundCommand.CommandLine[0]) << ErrorMessage; + EXPECT_EQ("and", FoundCommand.CommandLine[1]) << ErrorMessage; + EXPECT_EQ("some", FoundCommand.CommandLine[2]) << ErrorMessage; + EXPECT_EQ("arguments", FoundCommand.CommandLine[3]) << ErrorMessage; + + CompileCommand NotFound = findCompileArgsInJsonDatabase( + "a-file.cpp", + ("[{\"directory\":\"" + Directory + "\"," + + "\"command\":\"" + Command + "\"," + "\"file\":\"" + FileName + "\"}]").str(), + ErrorMessage); + EXPECT_TRUE(NotFound.Directory.empty()) << ErrorMessage; + EXPECT_TRUE(NotFound.CommandLine.empty()) << ErrorMessage; +} + +TEST(findCompileArgsInJsonDatabase, ReadsCompileCommandLinesWithSpaces) { + StringRef Directory("/some/directory"); + StringRef FileName("/path/to/a-file.cpp"); + StringRef Command("\\\"/path to compiler\\\" \\\"and an argument\\\""); + std::string ErrorMessage; + CompileCommand FoundCommand = findCompileArgsInJsonDatabase( + FileName, + ("[{\"directory\":\"" + Directory + "\"," + + "\"command\":\"" + Command + "\"," + "\"file\":\"" + FileName + "\"}]").str(), + ErrorMessage); + ASSERT_EQ(2u, FoundCommand.CommandLine.size()); + EXPECT_EQ("/path to compiler", FoundCommand.CommandLine[0]) << ErrorMessage; + EXPECT_EQ("and an argument", FoundCommand.CommandLine[1]) << ErrorMessage; +} + +TEST(findCompileArgsInJsonDatabase, ReadsDirectoryWithSpaces) { + StringRef Directory("/some directory / with spaces"); + StringRef FileName("/path/to/a-file.cpp"); + StringRef Command("a command"); + std::string ErrorMessage; + CompileCommand FoundCommand = findCompileArgsInJsonDatabase( + FileName, + ("[{\"directory\":\"" + Directory + "\"," + + "\"command\":\"" + Command + "\"," + "\"file\":\"" + FileName + "\"}]").str(), + ErrorMessage); + EXPECT_EQ(Directory, FoundCommand.Directory) << ErrorMessage; +} + +TEST(findCompileArgsInJsonDatabase, FindsEntry) { + StringRef Directory("directory"); + StringRef FileName("file"); + StringRef Command("command"); + std::string JsonDatabase = "["; + for (int I = 0; I < 10; ++I) { + if (I > 0) JsonDatabase += ","; + JsonDatabase += + ("{\"directory\":\"" + Directory + Twine(I) + "\"," + + "\"command\":\"" + Command + Twine(I) + "\"," + "\"file\":\"" + FileName + Twine(I) + "\"}").str(); + } + JsonDatabase += "]"; + std::string ErrorMessage; + CompileCommand FoundCommand = findCompileArgsInJsonDatabase( + "file4", JsonDatabase, ErrorMessage); + EXPECT_EQ("directory4", FoundCommand.Directory) << ErrorMessage; + ASSERT_EQ(1u, FoundCommand.CommandLine.size()) << ErrorMessage; + EXPECT_EQ("command4", FoundCommand.CommandLine[0]) << ErrorMessage; +} + +static std::vector<std::string> unescapeJsonCommandLine(StringRef Command) { + std::string JsonDatabase = + ("[{\"directory\":\"\", \"file\":\"test\", \"command\": \"" + + Command + "\"}]").str(); + std::string ErrorMessage; + CompileCommand FoundCommand = findCompileArgsInJsonDatabase( + "test", JsonDatabase, ErrorMessage); + EXPECT_TRUE(ErrorMessage.empty()) << ErrorMessage; + return FoundCommand.CommandLine; +} + +TEST(unescapeJsonCommandLine, ReturnsEmptyArrayOnEmptyString) { + std::vector<std::string> Result = unescapeJsonCommandLine(""); + EXPECT_TRUE(Result.empty()); +} + +TEST(unescapeJsonCommandLine, SplitsOnSpaces) { + std::vector<std::string> Result = unescapeJsonCommandLine("a b c"); + ASSERT_EQ(3ul, Result.size()); + EXPECT_EQ("a", Result[0]); + EXPECT_EQ("b", Result[1]); + EXPECT_EQ("c", Result[2]); +} + +TEST(unescapeJsonCommandLine, MungesMultipleSpaces) { + std::vector<std::string> Result = unescapeJsonCommandLine(" a b "); + ASSERT_EQ(2ul, Result.size()); + EXPECT_EQ("a", Result[0]); + EXPECT_EQ("b", Result[1]); +} + +TEST(unescapeJsonCommandLine, UnescapesBackslashCharacters) { + std::vector<std::string> Backslash = unescapeJsonCommandLine("a\\\\\\\\"); + ASSERT_EQ(1ul, Backslash.size()); + EXPECT_EQ("a\\", Backslash[0]); + std::vector<std::string> Quote = unescapeJsonCommandLine("a\\\\\\\""); + ASSERT_EQ(1ul, Quote.size()); + EXPECT_EQ("a\"", Quote[0]); +} + +TEST(unescapeJsonCommandLine, DoesNotMungeSpacesBetweenQuotes) { + std::vector<std::string> Result = unescapeJsonCommandLine("\\\" a b \\\""); + ASSERT_EQ(1ul, Result.size()); + EXPECT_EQ(" a b ", Result[0]); +} + +TEST(unescapeJsonCommandLine, AllowsMultipleQuotedArguments) { + std::vector<std::string> Result = unescapeJsonCommandLine( + " \\\" a \\\" \\\" b \\\" "); + ASSERT_EQ(2ul, Result.size()); + EXPECT_EQ(" a ", Result[0]); + EXPECT_EQ(" b ", Result[1]); +} + +TEST(unescapeJsonCommandLine, AllowsEmptyArgumentsInQuotes) { + std::vector<std::string> Result = unescapeJsonCommandLine( + "\\\"\\\"\\\"\\\""); + ASSERT_EQ(1ul, Result.size()); + EXPECT_TRUE(Result[0].empty()) << Result[0]; +} + +TEST(unescapeJsonCommandLine, ParsesEscapedQuotesInQuotedStrings) { + std::vector<std::string> Result = unescapeJsonCommandLine( + "\\\"\\\\\\\"\\\""); + ASSERT_EQ(1ul, Result.size()); + EXPECT_EQ("\"", Result[0]); +} + +TEST(unescapeJsonCommandLine, ParsesMultipleArgumentsWithEscapedCharacters) { + std::vector<std::string> Result = unescapeJsonCommandLine( + " \\\\\\\" \\\"a \\\\\\\" b \\\" \\\"and\\\\\\\\c\\\" \\\\\\\""); + ASSERT_EQ(4ul, Result.size()); + EXPECT_EQ("\"", Result[0]); + EXPECT_EQ("a \" b ", Result[1]); + EXPECT_EQ("and\\c", Result[2]); + EXPECT_EQ("\"", Result[3]); +} + +TEST(unescapeJsonCommandLine, ParsesStringsWithoutSpacesIntoSingleArgument) { + std::vector<std::string> QuotedNoSpaces = unescapeJsonCommandLine( + "\\\"a\\\"\\\"b\\\""); + ASSERT_EQ(1ul, QuotedNoSpaces.size()); + EXPECT_EQ("ab", QuotedNoSpaces[0]); + + std::vector<std::string> MixedNoSpaces = unescapeJsonCommandLine( + "\\\"a\\\"bcd\\\"ef\\\"\\\"\\\"\\\"g\\\""); + ASSERT_EQ(1ul, MixedNoSpaces.size()); + EXPECT_EQ("abcdefg", MixedNoSpaces[0]); +} + +TEST(unescapeJsonCommandLine, ParsesQuotedStringWithoutClosingQuote) { + std::vector<std::string> Unclosed = unescapeJsonCommandLine("\\\"abc"); + ASSERT_EQ(1ul, Unclosed.size()); + EXPECT_EQ("abc", Unclosed[0]); + + std::vector<std::string> Empty = unescapeJsonCommandLine("\\\""); + ASSERT_EQ(1ul, Empty.size()); + EXPECT_EQ("", Empty[0]); +} + +TEST(FixedCompilationDatabase, ReturnsFixedCommandLine) { + std::vector<std::string> CommandLine; + CommandLine.push_back("one"); + CommandLine.push_back("two"); + FixedCompilationDatabase Database(".", CommandLine); + std::vector<CompileCommand> Result = + Database.getCompileCommands("source"); + ASSERT_EQ(1ul, Result.size()); + std::vector<std::string> ExpectedCommandLine(1, "clang-tool"); + ExpectedCommandLine.insert(ExpectedCommandLine.end(), + CommandLine.begin(), CommandLine.end()); + ExpectedCommandLine.push_back("source"); + EXPECT_EQ(".", Result[0].Directory); + EXPECT_EQ(ExpectedCommandLine, Result[0].CommandLine); +} + +TEST(ParseFixedCompilationDatabase, ReturnsNullOnEmptyArgumentList) { + int Argc = 0; + llvm::OwningPtr<FixedCompilationDatabase> Database( + FixedCompilationDatabase::loadFromCommandLine(Argc, NULL)); + EXPECT_FALSE(Database); + EXPECT_EQ(0, Argc); +} + +TEST(ParseFixedCompilationDatabase, ReturnsNullWithoutDoubleDash) { + int Argc = 2; + const char *Argv[] = { "1", "2" }; + llvm::OwningPtr<FixedCompilationDatabase> Database( + FixedCompilationDatabase::loadFromCommandLine(Argc, Argv)); + EXPECT_FALSE(Database); + EXPECT_EQ(2, Argc); +} + +TEST(ParseFixedCompilationDatabase, ReturnsArgumentsAfterDoubleDash) { + int Argc = 5; + const char *Argv[] = { "1", "2", "--\0no-constant-folding", "3", "4" }; + llvm::OwningPtr<FixedCompilationDatabase> Database( + FixedCompilationDatabase::loadFromCommandLine(Argc, Argv)); + ASSERT_TRUE(Database); + std::vector<CompileCommand> Result = + Database->getCompileCommands("source"); + ASSERT_EQ(1ul, Result.size()); + ASSERT_EQ(".", Result[0].Directory); + std::vector<std::string> CommandLine; + CommandLine.push_back("clang-tool"); + CommandLine.push_back("3"); + CommandLine.push_back("4"); + CommandLine.push_back("source"); + ASSERT_EQ(CommandLine, Result[0].CommandLine); + EXPECT_EQ(2, Argc); +} + +TEST(ParseFixedCompilationDatabase, ReturnsEmptyCommandLine) { + int Argc = 3; + const char *Argv[] = { "1", "2", "--\0no-constant-folding" }; + llvm::OwningPtr<FixedCompilationDatabase> Database( + FixedCompilationDatabase::loadFromCommandLine(Argc, Argv)); + ASSERT_TRUE(Database); + std::vector<CompileCommand> Result = + Database->getCompileCommands("source"); + ASSERT_EQ(1ul, Result.size()); + ASSERT_EQ(".", Result[0].Directory); + std::vector<std::string> CommandLine; + CommandLine.push_back("clang-tool"); + CommandLine.push_back("source"); + ASSERT_EQ(CommandLine, Result[0].CommandLine); + EXPECT_EQ(2, Argc); +} + +} // end namespace tooling +} // end namespace clang diff --git a/clang/unittests/Tooling/Makefile b/clang/unittests/Tooling/Makefile new file mode 100644 index 0000000..0829da5 --- /dev/null +++ b/clang/unittests/Tooling/Makefile @@ -0,0 +1,17 @@ +##===- unittests/Tooling/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 = Tooling +LINK_COMPONENTS := support mc +USEDLIBS = clangTooling.a clangFrontend.a clangSerialization.a clangDriver.a \ + clangParse.a clangSema.a clangAnalysis.a clangEdit.a clangAST.a \ + clangLex.a clangBasic.a + +include $(CLANG_LEVEL)/unittests/Makefile diff --git a/clang/unittests/Tooling/ToolingTest.cpp b/clang/unittests/Tooling/ToolingTest.cpp new file mode 100644 index 0000000..c7b2210 --- /dev/null +++ b/clang/unittests/Tooling/ToolingTest.cpp @@ -0,0 +1,113 @@ +//===- unittest/Tooling/ToolingTest.cpp - Tooling unit tests --------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/DeclGroup.h" +#include "clang/Frontend/FrontendAction.h" +#include "clang/Frontend/FrontendActions.h" +#include "clang/Tooling/CompilationDatabase.h" +#include "clang/Tooling/Tooling.h" +#include "gtest/gtest.h" + +namespace clang { +namespace tooling { + +namespace { +/// Takes an ast consumer and returns it from CreateASTConsumer. This only +/// works with single translation unit compilations. +class TestAction : public clang::ASTFrontendAction { + public: + /// Takes ownership of TestConsumer. + explicit TestAction(clang::ASTConsumer *TestConsumer) + : TestConsumer(TestConsumer) {} + + protected: + virtual clang::ASTConsumer* CreateASTConsumer( + clang::CompilerInstance& compiler, StringRef dummy) { + /// TestConsumer will be deleted by the framework calling us. + return TestConsumer; + } + + private: + clang::ASTConsumer * const TestConsumer; +}; + +class FindTopLevelDeclConsumer : public clang::ASTConsumer { + public: + explicit FindTopLevelDeclConsumer(bool *FoundTopLevelDecl) + : FoundTopLevelDecl(FoundTopLevelDecl) {} + virtual bool HandleTopLevelDecl(clang::DeclGroupRef DeclGroup) { + *FoundTopLevelDecl = true; + return true; + } + private: + bool * const FoundTopLevelDecl; +}; +} // end namespace + +TEST(runToolOnCode, FindsTopLevelDeclOnEmptyCode) { + bool FoundTopLevelDecl = false; + EXPECT_TRUE(runToolOnCode( + new TestAction(new FindTopLevelDeclConsumer(&FoundTopLevelDecl)), "")); + EXPECT_TRUE(FoundTopLevelDecl); +} + +namespace { +class FindClassDeclXConsumer : public clang::ASTConsumer { + public: + FindClassDeclXConsumer(bool *FoundClassDeclX) + : FoundClassDeclX(FoundClassDeclX) {} + virtual bool HandleTopLevelDecl(clang::DeclGroupRef GroupRef) { + if (CXXRecordDecl* Record = dyn_cast<clang::CXXRecordDecl>( + *GroupRef.begin())) { + if (Record->getName() == "X") { + *FoundClassDeclX = true; + } + } + return true; + } + private: + bool *FoundClassDeclX; +}; +} // end namespace + +TEST(runToolOnCode, FindsClassDecl) { + bool FoundClassDeclX = false; + EXPECT_TRUE(runToolOnCode(new TestAction( + new FindClassDeclXConsumer(&FoundClassDeclX)), "class X;")); + EXPECT_TRUE(FoundClassDeclX); + + FoundClassDeclX = false; + EXPECT_TRUE(runToolOnCode(new TestAction( + new FindClassDeclXConsumer(&FoundClassDeclX)), "class Y;")); + EXPECT_FALSE(FoundClassDeclX); +} + +TEST(newFrontendActionFactory, CreatesFrontendActionFactoryFromType) { + llvm::OwningPtr<FrontendActionFactory> Factory( + newFrontendActionFactory<SyntaxOnlyAction>()); + llvm::OwningPtr<FrontendAction> Action(Factory->create()); + EXPECT_TRUE(Action.get() != NULL); +} + +struct IndependentFrontendActionCreator { + FrontendAction *newFrontendAction() { return new SyntaxOnlyAction; } +}; + +TEST(newFrontendActionFactory, CreatesFrontendActionFactoryFromFactoryType) { + IndependentFrontendActionCreator Creator; + llvm::OwningPtr<FrontendActionFactory> Factory( + newFrontendActionFactory(&Creator)); + llvm::OwningPtr<FrontendAction> Action(Factory->create()); + EXPECT_TRUE(Action.get() != NULL); +} + +} // end namespace tooling +} // end namespace clang |