From 222e2a7620e6520ffaf4fc4e69d79c18da31542e Mon Sep 17 00:00:00 2001 From: "Zancanaro; Carlo" Date: Mon, 24 Sep 2012 09:58:17 +1000 Subject: Add the clang library to the repo (with some of my changes, too). --- .../unittests/Tooling/CompilationDatabaseTest.cpp | 292 +++++++++++++++++++++ clang/unittests/Tooling/Makefile | 17 ++ clang/unittests/Tooling/ToolingTest.cpp | 113 ++++++++ 3 files changed, 422 insertions(+) create mode 100644 clang/unittests/Tooling/CompilationDatabaseTest.cpp create mode 100644 clang/unittests/Tooling/Makefile create mode 100644 clang/unittests/Tooling/ToolingTest.cpp (limited to 'clang/unittests/Tooling') 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 Database( + JSONCompilationDatabase::loadFromBuffer(JSONDatabase, ErrorMessage)); + if (!Database) + return CompileCommand(); + std::vector 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 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 Result = unescapeJsonCommandLine(""); + EXPECT_TRUE(Result.empty()); +} + +TEST(unescapeJsonCommandLine, SplitsOnSpaces) { + std::vector 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 Result = unescapeJsonCommandLine(" a b "); + ASSERT_EQ(2ul, Result.size()); + EXPECT_EQ("a", Result[0]); + EXPECT_EQ("b", Result[1]); +} + +TEST(unescapeJsonCommandLine, UnescapesBackslashCharacters) { + std::vector Backslash = unescapeJsonCommandLine("a\\\\\\\\"); + ASSERT_EQ(1ul, Backslash.size()); + EXPECT_EQ("a\\", Backslash[0]); + std::vector Quote = unescapeJsonCommandLine("a\\\\\\\""); + ASSERT_EQ(1ul, Quote.size()); + EXPECT_EQ("a\"", Quote[0]); +} + +TEST(unescapeJsonCommandLine, DoesNotMungeSpacesBetweenQuotes) { + std::vector Result = unescapeJsonCommandLine("\\\" a b \\\""); + ASSERT_EQ(1ul, Result.size()); + EXPECT_EQ(" a b ", Result[0]); +} + +TEST(unescapeJsonCommandLine, AllowsMultipleQuotedArguments) { + std::vector Result = unescapeJsonCommandLine( + " \\\" a \\\" \\\" b \\\" "); + ASSERT_EQ(2ul, Result.size()); + EXPECT_EQ(" a ", Result[0]); + EXPECT_EQ(" b ", Result[1]); +} + +TEST(unescapeJsonCommandLine, AllowsEmptyArgumentsInQuotes) { + std::vector Result = unescapeJsonCommandLine( + "\\\"\\\"\\\"\\\""); + ASSERT_EQ(1ul, Result.size()); + EXPECT_TRUE(Result[0].empty()) << Result[0]; +} + +TEST(unescapeJsonCommandLine, ParsesEscapedQuotesInQuotedStrings) { + std::vector Result = unescapeJsonCommandLine( + "\\\"\\\\\\\"\\\""); + ASSERT_EQ(1ul, Result.size()); + EXPECT_EQ("\"", Result[0]); +} + +TEST(unescapeJsonCommandLine, ParsesMultipleArgumentsWithEscapedCharacters) { + std::vector 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 QuotedNoSpaces = unescapeJsonCommandLine( + "\\\"a\\\"\\\"b\\\""); + ASSERT_EQ(1ul, QuotedNoSpaces.size()); + EXPECT_EQ("ab", QuotedNoSpaces[0]); + + std::vector MixedNoSpaces = unescapeJsonCommandLine( + "\\\"a\\\"bcd\\\"ef\\\"\\\"\\\"\\\"g\\\""); + ASSERT_EQ(1ul, MixedNoSpaces.size()); + EXPECT_EQ("abcdefg", MixedNoSpaces[0]); +} + +TEST(unescapeJsonCommandLine, ParsesQuotedStringWithoutClosingQuote) { + std::vector Unclosed = unescapeJsonCommandLine("\\\"abc"); + ASSERT_EQ(1ul, Unclosed.size()); + EXPECT_EQ("abc", Unclosed[0]); + + std::vector Empty = unescapeJsonCommandLine("\\\""); + ASSERT_EQ(1ul, Empty.size()); + EXPECT_EQ("", Empty[0]); +} + +TEST(FixedCompilationDatabase, ReturnsFixedCommandLine) { + std::vector CommandLine; + CommandLine.push_back("one"); + CommandLine.push_back("two"); + FixedCompilationDatabase Database(".", CommandLine); + std::vector Result = + Database.getCompileCommands("source"); + ASSERT_EQ(1ul, Result.size()); + std::vector 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 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 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 Database( + FixedCompilationDatabase::loadFromCommandLine(Argc, Argv)); + ASSERT_TRUE(Database); + std::vector Result = + Database->getCompileCommands("source"); + ASSERT_EQ(1ul, Result.size()); + ASSERT_EQ(".", Result[0].Directory); + std::vector 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 Database( + FixedCompilationDatabase::loadFromCommandLine(Argc, Argv)); + ASSERT_TRUE(Database); + std::vector Result = + Database->getCompileCommands("source"); + ASSERT_EQ(1ul, Result.size()); + ASSERT_EQ(".", Result[0].Directory); + std::vector 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( + *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 Factory( + newFrontendActionFactory()); + llvm::OwningPtr Action(Factory->create()); + EXPECT_TRUE(Action.get() != NULL); +} + +struct IndependentFrontendActionCreator { + FrontendAction *newFrontendAction() { return new SyntaxOnlyAction; } +}; + +TEST(newFrontendActionFactory, CreatesFrontendActionFactoryFromFactoryType) { + IndependentFrontendActionCreator Creator; + llvm::OwningPtr Factory( + newFrontendActionFactory(&Creator)); + llvm::OwningPtr Action(Factory->create()); + EXPECT_TRUE(Action.get() != NULL); +} + +} // end namespace tooling +} // end namespace clang -- cgit v1.2.3