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/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 for
32// 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(Key: Decl->getNameAsString(),
46 Value: 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>(args: Context);
80 }
81
82private:
83 ExecutionContext *const Context;
84};
85
86} // namespace
87
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::Error
98 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>(args: 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: &argv[0], Category&: TestCategory);
141 ASSERT_FALSE((bool)Executor);
142 llvm::consumeError(Err: 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(Val: ""));
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: &argv[0], Category&: 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: &argv[0], Category&: 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: &argv[0], Category&: 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(FilePath: "a.cc", Content: "int x = 0;");
186
187 auto Err = Executor.execute(Action: newFrontendActionFactory<SyntaxOnlyAction>(),
188 Adjuster: 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(FilePath: "a.cc", Content: "int x = 0;");
197
198 auto Err = Executor.execute(Action: 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(FilePath: "a.cc", Content: "int x = 0; void f() {}");
210
211 auto Err = Executor.execute(Action: 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, and
218 // compilation unit.
219 EXPECT_EQ("::/1", KVs[0].second);
220
221 Executor.getToolResults()->forEachResult(
222 Callback: [](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(V: "[a-c].cc");
251 Executor.mapVirtualFile(FilePath: "a.cc", Content: "void x() {}");
252 Executor.mapVirtualFile(FilePath: "b.cc", Content: "void y() {}");
253 Executor.mapVirtualFile(FilePath: "c.cc", Content: "void z() {}");
254 Executor.mapVirtualFile(FilePath: "ignore.cc", Content: "void d() {}");
255
256 auto Err = Executor.execute(Action: 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(V: ".*"); // 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(val: i) + ".cc";
272 std::string Symbol = "looong_function_name_" + std::to_string(val: i);
273 Files.push_back(x: File);
274 FileToContent[File] = "void " + Symbol + "() {}";
275 ExpectedSymbols.push_back(x: 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(FilePath: FileAndContent.first, Content: FileAndContent.second);
282 }
283
284 auto Err = Executor.execute(Action: std::unique_ptr<FrontendActionFactory>(
285 new ReportResultActionFactory(Executor.getExecutionContext())));
286 ASSERT_TRUE(!Err);
287 std::vector<std::string> Results;
288 Executor.getToolResults()->forEachResult(
289 Callback: [&](StringRef Name, StringRef) { Results.push_back(x: std::string(Name)); });
290 EXPECT_THAT(ExpectedSymbols, ::testing::UnorderedElementsAreArray(Results));
291}
292
293} // end namespace tooling
294} // end namespace clang
295

Provided by KDAB

Privacy Policy
Improve your Profiling and Debugging skills
Find out more

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