blob: 6b58abbd3259c5a39d55863bc919bc45f731ca95 [file] [log] [blame]
Manuel Klimekcb971c62012-04-04 12:07:46 +00001//===- 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"
Daniel Jasperd3420c92012-10-08 16:08:15 +000014#include "clang/Tooling/FileMatchTrie.h"
Daniel Jasper7fd90b02012-08-24 05:50:27 +000015#include "clang/Tooling/JSONCompilationDatabase.h"
Manuel Klimekcb971c62012-04-04 12:07:46 +000016#include "clang/Tooling/Tooling.h"
17#include "gtest/gtest.h"
18
19namespace clang {
20namespace tooling {
21
Manuel Klimek1a8d6862012-05-15 11:46:07 +000022static void expectFailure(StringRef JSONDatabase, StringRef Explanation) {
23 std::string ErrorMessage;
24 EXPECT_EQ(NULL, JSONCompilationDatabase::loadFromBuffer(JSONDatabase,
25 ErrorMessage))
26 << "Expected an error because of: " << Explanation;
27}
28
29TEST(JSONCompilationDatabase, ErrsOnInvalidFormat) {
30 expectFailure("", "Empty database");
31 expectFailure("{", "Invalid JSON");
32 expectFailure("[[]]", "Array instead of object");
33 expectFailure("[{\"a\":[]}]", "Array instead of value");
34 expectFailure("[{\"a\":\"b\"}]", "Unknown key");
35 expectFailure("[{[]:\"\"}]", "Incorrectly typed entry");
36 expectFailure("[{}]", "Empty entry");
37 expectFailure("[{\"directory\":\"\",\"command\":\"\"}]", "Missing file");
38 expectFailure("[{\"directory\":\"\",\"file\":\"\"}]", "Missing command");
39 expectFailure("[{\"command\":\"\",\"file\":\"\"}]", "Missing directory");
40}
41
Manuel Klimeka3c70962012-07-13 12:31:45 +000042static std::vector<std::string> getAllFiles(StringRef JSONDatabase,
43 std::string &ErrorMessage) {
44 llvm::OwningPtr<CompilationDatabase> Database(
45 JSONCompilationDatabase::loadFromBuffer(JSONDatabase, ErrorMessage));
46 if (!Database) {
47 ADD_FAILURE() << ErrorMessage;
48 return std::vector<std::string>();
49 }
50 return Database->getAllFiles();
51}
52
53TEST(JSONCompilationDatabase, GetAllFiles) {
54 std::string ErrorMessage;
55 EXPECT_EQ(std::vector<std::string>(),
56 getAllFiles("[]", ErrorMessage)) << ErrorMessage;
57
58 std::vector<std::string> expected_files;
Daniel Jasperd3420c92012-10-08 16:08:15 +000059 expected_files.push_back("/dir/file1");
60 expected_files.push_back("/dir/file2");
Manuel Klimeka3c70962012-07-13 12:31:45 +000061 EXPECT_EQ(expected_files, getAllFiles(
Daniel Jasperd3420c92012-10-08 16:08:15 +000062 "[{\"directory\":\"/dir\","
Manuel Klimeka3c70962012-07-13 12:31:45 +000063 "\"command\":\"command\","
64 "\"file\":\"file1\"},"
Daniel Jasperd3420c92012-10-08 16:08:15 +000065 " {\"directory\":\"/dir\","
Manuel Klimeka3c70962012-07-13 12:31:45 +000066 "\"command\":\"command\","
67 "\"file\":\"file2\"}]",
68 ErrorMessage)) << ErrorMessage;
69}
70
Manuel Klimekcb971c62012-04-04 12:07:46 +000071static CompileCommand findCompileArgsInJsonDatabase(StringRef FileName,
72 StringRef JSONDatabase,
73 std::string &ErrorMessage) {
74 llvm::OwningPtr<CompilationDatabase> Database(
75 JSONCompilationDatabase::loadFromBuffer(JSONDatabase, ErrorMessage));
76 if (!Database)
77 return CompileCommand();
78 std::vector<CompileCommand> Commands = Database->getCompileCommands(FileName);
79 EXPECT_LE(Commands.size(), 1u);
80 if (Commands.empty())
81 return CompileCommand();
82 return Commands[0];
83}
84
Daniel Jasperd3420c92012-10-08 16:08:15 +000085struct FakeComparator : public PathComparator {
86 virtual ~FakeComparator() {}
Daniel Jasper2dbe2fa2012-10-08 18:31:54 +000087 virtual bool equivalent(StringRef FileA, StringRef FileB) const {
88 return FileA.equals_lower(FileB);
Daniel Jasperd3420c92012-10-08 16:08:15 +000089 }
90};
91
92class FileMatchTrieTest : public ::testing::Test {
93protected:
94 FileMatchTrieTest() : Trie(new FakeComparator()) {}
95
96 StringRef find(StringRef Path) {
97 llvm::raw_string_ostream ES(Error);
98 return Trie.findEquivalent(Path, ES);
99 }
100
101 FileMatchTrie Trie;
102 std::string Error;
103};
104
105TEST_F(FileMatchTrieTest, InsertingRelativePath) {
106 Trie.insert("/path/file.cc");
107 Trie.insert("file.cc");
108 EXPECT_EQ("/path/file.cc", find("/path/file.cc"));
109}
110
111TEST_F(FileMatchTrieTest, MatchingRelativePath) {
112 EXPECT_EQ("", find("file.cc"));
113}
114
115TEST_F(FileMatchTrieTest, ReturnsBestResults) {
116 Trie.insert("/d/c/b.cc");
117 Trie.insert("/d/b/b.cc");
118 EXPECT_EQ("/d/b/b.cc", find("/d/b/b.cc"));
119}
120
121TEST_F(FileMatchTrieTest, HandlesSymlinks) {
122 Trie.insert("/AA/file.cc");
123 EXPECT_EQ("/AA/file.cc", find("/aa/file.cc"));
124}
125
126TEST_F(FileMatchTrieTest, ReportsSymlinkAmbiguity) {
127 Trie.insert("/Aa/file.cc");
128 Trie.insert("/aA/file.cc");
129 EXPECT_TRUE(find("/aa/file.cc").empty());
130 EXPECT_EQ("Path is ambiguous", Error);
131}
132
133TEST_F(FileMatchTrieTest, LongerMatchingSuffixPreferred) {
134 Trie.insert("/src/Aa/file.cc");
135 Trie.insert("/src/aA/file.cc");
136 Trie.insert("/SRC/aa/file.cc");
137 EXPECT_EQ("/SRC/aa/file.cc", find("/src/aa/file.cc"));
138}
139
140TEST_F(FileMatchTrieTest, EmptyTrie) {
141 EXPECT_TRUE(find("/some/path").empty());
142}
143
144TEST_F(FileMatchTrieTest, NoResult) {
145 Trie.insert("/somepath/otherfile.cc");
146 Trie.insert("/otherpath/somefile.cc");
147 EXPECT_EQ("", find("/somepath/somefile.cc"));
148}
149
150TEST_F(FileMatchTrieTest, RootElementDifferent) {
151 Trie.insert("/path/file.cc");
152 Trie.insert("/otherpath/file.cc");
153 EXPECT_EQ("/path/file.cc", find("/path/file.cc"));
154}
155
156TEST_F(FileMatchTrieTest, CannotResolveRelativePath) {
157 EXPECT_EQ("", find("relative-path.cc"));
158 EXPECT_EQ("Cannot resolve relative paths", Error);
159}
160
Manuel Klimekcb971c62012-04-04 12:07:46 +0000161TEST(findCompileArgsInJsonDatabase, FindsNothingIfEmpty) {
162 std::string ErrorMessage;
163 CompileCommand NotFound = findCompileArgsInJsonDatabase(
164 "a-file.cpp", "", ErrorMessage);
165 EXPECT_TRUE(NotFound.CommandLine.empty()) << ErrorMessage;
166 EXPECT_TRUE(NotFound.Directory.empty()) << ErrorMessage;
167}
168
169TEST(findCompileArgsInJsonDatabase, ReadsSingleEntry) {
170 StringRef Directory("/some/directory");
171 StringRef FileName("/path/to/a-file.cpp");
172 StringRef Command("/path/to/compiler and some arguments");
173 std::string ErrorMessage;
174 CompileCommand FoundCommand = findCompileArgsInJsonDatabase(
175 FileName,
176 ("[{\"directory\":\"" + Directory + "\"," +
177 "\"command\":\"" + Command + "\","
178 "\"file\":\"" + FileName + "\"}]").str(),
179 ErrorMessage);
180 EXPECT_EQ(Directory, FoundCommand.Directory) << ErrorMessage;
181 ASSERT_EQ(4u, FoundCommand.CommandLine.size()) << ErrorMessage;
182 EXPECT_EQ("/path/to/compiler", FoundCommand.CommandLine[0]) << ErrorMessage;
183 EXPECT_EQ("and", FoundCommand.CommandLine[1]) << ErrorMessage;
184 EXPECT_EQ("some", FoundCommand.CommandLine[2]) << ErrorMessage;
185 EXPECT_EQ("arguments", FoundCommand.CommandLine[3]) << ErrorMessage;
186
187 CompileCommand NotFound = findCompileArgsInJsonDatabase(
188 "a-file.cpp",
189 ("[{\"directory\":\"" + Directory + "\"," +
190 "\"command\":\"" + Command + "\","
191 "\"file\":\"" + FileName + "\"}]").str(),
192 ErrorMessage);
193 EXPECT_TRUE(NotFound.Directory.empty()) << ErrorMessage;
194 EXPECT_TRUE(NotFound.CommandLine.empty()) << ErrorMessage;
195}
196
197TEST(findCompileArgsInJsonDatabase, ReadsCompileCommandLinesWithSpaces) {
198 StringRef Directory("/some/directory");
199 StringRef FileName("/path/to/a-file.cpp");
200 StringRef Command("\\\"/path to compiler\\\" \\\"and an argument\\\"");
201 std::string ErrorMessage;
202 CompileCommand FoundCommand = findCompileArgsInJsonDatabase(
203 FileName,
204 ("[{\"directory\":\"" + Directory + "\"," +
205 "\"command\":\"" + Command + "\","
206 "\"file\":\"" + FileName + "\"}]").str(),
207 ErrorMessage);
208 ASSERT_EQ(2u, FoundCommand.CommandLine.size());
209 EXPECT_EQ("/path to compiler", FoundCommand.CommandLine[0]) << ErrorMessage;
210 EXPECT_EQ("and an argument", FoundCommand.CommandLine[1]) << ErrorMessage;
211}
212
213TEST(findCompileArgsInJsonDatabase, ReadsDirectoryWithSpaces) {
214 StringRef Directory("/some directory / with spaces");
215 StringRef FileName("/path/to/a-file.cpp");
216 StringRef Command("a command");
217 std::string ErrorMessage;
218 CompileCommand FoundCommand = findCompileArgsInJsonDatabase(
219 FileName,
220 ("[{\"directory\":\"" + Directory + "\"," +
221 "\"command\":\"" + Command + "\","
222 "\"file\":\"" + FileName + "\"}]").str(),
223 ErrorMessage);
224 EXPECT_EQ(Directory, FoundCommand.Directory) << ErrorMessage;
225}
226
227TEST(findCompileArgsInJsonDatabase, FindsEntry) {
Daniel Jasperd3420c92012-10-08 16:08:15 +0000228 StringRef Directory("/directory");
Manuel Klimekcb971c62012-04-04 12:07:46 +0000229 StringRef FileName("file");
230 StringRef Command("command");
231 std::string JsonDatabase = "[";
232 for (int I = 0; I < 10; ++I) {
233 if (I > 0) JsonDatabase += ",";
234 JsonDatabase +=
235 ("{\"directory\":\"" + Directory + Twine(I) + "\"," +
236 "\"command\":\"" + Command + Twine(I) + "\","
237 "\"file\":\"" + FileName + Twine(I) + "\"}").str();
238 }
239 JsonDatabase += "]";
240 std::string ErrorMessage;
241 CompileCommand FoundCommand = findCompileArgsInJsonDatabase(
Daniel Jasperd3420c92012-10-08 16:08:15 +0000242 "/directory4/file4", JsonDatabase, ErrorMessage);
243 EXPECT_EQ("/directory4", FoundCommand.Directory) << ErrorMessage;
Manuel Klimekcb971c62012-04-04 12:07:46 +0000244 ASSERT_EQ(1u, FoundCommand.CommandLine.size()) << ErrorMessage;
245 EXPECT_EQ("command4", FoundCommand.CommandLine[0]) << ErrorMessage;
246}
247
248static std::vector<std::string> unescapeJsonCommandLine(StringRef Command) {
249 std::string JsonDatabase =
Daniel Jasperd3420c92012-10-08 16:08:15 +0000250 ("[{\"directory\":\"/root\", \"file\":\"test\", \"command\": \"" +
Manuel Klimekcb971c62012-04-04 12:07:46 +0000251 Command + "\"}]").str();
252 std::string ErrorMessage;
253 CompileCommand FoundCommand = findCompileArgsInJsonDatabase(
Daniel Jasperd3420c92012-10-08 16:08:15 +0000254 "/root/test", JsonDatabase, ErrorMessage);
Manuel Klimekcb971c62012-04-04 12:07:46 +0000255 EXPECT_TRUE(ErrorMessage.empty()) << ErrorMessage;
256 return FoundCommand.CommandLine;
257}
258
259TEST(unescapeJsonCommandLine, ReturnsEmptyArrayOnEmptyString) {
260 std::vector<std::string> Result = unescapeJsonCommandLine("");
261 EXPECT_TRUE(Result.empty());
262}
263
264TEST(unescapeJsonCommandLine, SplitsOnSpaces) {
265 std::vector<std::string> Result = unescapeJsonCommandLine("a b c");
266 ASSERT_EQ(3ul, Result.size());
267 EXPECT_EQ("a", Result[0]);
268 EXPECT_EQ("b", Result[1]);
269 EXPECT_EQ("c", Result[2]);
270}
271
272TEST(unescapeJsonCommandLine, MungesMultipleSpaces) {
273 std::vector<std::string> Result = unescapeJsonCommandLine(" a b ");
274 ASSERT_EQ(2ul, Result.size());
275 EXPECT_EQ("a", Result[0]);
276 EXPECT_EQ("b", Result[1]);
277}
278
279TEST(unescapeJsonCommandLine, UnescapesBackslashCharacters) {
280 std::vector<std::string> Backslash = unescapeJsonCommandLine("a\\\\\\\\");
281 ASSERT_EQ(1ul, Backslash.size());
282 EXPECT_EQ("a\\", Backslash[0]);
283 std::vector<std::string> Quote = unescapeJsonCommandLine("a\\\\\\\"");
284 ASSERT_EQ(1ul, Quote.size());
285 EXPECT_EQ("a\"", Quote[0]);
286}
287
288TEST(unescapeJsonCommandLine, DoesNotMungeSpacesBetweenQuotes) {
289 std::vector<std::string> Result = unescapeJsonCommandLine("\\\" a b \\\"");
290 ASSERT_EQ(1ul, Result.size());
291 EXPECT_EQ(" a b ", Result[0]);
292}
293
294TEST(unescapeJsonCommandLine, AllowsMultipleQuotedArguments) {
295 std::vector<std::string> Result = unescapeJsonCommandLine(
296 " \\\" a \\\" \\\" b \\\" ");
297 ASSERT_EQ(2ul, Result.size());
298 EXPECT_EQ(" a ", Result[0]);
299 EXPECT_EQ(" b ", Result[1]);
300}
301
302TEST(unescapeJsonCommandLine, AllowsEmptyArgumentsInQuotes) {
303 std::vector<std::string> Result = unescapeJsonCommandLine(
304 "\\\"\\\"\\\"\\\"");
305 ASSERT_EQ(1ul, Result.size());
306 EXPECT_TRUE(Result[0].empty()) << Result[0];
307}
308
309TEST(unescapeJsonCommandLine, ParsesEscapedQuotesInQuotedStrings) {
310 std::vector<std::string> Result = unescapeJsonCommandLine(
311 "\\\"\\\\\\\"\\\"");
312 ASSERT_EQ(1ul, Result.size());
313 EXPECT_EQ("\"", Result[0]);
314}
315
316TEST(unescapeJsonCommandLine, ParsesMultipleArgumentsWithEscapedCharacters) {
317 std::vector<std::string> Result = unescapeJsonCommandLine(
318 " \\\\\\\" \\\"a \\\\\\\" b \\\" \\\"and\\\\\\\\c\\\" \\\\\\\"");
319 ASSERT_EQ(4ul, Result.size());
320 EXPECT_EQ("\"", Result[0]);
321 EXPECT_EQ("a \" b ", Result[1]);
322 EXPECT_EQ("and\\c", Result[2]);
323 EXPECT_EQ("\"", Result[3]);
324}
325
326TEST(unescapeJsonCommandLine, ParsesStringsWithoutSpacesIntoSingleArgument) {
327 std::vector<std::string> QuotedNoSpaces = unescapeJsonCommandLine(
328 "\\\"a\\\"\\\"b\\\"");
329 ASSERT_EQ(1ul, QuotedNoSpaces.size());
330 EXPECT_EQ("ab", QuotedNoSpaces[0]);
331
332 std::vector<std::string> MixedNoSpaces = unescapeJsonCommandLine(
333 "\\\"a\\\"bcd\\\"ef\\\"\\\"\\\"\\\"g\\\"");
334 ASSERT_EQ(1ul, MixedNoSpaces.size());
335 EXPECT_EQ("abcdefg", MixedNoSpaces[0]);
336}
337
338TEST(unescapeJsonCommandLine, ParsesQuotedStringWithoutClosingQuote) {
339 std::vector<std::string> Unclosed = unescapeJsonCommandLine("\\\"abc");
340 ASSERT_EQ(1ul, Unclosed.size());
341 EXPECT_EQ("abc", Unclosed[0]);
342
343 std::vector<std::string> Empty = unescapeJsonCommandLine("\\\"");
344 ASSERT_EQ(1ul, Empty.size());
345 EXPECT_EQ("", Empty[0]);
346}
347
Manuel Klimek30318e62012-04-18 07:41:50 +0000348TEST(FixedCompilationDatabase, ReturnsFixedCommandLine) {
349 std::vector<std::string> CommandLine;
350 CommandLine.push_back("one");
351 CommandLine.push_back("two");
352 FixedCompilationDatabase Database(".", CommandLine);
353 std::vector<CompileCommand> Result =
354 Database.getCompileCommands("source");
355 ASSERT_EQ(1ul, Result.size());
356 std::vector<std::string> ExpectedCommandLine(1, "clang-tool");
357 ExpectedCommandLine.insert(ExpectedCommandLine.end(),
358 CommandLine.begin(), CommandLine.end());
359 ExpectedCommandLine.push_back("source");
360 EXPECT_EQ(".", Result[0].Directory);
361 EXPECT_EQ(ExpectedCommandLine, Result[0].CommandLine);
362}
363
Manuel Klimeka3c70962012-07-13 12:31:45 +0000364TEST(FixedCompilationDatabase, GetAllFiles) {
365 std::vector<std::string> CommandLine;
366 CommandLine.push_back("one");
367 CommandLine.push_back("two");
368 FixedCompilationDatabase Database(".", CommandLine);
369
370 EXPECT_EQ(0ul, Database.getAllFiles().size());
371}
372
Manuel Klimek30318e62012-04-18 07:41:50 +0000373TEST(ParseFixedCompilationDatabase, ReturnsNullOnEmptyArgumentList) {
374 int Argc = 0;
375 llvm::OwningPtr<FixedCompilationDatabase> Database(
376 FixedCompilationDatabase::loadFromCommandLine(Argc, NULL));
377 EXPECT_FALSE(Database);
378 EXPECT_EQ(0, Argc);
379}
380
381TEST(ParseFixedCompilationDatabase, ReturnsNullWithoutDoubleDash) {
382 int Argc = 2;
383 const char *Argv[] = { "1", "2" };
384 llvm::OwningPtr<FixedCompilationDatabase> Database(
385 FixedCompilationDatabase::loadFromCommandLine(Argc, Argv));
386 EXPECT_FALSE(Database);
387 EXPECT_EQ(2, Argc);
388}
389
390TEST(ParseFixedCompilationDatabase, ReturnsArgumentsAfterDoubleDash) {
391 int Argc = 5;
392 const char *Argv[] = { "1", "2", "--\0no-constant-folding", "3", "4" };
393 llvm::OwningPtr<FixedCompilationDatabase> Database(
394 FixedCompilationDatabase::loadFromCommandLine(Argc, Argv));
395 ASSERT_TRUE(Database);
396 std::vector<CompileCommand> Result =
397 Database->getCompileCommands("source");
398 ASSERT_EQ(1ul, Result.size());
399 ASSERT_EQ(".", Result[0].Directory);
400 std::vector<std::string> CommandLine;
401 CommandLine.push_back("clang-tool");
402 CommandLine.push_back("3");
403 CommandLine.push_back("4");
404 CommandLine.push_back("source");
405 ASSERT_EQ(CommandLine, Result[0].CommandLine);
406 EXPECT_EQ(2, Argc);
407}
408
409TEST(ParseFixedCompilationDatabase, ReturnsEmptyCommandLine) {
410 int Argc = 3;
411 const char *Argv[] = { "1", "2", "--\0no-constant-folding" };
412 llvm::OwningPtr<FixedCompilationDatabase> Database(
413 FixedCompilationDatabase::loadFromCommandLine(Argc, Argv));
414 ASSERT_TRUE(Database);
415 std::vector<CompileCommand> Result =
416 Database->getCompileCommands("source");
417 ASSERT_EQ(1ul, Result.size());
418 ASSERT_EQ(".", Result[0].Directory);
419 std::vector<std::string> CommandLine;
420 CommandLine.push_back("clang-tool");
421 CommandLine.push_back("source");
422 ASSERT_EQ(CommandLine, Result[0].CommandLine);
423 EXPECT_EQ(2, Argc);
424}
425
Manuel Klimekcb971c62012-04-04 12:07:46 +0000426} // end namespace tooling
427} // end namespace clang