1 //===- unittest/Tooling/CompilationDatabaseTest.cpp -----------------------===//
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/AST/ASTConsumer.h"
11 #include "clang/AST/DeclCXX.h"
12 #include "clang/AST/DeclGroup.h"
13 #include "clang/Frontend/FrontendAction.h"
14 #include "clang/Tooling/CompilationDatabase.h"
15 #include "clang/Tooling/Tooling.h"
16 #include "gtest/gtest.h"
17 
18 namespace clang {
19 namespace tooling {
20 
21 static CompileCommand findCompileArgsInJsonDatabase(StringRef FileName,
22                                                     StringRef JSONDatabase,
23                                                     std::string &ErrorMessage) {
24   llvm::OwningPtr<CompilationDatabase> Database(
25       JSONCompilationDatabase::loadFromBuffer(JSONDatabase, ErrorMessage));
26   if (!Database)
27     return CompileCommand();
28   std::vector<CompileCommand> Commands = Database->getCompileCommands(FileName);
29   EXPECT_LE(Commands.size(), 1u);
30   if (Commands.empty())
31     return CompileCommand();
32   return Commands[0];
33 }
34 
35 TEST(findCompileArgsInJsonDatabase, FindsNothingIfEmpty) {
36   std::string ErrorMessage;
37   CompileCommand NotFound = findCompileArgsInJsonDatabase(
38     "a-file.cpp", "", ErrorMessage);
39   EXPECT_TRUE(NotFound.CommandLine.empty()) << ErrorMessage;
40   EXPECT_TRUE(NotFound.Directory.empty()) << ErrorMessage;
41 }
42 
43 TEST(findCompileArgsInJsonDatabase, ReadsSingleEntry) {
44   StringRef Directory("/some/directory");
45   StringRef FileName("/path/to/a-file.cpp");
46   StringRef Command("/path/to/compiler and some arguments");
47   std::string ErrorMessage;
48   CompileCommand FoundCommand = findCompileArgsInJsonDatabase(
49     FileName,
50     ("[{\"directory\":\"" + Directory + "\"," +
51        "\"command\":\"" + Command + "\","
52        "\"file\":\"" + FileName + "\"}]").str(),
53     ErrorMessage);
54   EXPECT_EQ(Directory, FoundCommand.Directory) << ErrorMessage;
55   ASSERT_EQ(4u, FoundCommand.CommandLine.size()) << ErrorMessage;
56   EXPECT_EQ("/path/to/compiler", FoundCommand.CommandLine[0]) << ErrorMessage;
57   EXPECT_EQ("and", FoundCommand.CommandLine[1]) << ErrorMessage;
58   EXPECT_EQ("some", FoundCommand.CommandLine[2]) << ErrorMessage;
59   EXPECT_EQ("arguments", FoundCommand.CommandLine[3]) << ErrorMessage;
60 
61   CompileCommand NotFound = findCompileArgsInJsonDatabase(
62     "a-file.cpp",
63     ("[{\"directory\":\"" + Directory + "\"," +
64        "\"command\":\"" + Command + "\","
65        "\"file\":\"" + FileName + "\"}]").str(),
66     ErrorMessage);
67   EXPECT_TRUE(NotFound.Directory.empty()) << ErrorMessage;
68   EXPECT_TRUE(NotFound.CommandLine.empty()) << ErrorMessage;
69 }
70 
71 TEST(findCompileArgsInJsonDatabase, ReadsCompileCommandLinesWithSpaces) {
72   StringRef Directory("/some/directory");
73   StringRef FileName("/path/to/a-file.cpp");
74   StringRef Command("\\\"/path to compiler\\\" \\\"and an argument\\\"");
75   std::string ErrorMessage;
76   CompileCommand FoundCommand = findCompileArgsInJsonDatabase(
77     FileName,
78     ("[{\"directory\":\"" + Directory + "\"," +
79        "\"command\":\"" + Command + "\","
80        "\"file\":\"" + FileName + "\"}]").str(),
81     ErrorMessage);
82   ASSERT_EQ(2u, FoundCommand.CommandLine.size());
83   EXPECT_EQ("/path to compiler", FoundCommand.CommandLine[0]) << ErrorMessage;
84   EXPECT_EQ("and an argument", FoundCommand.CommandLine[1]) << ErrorMessage;
85 }
86 
87 TEST(findCompileArgsInJsonDatabase, ReadsDirectoryWithSpaces) {
88   StringRef Directory("/some directory / with spaces");
89   StringRef FileName("/path/to/a-file.cpp");
90   StringRef Command("a command");
91   std::string ErrorMessage;
92   CompileCommand FoundCommand = findCompileArgsInJsonDatabase(
93     FileName,
94     ("[{\"directory\":\"" + Directory + "\"," +
95        "\"command\":\"" + Command + "\","
96        "\"file\":\"" + FileName + "\"}]").str(),
97     ErrorMessage);
98   EXPECT_EQ(Directory, FoundCommand.Directory) << ErrorMessage;
99 }
100 
101 TEST(findCompileArgsInJsonDatabase, FindsEntry) {
102   StringRef Directory("directory");
103   StringRef FileName("file");
104   StringRef Command("command");
105   std::string JsonDatabase = "[";
106   for (int I = 0; I < 10; ++I) {
107     if (I > 0) JsonDatabase += ",";
108     JsonDatabase +=
109       ("{\"directory\":\"" + Directory + Twine(I) + "\"," +
110         "\"command\":\"" + Command + Twine(I) + "\","
111         "\"file\":\"" + FileName + Twine(I) + "\"}").str();
112   }
113   JsonDatabase += "]";
114   std::string ErrorMessage;
115   CompileCommand FoundCommand = findCompileArgsInJsonDatabase(
116     "file4", JsonDatabase, ErrorMessage);
117   EXPECT_EQ("directory4", FoundCommand.Directory) << ErrorMessage;
118   ASSERT_EQ(1u, FoundCommand.CommandLine.size()) << ErrorMessage;
119   EXPECT_EQ("command4", FoundCommand.CommandLine[0]) << ErrorMessage;
120 }
121 
122 static std::vector<std::string> unescapeJsonCommandLine(StringRef Command) {
123   std::string JsonDatabase =
124     ("[{\"directory\":\"\", \"file\":\"test\", \"command\": \"" +
125      Command + "\"}]").str();
126   std::string ErrorMessage;
127   CompileCommand FoundCommand = findCompileArgsInJsonDatabase(
128     "test", JsonDatabase, ErrorMessage);
129   EXPECT_TRUE(ErrorMessage.empty()) << ErrorMessage;
130   return FoundCommand.CommandLine;
131 }
132 
133 TEST(unescapeJsonCommandLine, ReturnsEmptyArrayOnEmptyString) {
134   std::vector<std::string> Result = unescapeJsonCommandLine("");
135   EXPECT_TRUE(Result.empty());
136 }
137 
138 TEST(unescapeJsonCommandLine, SplitsOnSpaces) {
139   std::vector<std::string> Result = unescapeJsonCommandLine("a b c");
140   ASSERT_EQ(3ul, Result.size());
141   EXPECT_EQ("a", Result[0]);
142   EXPECT_EQ("b", Result[1]);
143   EXPECT_EQ("c", Result[2]);
144 }
145 
146 TEST(unescapeJsonCommandLine, MungesMultipleSpaces) {
147   std::vector<std::string> Result = unescapeJsonCommandLine("   a   b   ");
148   ASSERT_EQ(2ul, Result.size());
149   EXPECT_EQ("a", Result[0]);
150   EXPECT_EQ("b", Result[1]);
151 }
152 
153 TEST(unescapeJsonCommandLine, UnescapesBackslashCharacters) {
154   std::vector<std::string> Backslash = unescapeJsonCommandLine("a\\\\\\\\");
155   ASSERT_EQ(1ul, Backslash.size());
156   EXPECT_EQ("a\\", Backslash[0]);
157   std::vector<std::string> Quote = unescapeJsonCommandLine("a\\\\\\\"");
158   ASSERT_EQ(1ul, Quote.size());
159   EXPECT_EQ("a\"", Quote[0]);
160 }
161 
162 TEST(unescapeJsonCommandLine, DoesNotMungeSpacesBetweenQuotes) {
163   std::vector<std::string> Result = unescapeJsonCommandLine("\\\"  a  b  \\\"");
164   ASSERT_EQ(1ul, Result.size());
165   EXPECT_EQ("  a  b  ", Result[0]);
166 }
167 
168 TEST(unescapeJsonCommandLine, AllowsMultipleQuotedArguments) {
169   std::vector<std::string> Result = unescapeJsonCommandLine(
170       "  \\\" a \\\"  \\\" b \\\"  ");
171   ASSERT_EQ(2ul, Result.size());
172   EXPECT_EQ(" a ", Result[0]);
173   EXPECT_EQ(" b ", Result[1]);
174 }
175 
176 TEST(unescapeJsonCommandLine, AllowsEmptyArgumentsInQuotes) {
177   std::vector<std::string> Result = unescapeJsonCommandLine(
178       "\\\"\\\"\\\"\\\"");
179   ASSERT_EQ(1ul, Result.size());
180   EXPECT_TRUE(Result[0].empty()) << Result[0];
181 }
182 
183 TEST(unescapeJsonCommandLine, ParsesEscapedQuotesInQuotedStrings) {
184   std::vector<std::string> Result = unescapeJsonCommandLine(
185       "\\\"\\\\\\\"\\\"");
186   ASSERT_EQ(1ul, Result.size());
187   EXPECT_EQ("\"", Result[0]);
188 }
189 
190 TEST(unescapeJsonCommandLine, ParsesMultipleArgumentsWithEscapedCharacters) {
191   std::vector<std::string> Result = unescapeJsonCommandLine(
192       "  \\\\\\\"  \\\"a \\\\\\\" b \\\"     \\\"and\\\\\\\\c\\\"   \\\\\\\"");
193   ASSERT_EQ(4ul, Result.size());
194   EXPECT_EQ("\"", Result[0]);
195   EXPECT_EQ("a \" b ", Result[1]);
196   EXPECT_EQ("and\\c", Result[2]);
197   EXPECT_EQ("\"", Result[3]);
198 }
199 
200 TEST(unescapeJsonCommandLine, ParsesStringsWithoutSpacesIntoSingleArgument) {
201   std::vector<std::string> QuotedNoSpaces = unescapeJsonCommandLine(
202       "\\\"a\\\"\\\"b\\\"");
203   ASSERT_EQ(1ul, QuotedNoSpaces.size());
204   EXPECT_EQ("ab", QuotedNoSpaces[0]);
205 
206   std::vector<std::string> MixedNoSpaces = unescapeJsonCommandLine(
207       "\\\"a\\\"bcd\\\"ef\\\"\\\"\\\"\\\"g\\\"");
208   ASSERT_EQ(1ul, MixedNoSpaces.size());
209   EXPECT_EQ("abcdefg", MixedNoSpaces[0]);
210 }
211 
212 TEST(unescapeJsonCommandLine, ParsesQuotedStringWithoutClosingQuote) {
213   std::vector<std::string> Unclosed = unescapeJsonCommandLine("\\\"abc");
214   ASSERT_EQ(1ul, Unclosed.size());
215   EXPECT_EQ("abc", Unclosed[0]);
216 
217   std::vector<std::string> Empty = unescapeJsonCommandLine("\\\"");
218   ASSERT_EQ(1ul, Empty.size());
219   EXPECT_EQ("", Empty[0]);
220 }
221 
222 TEST(FixedCompilationDatabase, ReturnsFixedCommandLine) {
223   std::vector<std::string> CommandLine;
224   CommandLine.push_back("one");
225   CommandLine.push_back("two");
226   FixedCompilationDatabase Database(".", CommandLine);
227   std::vector<CompileCommand> Result =
228     Database.getCompileCommands("source");
229   ASSERT_EQ(1ul, Result.size());
230   std::vector<std::string> ExpectedCommandLine(1, "clang-tool");
231   ExpectedCommandLine.insert(ExpectedCommandLine.end(),
232                              CommandLine.begin(), CommandLine.end());
233   ExpectedCommandLine.push_back("source");
234   EXPECT_EQ(".", Result[0].Directory);
235   EXPECT_EQ(ExpectedCommandLine, Result[0].CommandLine);
236 }
237 
238 TEST(ParseFixedCompilationDatabase, ReturnsNullOnEmptyArgumentList) {
239   int Argc = 0;
240   llvm::OwningPtr<FixedCompilationDatabase> Database(
241       FixedCompilationDatabase::loadFromCommandLine(Argc, NULL));
242   EXPECT_FALSE(Database);
243   EXPECT_EQ(0, Argc);
244 }
245 
246 TEST(ParseFixedCompilationDatabase, ReturnsNullWithoutDoubleDash) {
247   int Argc = 2;
248   const char *Argv[] = { "1", "2" };
249   llvm::OwningPtr<FixedCompilationDatabase> Database(
250       FixedCompilationDatabase::loadFromCommandLine(Argc, Argv));
251   EXPECT_FALSE(Database);
252   EXPECT_EQ(2, Argc);
253 }
254 
255 TEST(ParseFixedCompilationDatabase, ReturnsArgumentsAfterDoubleDash) {
256   int Argc = 5;
257   const char *Argv[] = { "1", "2", "--\0no-constant-folding", "3", "4" };
258   llvm::OwningPtr<FixedCompilationDatabase> Database(
259       FixedCompilationDatabase::loadFromCommandLine(Argc, Argv));
260   ASSERT_TRUE(Database);
261   std::vector<CompileCommand> Result =
262     Database->getCompileCommands("source");
263   ASSERT_EQ(1ul, Result.size());
264   ASSERT_EQ(".", Result[0].Directory);
265   std::vector<std::string> CommandLine;
266   CommandLine.push_back("clang-tool");
267   CommandLine.push_back("3");
268   CommandLine.push_back("4");
269   CommandLine.push_back("source");
270   ASSERT_EQ(CommandLine, Result[0].CommandLine);
271   EXPECT_EQ(2, Argc);
272 }
273 
274 TEST(ParseFixedCompilationDatabase, ReturnsEmptyCommandLine) {
275   int Argc = 3;
276   const char *Argv[] = { "1", "2", "--\0no-constant-folding" };
277   llvm::OwningPtr<FixedCompilationDatabase> Database(
278       FixedCompilationDatabase::loadFromCommandLine(Argc, Argv));
279   ASSERT_TRUE(Database);
280   std::vector<CompileCommand> Result =
281     Database->getCompileCommands("source");
282   ASSERT_EQ(1ul, Result.size());
283   ASSERT_EQ(".", Result[0].Directory);
284   std::vector<std::string> CommandLine;
285   CommandLine.push_back("clang-tool");
286   CommandLine.push_back("source");
287   ASSERT_EQ(CommandLine, Result[0].CommandLine);
288   EXPECT_EQ(2, Argc);
289 }
290 
291 } // end namespace tooling
292 } // end namespace clang
293