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

source code of clang/unittests/Tooling/ExecutionTest.cpp