diff options
Diffstat (limited to 'unittests/Tooling/ExecutionTest.cpp')
-rw-r--r-- | unittests/Tooling/ExecutionTest.cpp | 221 |
1 files changed, 221 insertions, 0 deletions
diff --git a/unittests/Tooling/ExecutionTest.cpp b/unittests/Tooling/ExecutionTest.cpp new file mode 100644 index 0000000000000..b0c16d6cc5d27 --- /dev/null +++ b/unittests/Tooling/ExecutionTest.cpp @@ -0,0 +1,221 @@ +//===- unittest/Tooling/ExecutionTest.cpp - Tool execution 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/RecursiveASTVisitor.h" +#include "clang/Frontend/ASTUnit.h" +#include "clang/Frontend/FrontendAction.h" +#include "clang/Frontend/FrontendActions.h" +#include "clang/Tooling/CompilationDatabase.h" +#include "clang/Tooling/Execution.h" +#include "clang/Tooling/StandaloneExecution.h" +#include "clang/Tooling/ToolExecutorPluginRegistry.h" +#include "clang/Tooling/Tooling.h" +#include "gtest/gtest.h" +#include <algorithm> +#include <string> + +namespace clang { +namespace tooling { + +namespace { + +// This traverses the AST and outputs function name as key and "1" as value for +// each function declaration. +class ASTConsumerWithResult + : public ASTConsumer, + public RecursiveASTVisitor<ASTConsumerWithResult> { +public: + using ASTVisitor = RecursiveASTVisitor<ASTConsumerWithResult>; + + explicit ASTConsumerWithResult(ExecutionContext *Context) : Context(Context) { + assert(Context != nullptr); + } + + void HandleTranslationUnit(clang::ASTContext &Context) override { + TraverseDecl(Context.getTranslationUnitDecl()); + } + + bool TraverseFunctionDecl(clang::FunctionDecl *Decl) { + Context->reportResult(Decl->getNameAsString(), "1"); + return ASTVisitor::TraverseFunctionDecl(Decl); + } + +private: + ExecutionContext *const Context; +}; + +class ReportResultAction : public ASTFrontendAction { +public: + explicit ReportResultAction(ExecutionContext *Context) : Context(Context) { + assert(Context != nullptr); + } + +protected: + std::unique_ptr<clang::ASTConsumer> + CreateASTConsumer(clang::CompilerInstance &compiler, + StringRef /* dummy */) override { + std::unique_ptr<clang::ASTConsumer> ast_consumer{ + new ASTConsumerWithResult(Context)}; + return ast_consumer; + } + +private: + ExecutionContext *const Context; +}; + +class ReportResultActionFactory : public FrontendActionFactory { +public: + ReportResultActionFactory(ExecutionContext *Context) : Context(Context) {} + FrontendAction *create() override { return new ReportResultAction(Context); } + +private: + ExecutionContext *const Context; +}; + +} // namespace + +class TestToolExecutor : public ToolExecutor { +public: + static const char *ExecutorName; + + TestToolExecutor(CommonOptionsParser Options) + : OptionsParser(std::move(Options)) {} + + StringRef getExecutorName() const override { return ExecutorName; } + + llvm::Error + execute(llvm::ArrayRef<std::pair<std::unique_ptr<FrontendActionFactory>, + ArgumentsAdjuster>>) override { + return llvm::Error::success(); + } + + ExecutionContext *getExecutionContext() override { return nullptr; }; + + ToolResults *getToolResults() override { return nullptr; } + + llvm::ArrayRef<std::string> getSourcePaths() const { + return OptionsParser.getSourcePathList(); + } + + void mapVirtualFile(StringRef FilePath, StringRef Content) override { + VFS[FilePath] = Content; + } + +private: + CommonOptionsParser OptionsParser; + std::string SourcePaths; + std::map<std::string, std::string> VFS; +}; + +const char *TestToolExecutor::ExecutorName = "test-executor"; + +class TestToolExecutorPlugin : public ToolExecutorPlugin { +public: + llvm::Expected<std::unique_ptr<ToolExecutor>> + create(CommonOptionsParser &OptionsParser) override { + return llvm::make_unique<TestToolExecutor>(std::move(OptionsParser)); + } +}; + +static ToolExecutorPluginRegistry::Add<TestToolExecutorPlugin> + X("test-executor", "Plugin for TestToolExecutor."); + +llvm::cl::OptionCategory TestCategory("execution-test options"); + +TEST(CreateToolExecutorTest, FailedCreateExecutorUndefinedFlag) { + std::vector<const char *> argv = {"prog", "--fake_flag_no_no_no", "f"}; + int argc = argv.size(); + auto Executor = internal::createExecutorFromCommandLineArgsImpl( + argc, &argv[0], TestCategory); + ASSERT_FALSE((bool)Executor); + llvm::consumeError(Executor.takeError()); +} + +TEST(CreateToolExecutorTest, RegisterFlagsBeforeReset) { + llvm::cl::opt<std::string> BeforeReset( + "before_reset", llvm::cl::desc("Defined before reset."), + llvm::cl::init("")); + + llvm::cl::ResetAllOptionOccurrences(); + + std::vector<const char *> argv = {"prog", "--before_reset=set", "f"}; + int argc = argv.size(); + auto Executor = internal::createExecutorFromCommandLineArgsImpl( + argc, &argv[0], TestCategory); + ASSERT_TRUE((bool)Executor); + EXPECT_EQ(BeforeReset, "set"); + BeforeReset.removeArgument(); +} + +TEST(CreateToolExecutorTest, CreateStandaloneToolExecutor) { + std::vector<const char *> argv = {"prog", "standalone.cpp"}; + int argc = argv.size(); + auto Executor = internal::createExecutorFromCommandLineArgsImpl( + argc, &argv[0], TestCategory); + ASSERT_TRUE((bool)Executor); + EXPECT_EQ(Executor->get()->getExecutorName(), + StandaloneToolExecutor::ExecutorName); +} + +TEST(CreateToolExecutorTest, CreateTestToolExecutor) { + std::vector<const char *> argv = {"prog", "test.cpp", + "--executor=test-executor"}; + int argc = argv.size(); + auto Executor = internal::createExecutorFromCommandLineArgsImpl( + argc, &argv[0], TestCategory); + ASSERT_TRUE((bool)Executor); + EXPECT_EQ(Executor->get()->getExecutorName(), TestToolExecutor::ExecutorName); +} + +TEST(StandaloneToolTest, SynctaxOnlyActionOnSimpleCode) { + FixedCompilationDatabase Compilations(".", std::vector<std::string>()); + StandaloneToolExecutor Executor(Compilations, + std::vector<std::string>(1, "a.cc")); + Executor.mapVirtualFile("a.cc", "int x = 0;"); + + auto Err = Executor.execute(newFrontendActionFactory<SyntaxOnlyAction>(), + getClangSyntaxOnlyAdjuster()); + ASSERT_TRUE(!Err); +} + +TEST(StandaloneToolTest, SimpleAction) { + FixedCompilationDatabase Compilations(".", std::vector<std::string>()); + StandaloneToolExecutor Executor(Compilations, + std::vector<std::string>(1, "a.cc")); + Executor.mapVirtualFile("a.cc", "int x = 0;"); + + auto Err = Executor.execute(std::unique_ptr<FrontendActionFactory>( + new ReportResultActionFactory(Executor.getExecutionContext()))); + ASSERT_TRUE(!Err); + auto KVs = Executor.getToolResults()->AllKVResults(); + ASSERT_EQ(KVs.size(), 0u); +} + +TEST(StandaloneToolTest, SimpleActionWithResult) { + FixedCompilationDatabase Compilations(".", std::vector<std::string>()); + StandaloneToolExecutor Executor(Compilations, + std::vector<std::string>(1, "a.cc")); + Executor.mapVirtualFile("a.cc", "int x = 0; void f() {}"); + + auto Err = Executor.execute(std::unique_ptr<FrontendActionFactory>( + new ReportResultActionFactory(Executor.getExecutionContext()))); + ASSERT_TRUE(!Err); + auto KVs = Executor.getToolResults()->AllKVResults(); + ASSERT_EQ(KVs.size(), 1u); + EXPECT_EQ("f", KVs[0].first); + EXPECT_EQ("1", KVs[0].second); + + Executor.getToolResults()->forEachResult( + [](StringRef, StringRef Value) { EXPECT_EQ("1", Value); }); +} + +} // end namespace tooling +} // end namespace clang |