diff options
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  | 
