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