295 lines · cpp
1//===- unittest/Tooling/ExecutionTest.cpp - Tool execution tests. --------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "clang/Tooling/Execution.h"10#include "clang/AST/ASTConsumer.h"11#include "clang/AST/DeclCXX.h"12#include "clang/AST/DynamicRecursiveASTVisitor.h"13#include "clang/Frontend/ASTUnit.h"14#include "clang/Frontend/FrontendAction.h"15#include "clang/Frontend/FrontendActions.h"16#include "clang/Tooling/AllTUsExecution.h"17#include "clang/Tooling/CompilationDatabase.h"18#include "clang/Tooling/StandaloneExecution.h"19#include "clang/Tooling/ToolExecutorPluginRegistry.h"20#include "clang/Tooling/Tooling.h"21#include "gmock/gmock.h"22#include "gtest/gtest.h"23#include <algorithm>24#include <string>25 26namespace clang {27namespace tooling {28 29namespace {30 31// This traverses the AST and outputs function name as key and "1" as value for32// each function declaration.33class ASTConsumerWithResult : public ASTConsumer,34 public DynamicRecursiveASTVisitor {35public:36 explicit ASTConsumerWithResult(ExecutionContext *Context) : Context(Context) {37 assert(Context != nullptr);38 }39 40 void HandleTranslationUnit(clang::ASTContext &Context) override {41 TraverseDecl(Context.getTranslationUnitDecl());42 }43 44 bool TraverseFunctionDecl(clang::FunctionDecl *Decl) override {45 Context->reportResult(Decl->getNameAsString(),46 Context->getRevision() + ":" + Context->getCorpus() +47 ":" + Context->getCurrentCompilationUnit() +48 "/1");49 return DynamicRecursiveASTVisitor::TraverseFunctionDecl(Decl);50 }51 52private:53 ExecutionContext *const Context;54};55 56class ReportResultAction : public ASTFrontendAction {57public:58 explicit ReportResultAction(ExecutionContext *Context) : Context(Context) {59 assert(Context != nullptr);60 }61 62protected:63 std::unique_ptr<clang::ASTConsumer>64 CreateASTConsumer(clang::CompilerInstance &compiler,65 StringRef /* dummy */) override {66 std::unique_ptr<clang::ASTConsumer> ast_consumer{67 new ASTConsumerWithResult(Context)};68 return ast_consumer;69 }70 71private:72 ExecutionContext *const Context;73};74 75class ReportResultActionFactory : public FrontendActionFactory {76public:77 ReportResultActionFactory(ExecutionContext *Context) : Context(Context) {}78 std::unique_ptr<FrontendAction> create() override {79 return std::make_unique<ReportResultAction>(Context);80 }81 82private:83 ExecutionContext *const Context;84};85 86} // namespace87 88class TestToolExecutor : public ToolExecutor {89public:90 static const char *ExecutorName;91 92 TestToolExecutor(CommonOptionsParser Options)93 : OptionsParser(std::move(Options)) {}94 95 StringRef getExecutorName() const override { return ExecutorName; }96 97 llvm::Error98 execute(llvm::ArrayRef<std::pair<std::unique_ptr<FrontendActionFactory>,99 ArgumentsAdjuster>>) override {100 return llvm::Error::success();101 }102 103 ExecutionContext *getExecutionContext() override { return nullptr; };104 105 ToolResults *getToolResults() override { return nullptr; }106 107 llvm::ArrayRef<std::string> getSourcePaths() const {108 return OptionsParser.getSourcePathList();109 }110 111 void mapVirtualFile(StringRef FilePath, StringRef Content) override {112 VFS[std::string(FilePath)] = std::string(Content);113 }114 115private:116 CommonOptionsParser OptionsParser;117 std::string SourcePaths;118 std::map<std::string, std::string> VFS;119};120 121const char *TestToolExecutor::ExecutorName = "test-executor";122 123class TestToolExecutorPlugin : public ToolExecutorPlugin {124public:125 llvm::Expected<std::unique_ptr<ToolExecutor>>126 create(CommonOptionsParser &OptionsParser) override {127 return std::make_unique<TestToolExecutor>(std::move(OptionsParser));128 }129};130 131static ToolExecutorPluginRegistry::Add<TestToolExecutorPlugin>132 X("test-executor", "Plugin for TestToolExecutor.");133 134llvm::cl::OptionCategory TestCategory("execution-test options");135 136TEST(CreateToolExecutorTest, FailedCreateExecutorUndefinedFlag) {137 std::vector<const char *> argv = {"prog", "--fake_flag_no_no_no", "f"};138 int argc = argv.size();139 auto Executor = internal::createExecutorFromCommandLineArgsImpl(140 argc, &argv[0], TestCategory);141 ASSERT_FALSE((bool)Executor);142 llvm::consumeError(Executor.takeError());143}144 145TEST(CreateToolExecutorTest, RegisterFlagsBeforeReset) {146 llvm::cl::opt<std::string> BeforeReset(147 "before_reset", llvm::cl::desc("Defined before reset."),148 llvm::cl::init(""));149 150 llvm::cl::ResetAllOptionOccurrences();151 152 std::vector<const char *> argv = {"prog", "--before_reset=set", "f"};153 int argc = argv.size();154 auto Executor = internal::createExecutorFromCommandLineArgsImpl(155 argc, &argv[0], TestCategory);156 ASSERT_TRUE((bool)Executor);157 EXPECT_EQ(BeforeReset, "set");158 BeforeReset.removeArgument();159}160 161TEST(CreateToolExecutorTest, CreateStandaloneToolExecutor) {162 std::vector<const char *> argv = {"prog", "standalone.cpp"};163 int argc = argv.size();164 auto Executor = internal::createExecutorFromCommandLineArgsImpl(165 argc, &argv[0], TestCategory);166 ASSERT_TRUE((bool)Executor);167 EXPECT_EQ(Executor->get()->getExecutorName(),168 StandaloneToolExecutor::ExecutorName);169}170 171TEST(CreateToolExecutorTest, CreateTestToolExecutor) {172 std::vector<const char *> argv = {"prog", "test.cpp",173 "--executor=test-executor"};174 int argc = argv.size();175 auto Executor = internal::createExecutorFromCommandLineArgsImpl(176 argc, &argv[0], TestCategory);177 ASSERT_TRUE((bool)Executor);178 EXPECT_EQ(Executor->get()->getExecutorName(), TestToolExecutor::ExecutorName);179}180 181TEST(StandaloneToolTest, SynctaxOnlyActionOnSimpleCode) {182 FixedCompilationDatabase Compilations(".", std::vector<std::string>());183 StandaloneToolExecutor Executor(Compilations,184 std::vector<std::string>(1, "a.cc"));185 Executor.mapVirtualFile("a.cc", "int x = 0;");186 187 auto Err = Executor.execute(newFrontendActionFactory<SyntaxOnlyAction>(),188 getClangSyntaxOnlyAdjuster());189 ASSERT_TRUE(!Err);190}191 192TEST(StandaloneToolTest, SimpleAction) {193 FixedCompilationDatabase Compilations(".", std::vector<std::string>());194 StandaloneToolExecutor Executor(Compilations,195 std::vector<std::string>(1, "a.cc"));196 Executor.mapVirtualFile("a.cc", "int x = 0;");197 198 auto Err = Executor.execute(std::unique_ptr<FrontendActionFactory>(199 new ReportResultActionFactory(Executor.getExecutionContext())));200 ASSERT_TRUE(!Err);201 auto KVs = Executor.getToolResults()->AllKVResults();202 ASSERT_EQ(KVs.size(), 0u);203}204 205TEST(StandaloneToolTest, SimpleActionWithResult) {206 FixedCompilationDatabase Compilations(".", std::vector<std::string>());207 StandaloneToolExecutor Executor(Compilations,208 std::vector<std::string>(1, "a.cc"));209 Executor.mapVirtualFile("a.cc", "int x = 0; void f() {}");210 211 auto Err = Executor.execute(std::unique_ptr<FrontendActionFactory>(212 new ReportResultActionFactory(Executor.getExecutionContext())));213 ASSERT_TRUE(!Err);214 auto KVs = Executor.getToolResults()->AllKVResults();215 ASSERT_EQ(KVs.size(), 1u);216 EXPECT_EQ("f", KVs[0].first);217 // Currently the standalone executor returns empty corpus, revision, and218 // compilation unit.219 EXPECT_EQ("::/1", KVs[0].second);220 221 Executor.getToolResults()->forEachResult(222 [](StringRef, StringRef Value) { EXPECT_EQ("::/1", Value); });223}224 225class FixedCompilationDatabaseWithFiles : public CompilationDatabase {226public:227 FixedCompilationDatabaseWithFiles(Twine Directory,228 ArrayRef<std::string> Files,229 ArrayRef<std::string> CommandLine)230 : FixedCompilations(Directory, CommandLine), Files(Files) {}231 232 std::vector<CompileCommand>233 getCompileCommands(StringRef FilePath) const override {234 return FixedCompilations.getCompileCommands(FilePath);235 }236 237 std::vector<std::string> getAllFiles() const override { return Files; }238 239private:240 FixedCompilationDatabase FixedCompilations;241 std::vector<std::string> Files;242};243 244MATCHER_P(Named, Name, "") { return arg.first == Name; }245 246TEST(AllTUsToolTest, AFewFiles) {247 FixedCompilationDatabaseWithFiles Compilations(248 ".", {"a.cc", "b.cc", "c.cc", "ignore.cc"}, std::vector<std::string>());249 AllTUsToolExecutor Executor(Compilations, /*ThreadCount=*/0);250 Filter.setValue("[a-c].cc");251 Executor.mapVirtualFile("a.cc", "void x() {}");252 Executor.mapVirtualFile("b.cc", "void y() {}");253 Executor.mapVirtualFile("c.cc", "void z() {}");254 Executor.mapVirtualFile("ignore.cc", "void d() {}");255 256 auto Err = Executor.execute(std::unique_ptr<FrontendActionFactory>(257 new ReportResultActionFactory(Executor.getExecutionContext())));258 ASSERT_TRUE(!Err);259 EXPECT_THAT(260 Executor.getToolResults()->AllKVResults(),261 ::testing::UnorderedElementsAre(Named("x"), Named("y"), Named("z")));262 Filter.setValue(".*"); // reset to default value.263}264 265TEST(AllTUsToolTest, ManyFiles) {266 unsigned NumFiles = 100;267 std::vector<std::string> Files;268 std::map<std::string, std::string> FileToContent;269 std::vector<std::string> ExpectedSymbols;270 for (unsigned i = 1; i <= NumFiles; ++i) {271 std::string File = "f" + std::to_string(i) + ".cc";272 std::string Symbol = "looong_function_name_" + std::to_string(i);273 Files.push_back(File);274 FileToContent[File] = "void " + Symbol + "() {}";275 ExpectedSymbols.push_back(Symbol);276 }277 FixedCompilationDatabaseWithFiles Compilations(".", Files,278 std::vector<std::string>());279 AllTUsToolExecutor Executor(Compilations, /*ThreadCount=*/0);280 for (const auto &FileAndContent : FileToContent) {281 Executor.mapVirtualFile(FileAndContent.first, FileAndContent.second);282 }283 284 auto Err = Executor.execute(std::unique_ptr<FrontendActionFactory>(285 new ReportResultActionFactory(Executor.getExecutionContext())));286 ASSERT_TRUE(!Err);287 std::vector<std::string> Results;288 Executor.getToolResults()->forEachResult(289 [&](StringRef Name, StringRef) { Results.push_back(std::string(Name)); });290 EXPECT_THAT(ExpectedSymbols, ::testing::UnorderedElementsAreArray(Results));291}292 293} // end namespace tooling294} // end namespace clang295