1 //===- unittest/Tooling/ExecutionTest.cpp - Tool execution tests. --------===//
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
7 //===----------------------------------------------------------------------===//
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"
31 // This traverses the AST and outputs function name as key and "1" as value for
32 // each function declaration.
33 class ASTConsumerWithResult
35 public RecursiveASTVisitor
<ASTConsumerWithResult
> {
37 using ASTVisitor
= RecursiveASTVisitor
<ASTConsumerWithResult
>;
39 explicit ASTConsumerWithResult(ExecutionContext
*Context
) : Context(Context
) {
40 assert(Context
!= nullptr);
43 void HandleTranslationUnit(clang::ASTContext
&Context
) override
{
44 TraverseDecl(Context
.getTranslationUnitDecl());
47 bool TraverseFunctionDecl(clang::FunctionDecl
*Decl
) {
48 Context
->reportResult(Decl
->getNameAsString(),
49 Context
->getRevision() + ":" + Context
->getCorpus() +
50 ":" + Context
->getCurrentCompilationUnit() +
52 return ASTVisitor::TraverseFunctionDecl(Decl
);
56 ExecutionContext
*const Context
;
59 class ReportResultAction
: public ASTFrontendAction
{
61 explicit ReportResultAction(ExecutionContext
*Context
) : Context(Context
) {
62 assert(Context
!= nullptr);
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
)};
75 ExecutionContext
*const Context
;
78 class ReportResultActionFactory
: public FrontendActionFactory
{
80 ReportResultActionFactory(ExecutionContext
*Context
) : Context(Context
) {}
81 std::unique_ptr
<FrontendAction
> create() override
{
82 return std::make_unique
<ReportResultAction
>(Context
);
86 ExecutionContext
*const Context
;
91 class TestToolExecutor
: public ToolExecutor
{
93 static const char *ExecutorName
;
95 TestToolExecutor(CommonOptionsParser Options
)
96 : OptionsParser(std::move(Options
)) {}
98 StringRef
getExecutorName() const override
{ return ExecutorName
; }
101 execute(llvm::ArrayRef
<std::pair
<std::unique_ptr
<FrontendActionFactory
>,
102 ArgumentsAdjuster
>>) override
{
103 return llvm::Error::success();
106 ExecutionContext
*getExecutionContext() override
{ return nullptr; };
108 ToolResults
*getToolResults() override
{ return nullptr; }
110 llvm::ArrayRef
<std::string
> getSourcePaths() const {
111 return OptionsParser
.getSourcePathList();
114 void mapVirtualFile(StringRef FilePath
, StringRef Content
) override
{
115 VFS
[std::string(FilePath
)] = std::string(Content
);
119 CommonOptionsParser OptionsParser
;
120 std::string SourcePaths
;
121 std::map
<std::string
, std::string
> VFS
;
124 const char *TestToolExecutor::ExecutorName
= "test-executor";
126 class TestToolExecutorPlugin
: public ToolExecutorPlugin
{
128 llvm::Expected
<std::unique_ptr
<ToolExecutor
>>
129 create(CommonOptionsParser
&OptionsParser
) override
{
130 return std::make_unique
<TestToolExecutor
>(std::move(OptionsParser
));
134 static ToolExecutorPluginRegistry::Add
<TestToolExecutorPlugin
>
135 X("test-executor", "Plugin for TestToolExecutor.");
137 llvm::cl::OptionCategory
TestCategory("execution-test options");
139 TEST(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
[0], TestCategory
);
144 ASSERT_FALSE((bool)Executor
);
145 llvm::consumeError(Executor
.takeError());
148 TEST(CreateToolExecutorTest
, RegisterFlagsBeforeReset
) {
149 llvm::cl::opt
<std::string
> BeforeReset(
150 "before_reset", llvm::cl::desc("Defined before reset."),
153 llvm::cl::ResetAllOptionOccurrences();
155 std::vector
<const char *> argv
= {"prog", "--before_reset=set", "f"};
156 int argc
= argv
.size();
157 auto Executor
= internal::createExecutorFromCommandLineArgsImpl(
158 argc
, &argv
[0], TestCategory
);
159 ASSERT_TRUE((bool)Executor
);
160 EXPECT_EQ(BeforeReset
, "set");
161 BeforeReset
.removeArgument();
164 TEST(CreateToolExecutorTest
, CreateStandaloneToolExecutor
) {
165 std::vector
<const char *> argv
= {"prog", "standalone.cpp"};
166 int argc
= argv
.size();
167 auto Executor
= internal::createExecutorFromCommandLineArgsImpl(
168 argc
, &argv
[0], TestCategory
);
169 ASSERT_TRUE((bool)Executor
);
170 EXPECT_EQ(Executor
->get()->getExecutorName(),
171 StandaloneToolExecutor::ExecutorName
);
174 TEST(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
[0], TestCategory
);
180 ASSERT_TRUE((bool)Executor
);
181 EXPECT_EQ(Executor
->get()->getExecutorName(), TestToolExecutor::ExecutorName
);
184 TEST(StandaloneToolTest
, SynctaxOnlyActionOnSimpleCode
) {
185 FixedCompilationDatabase
Compilations(".", std::vector
<std::string
>());
186 StandaloneToolExecutor
Executor(Compilations
,
187 std::vector
<std::string
>(1, "a.cc"));
188 Executor
.mapVirtualFile("a.cc", "int x = 0;");
190 auto Err
= Executor
.execute(newFrontendActionFactory
<SyntaxOnlyAction
>(),
191 getClangSyntaxOnlyAdjuster());
195 TEST(StandaloneToolTest
, SimpleAction
) {
196 FixedCompilationDatabase
Compilations(".", std::vector
<std::string
>());
197 StandaloneToolExecutor
Executor(Compilations
,
198 std::vector
<std::string
>(1, "a.cc"));
199 Executor
.mapVirtualFile("a.cc", "int x = 0;");
201 auto Err
= Executor
.execute(std::unique_ptr
<FrontendActionFactory
>(
202 new ReportResultActionFactory(Executor
.getExecutionContext())));
204 auto KVs
= Executor
.getToolResults()->AllKVResults();
205 ASSERT_EQ(KVs
.size(), 0u);
208 TEST(StandaloneToolTest
, SimpleActionWithResult
) {
209 FixedCompilationDatabase
Compilations(".", std::vector
<std::string
>());
210 StandaloneToolExecutor
Executor(Compilations
,
211 std::vector
<std::string
>(1, "a.cc"));
212 Executor
.mapVirtualFile("a.cc", "int x = 0; void f() {}");
214 auto Err
= Executor
.execute(std::unique_ptr
<FrontendActionFactory
>(
215 new ReportResultActionFactory(Executor
.getExecutionContext())));
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
222 EXPECT_EQ("::/1", KVs
[0].second
);
224 Executor
.getToolResults()->forEachResult(
225 [](StringRef
, StringRef Value
) { EXPECT_EQ("::/1", Value
); });
228 class FixedCompilationDatabaseWithFiles
: public CompilationDatabase
{
230 FixedCompilationDatabaseWithFiles(Twine Directory
,
231 ArrayRef
<std::string
> Files
,
232 ArrayRef
<std::string
> CommandLine
)
233 : FixedCompilations(Directory
, CommandLine
), Files(Files
) {}
235 std::vector
<CompileCommand
>
236 getCompileCommands(StringRef FilePath
) const override
{
237 return FixedCompilations
.getCompileCommands(FilePath
);
240 std::vector
<std::string
> getAllFiles() const override
{ return Files
; }
243 FixedCompilationDatabase FixedCompilations
;
244 std::vector
<std::string
> Files
;
247 MATCHER_P(Named
, Name
, "") { return arg
.first
== Name
; }
249 TEST(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("[a-c].cc");
254 Executor
.mapVirtualFile("a.cc", "void x() {}");
255 Executor
.mapVirtualFile("b.cc", "void y() {}");
256 Executor
.mapVirtualFile("c.cc", "void z() {}");
257 Executor
.mapVirtualFile("ignore.cc", "void d() {}");
259 auto Err
= Executor
.execute(std::unique_ptr
<FrontendActionFactory
>(
260 new ReportResultActionFactory(Executor
.getExecutionContext())));
263 Executor
.getToolResults()->AllKVResults(),
264 ::testing::UnorderedElementsAre(Named("x"), Named("y"), Named("z")));
265 Filter
.setValue(".*"); // reset to default value.
268 TEST(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(i
) + ".cc";
275 std::string Symbol
= "looong_function_name_" + std::to_string(i
);
276 Files
.push_back(File
);
277 FileToContent
[File
] = "void " + Symbol
+ "() {}";
278 ExpectedSymbols
.push_back(Symbol
);
280 FixedCompilationDatabaseWithFiles
Compilations(".", Files
,
281 std::vector
<std::string
>());
282 AllTUsToolExecutor
Executor(Compilations
, /*ThreadCount=*/0);
283 for (const auto &FileAndContent
: FileToContent
) {
284 Executor
.mapVirtualFile(FileAndContent
.first
, FileAndContent
.second
);
287 auto Err
= Executor
.execute(std::unique_ptr
<FrontendActionFactory
>(
288 new ReportResultActionFactory(Executor
.getExecutionContext())));
290 std::vector
<std::string
> Results
;
291 Executor
.getToolResults()->forEachResult(
292 [&](StringRef Name
, StringRef
) { Results
.push_back(std::string(Name
)); });
293 EXPECT_THAT(ExpectedSymbols
, ::testing::UnorderedElementsAreArray(Results
));
296 } // end namespace tooling
297 } // end namespace clang