//===- 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 #include 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 { public: using ASTVisitor = RecursiveASTVisitor; 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 CreateASTConsumer(clang::CompilerInstance &compiler, StringRef /* dummy */) override { std::unique_ptr 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, ArgumentsAdjuster>>) override { return llvm::Error::success(); } ExecutionContext *getExecutionContext() override { return nullptr; }; ToolResults *getToolResults() override { return nullptr; } llvm::ArrayRef getSourcePaths() const { return OptionsParser.getSourcePathList(); } void mapVirtualFile(StringRef FilePath, StringRef Content) override { VFS[FilePath] = Content; } private: CommonOptionsParser OptionsParser; std::string SourcePaths; std::map VFS; }; const char *TestToolExecutor::ExecutorName = "test-executor"; class TestToolExecutorPlugin : public ToolExecutorPlugin { public: llvm::Expected> create(CommonOptionsParser &OptionsParser) override { return llvm::make_unique(std::move(OptionsParser)); } }; static ToolExecutorPluginRegistry::Add X("test-executor", "Plugin for TestToolExecutor."); llvm::cl::OptionCategory TestCategory("execution-test options"); TEST(CreateToolExecutorTest, FailedCreateExecutorUndefinedFlag) { std::vector 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 BeforeReset( "before_reset", llvm::cl::desc("Defined before reset."), llvm::cl::init("")); llvm::cl::ResetAllOptionOccurrences(); std::vector 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 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 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()); StandaloneToolExecutor Executor(Compilations, std::vector(1, "a.cc")); Executor.mapVirtualFile("a.cc", "int x = 0;"); auto Err = Executor.execute(newFrontendActionFactory(), getClangSyntaxOnlyAdjuster()); ASSERT_TRUE(!Err); } TEST(StandaloneToolTest, SimpleAction) { FixedCompilationDatabase Compilations(".", std::vector()); StandaloneToolExecutor Executor(Compilations, std::vector(1, "a.cc")); Executor.mapVirtualFile("a.cc", "int x = 0;"); auto Err = Executor.execute(std::unique_ptr( new ReportResultActionFactory(Executor.getExecutionContext()))); ASSERT_TRUE(!Err); auto KVs = Executor.getToolResults()->AllKVResults(); ASSERT_EQ(KVs.size(), 0u); } TEST(StandaloneToolTest, SimpleActionWithResult) { FixedCompilationDatabase Compilations(".", std::vector()); StandaloneToolExecutor Executor(Compilations, std::vector(1, "a.cc")); Executor.mapVirtualFile("a.cc", "int x = 0; void f() {}"); auto Err = Executor.execute(std::unique_ptr( 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