blob: b9a6d73a6d12b0ba33c2ea33fb8189489382d8b4 [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 Jasper8a5e8c32012-10-08 20:08:25 +000059 expected_files.push_back("//net/dir/file1");
60 expected_files.push_back("//net/dir/file2");
Manuel Klimeka3c70962012-07-13 12:31:45 +000061 EXPECT_EQ(expected_files, getAllFiles(
Daniel Jasper8a5e8c32012-10-08 20:08:25 +000062 "[{\"directory\":\"//net/dir\","
Manuel Klimeka3c70962012-07-13 12:31:45 +000063 "\"command\":\"command\","
64 "\"file\":\"file1\"},"
Daniel Jasper8a5e8c32012-10-08 20:08:25 +000065 " {\"directory\":\"//net/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) {
Daniel Jasper8a5e8c32012-10-08 20:08:25 +0000106 Trie.insert("//net/path/file.cc");
Daniel Jasperd3420c92012-10-08 16:08:15 +0000107 Trie.insert("file.cc");
Daniel Jasper8a5e8c32012-10-08 20:08:25 +0000108 EXPECT_EQ("//net/path/file.cc", find("//net/path/file.cc"));
Daniel Jasperd3420c92012-10-08 16:08:15 +0000109}
110
111TEST_F(FileMatchTrieTest, MatchingRelativePath) {
112 EXPECT_EQ("", find("file.cc"));
113}
114
115TEST_F(FileMatchTrieTest, ReturnsBestResults) {
Daniel Jasper8a5e8c32012-10-08 20:08:25 +0000116 Trie.insert("//net/d/c/b.cc");
117 Trie.insert("//net/d/b/b.cc");
118 EXPECT_EQ("//net/d/b/b.cc", find("//net/d/b/b.cc"));
Daniel Jasperd3420c92012-10-08 16:08:15 +0000119}
120
121TEST_F(FileMatchTrieTest, HandlesSymlinks) {
Daniel Jasper8a5e8c32012-10-08 20:08:25 +0000122 Trie.insert("//net/AA/file.cc");
123 EXPECT_EQ("//net/AA/file.cc", find("//net/aa/file.cc"));
Daniel Jasperd3420c92012-10-08 16:08:15 +0000124}
125
126TEST_F(FileMatchTrieTest, ReportsSymlinkAmbiguity) {
Daniel Jasper8a5e8c32012-10-08 20:08:25 +0000127 Trie.insert("//net/Aa/file.cc");
128 Trie.insert("//net/aA/file.cc");
129 EXPECT_TRUE(find("//net/aa/file.cc").empty());
Daniel Jasperd3420c92012-10-08 16:08:15 +0000130 EXPECT_EQ("Path is ambiguous", Error);
131}
132
133TEST_F(FileMatchTrieTest, LongerMatchingSuffixPreferred) {
Daniel Jasper8a5e8c32012-10-08 20:08:25 +0000134 Trie.insert("//net/src/Aa/file.cc");
135 Trie.insert("//net/src/aA/file.cc");
136 Trie.insert("//net/SRC/aa/file.cc");
137 EXPECT_EQ("//net/SRC/aa/file.cc", find("//net/src/aa/file.cc"));
Daniel Jasperd3420c92012-10-08 16:08:15 +0000138}
139
140TEST_F(FileMatchTrieTest, EmptyTrie) {
Daniel Jasper8a5e8c32012-10-08 20:08:25 +0000141 EXPECT_TRUE(find("//net/some/path").empty());
Daniel Jasperd3420c92012-10-08 16:08:15 +0000142}
143
144TEST_F(FileMatchTrieTest, NoResult) {
Daniel Jasper8a5e8c32012-10-08 20:08:25 +0000145 Trie.insert("//net/somepath/otherfile.cc");
146 Trie.insert("//net/otherpath/somefile.cc");
147 EXPECT_EQ("", find("//net/somepath/somefile.cc"));
Daniel Jasperd3420c92012-10-08 16:08:15 +0000148}
149
150TEST_F(FileMatchTrieTest, RootElementDifferent) {
Daniel Jasper8a5e8c32012-10-08 20:08:25 +0000151 Trie.insert("//net/path/file.cc");
152 Trie.insert("//net/otherpath/file.cc");
153 EXPECT_EQ("//net/path/file.cc", find("//net/path/file.cc"));
Daniel Jasperd3420c92012-10-08 16:08:15 +0000154}
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) {
Daniel Jasper8a5e8c32012-10-08 20:08:25 +0000170 StringRef Directory("//net/some/directory");
171 StringRef FileName("//net/path/to/a-file.cpp");
172 StringRef Command("//net/path/to/compiler and some arguments");
Manuel Klimekcb971c62012-04-04 12:07:46 +0000173 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;
Daniel Jasper8a5e8c32012-10-08 20:08:25 +0000182 EXPECT_EQ("//net/path/to/compiler",
183 FoundCommand.CommandLine[0]) << ErrorMessage;
Manuel Klimekcb971c62012-04-04 12:07:46 +0000184 EXPECT_EQ("and", FoundCommand.CommandLine[1]) << ErrorMessage;
185 EXPECT_EQ("some", FoundCommand.CommandLine[2]) << ErrorMessage;
186 EXPECT_EQ("arguments", FoundCommand.CommandLine[3]) << ErrorMessage;
187
188 CompileCommand NotFound = findCompileArgsInJsonDatabase(
189 "a-file.cpp",
190 ("[{\"directory\":\"" + Directory + "\"," +
191 "\"command\":\"" + Command + "\","
192 "\"file\":\"" + FileName + "\"}]").str(),
193 ErrorMessage);
194 EXPECT_TRUE(NotFound.Directory.empty()) << ErrorMessage;
195 EXPECT_TRUE(NotFound.CommandLine.empty()) << ErrorMessage;
196}
197
198TEST(findCompileArgsInJsonDatabase, ReadsCompileCommandLinesWithSpaces) {
Daniel Jasper8a5e8c32012-10-08 20:08:25 +0000199 StringRef Directory("//net/some/directory");
200 StringRef FileName("//net/path/to/a-file.cpp");
201 StringRef Command("\\\"//net/path to compiler\\\" \\\"and an argument\\\"");
Manuel Klimekcb971c62012-04-04 12:07:46 +0000202 std::string ErrorMessage;
203 CompileCommand FoundCommand = findCompileArgsInJsonDatabase(
204 FileName,
205 ("[{\"directory\":\"" + Directory + "\"," +
206 "\"command\":\"" + Command + "\","
207 "\"file\":\"" + FileName + "\"}]").str(),
208 ErrorMessage);
209 ASSERT_EQ(2u, FoundCommand.CommandLine.size());
Daniel Jasper8a5e8c32012-10-08 20:08:25 +0000210 EXPECT_EQ("//net/path to compiler",
211 FoundCommand.CommandLine[0]) << ErrorMessage;
Manuel Klimekcb971c62012-04-04 12:07:46 +0000212 EXPECT_EQ("and an argument", FoundCommand.CommandLine[1]) << ErrorMessage;
213}
214
215TEST(findCompileArgsInJsonDatabase, ReadsDirectoryWithSpaces) {
Daniel Jasper8a5e8c32012-10-08 20:08:25 +0000216 StringRef Directory("//net/some directory / with spaces");
217 StringRef FileName("//net/path/to/a-file.cpp");
Manuel Klimekcb971c62012-04-04 12:07:46 +0000218 StringRef Command("a command");
219 std::string ErrorMessage;
220 CompileCommand FoundCommand = findCompileArgsInJsonDatabase(
221 FileName,
222 ("[{\"directory\":\"" + Directory + "\"," +
223 "\"command\":\"" + Command + "\","
224 "\"file\":\"" + FileName + "\"}]").str(),
225 ErrorMessage);
226 EXPECT_EQ(Directory, FoundCommand.Directory) << ErrorMessage;
227}
228
229TEST(findCompileArgsInJsonDatabase, FindsEntry) {
Daniel Jasper8a5e8c32012-10-08 20:08:25 +0000230 StringRef Directory("//net/directory");
Manuel Klimekcb971c62012-04-04 12:07:46 +0000231 StringRef FileName("file");
232 StringRef Command("command");
233 std::string JsonDatabase = "[";
234 for (int I = 0; I < 10; ++I) {
235 if (I > 0) JsonDatabase += ",";
236 JsonDatabase +=
237 ("{\"directory\":\"" + Directory + Twine(I) + "\"," +
238 "\"command\":\"" + Command + Twine(I) + "\","
239 "\"file\":\"" + FileName + Twine(I) + "\"}").str();
240 }
241 JsonDatabase += "]";
242 std::string ErrorMessage;
243 CompileCommand FoundCommand = findCompileArgsInJsonDatabase(
Daniel Jasper8a5e8c32012-10-08 20:08:25 +0000244 "//net/directory4/file4", JsonDatabase, ErrorMessage);
245 EXPECT_EQ("//net/directory4", FoundCommand.Directory) << ErrorMessage;
Manuel Klimekcb971c62012-04-04 12:07:46 +0000246 ASSERT_EQ(1u, FoundCommand.CommandLine.size()) << ErrorMessage;
247 EXPECT_EQ("command4", FoundCommand.CommandLine[0]) << ErrorMessage;
248}
249
250static std::vector<std::string> unescapeJsonCommandLine(StringRef Command) {
251 std::string JsonDatabase =
Daniel Jasper8a5e8c32012-10-08 20:08:25 +0000252 ("[{\"directory\":\"//net/root\", \"file\":\"test\", \"command\": \"" +
Manuel Klimekcb971c62012-04-04 12:07:46 +0000253 Command + "\"}]").str();
254 std::string ErrorMessage;
255 CompileCommand FoundCommand = findCompileArgsInJsonDatabase(
Daniel Jasper8a5e8c32012-10-08 20:08:25 +0000256 "//net/root/test", JsonDatabase, ErrorMessage);
Manuel Klimekcb971c62012-04-04 12:07:46 +0000257 EXPECT_TRUE(ErrorMessage.empty()) << ErrorMessage;
258 return FoundCommand.CommandLine;
259}
260
261TEST(unescapeJsonCommandLine, ReturnsEmptyArrayOnEmptyString) {
262 std::vector<std::string> Result = unescapeJsonCommandLine("");
263 EXPECT_TRUE(Result.empty());
264}
265
266TEST(unescapeJsonCommandLine, SplitsOnSpaces) {
267 std::vector<std::string> Result = unescapeJsonCommandLine("a b c");
268 ASSERT_EQ(3ul, Result.size());
269 EXPECT_EQ("a", Result[0]);
270 EXPECT_EQ("b", Result[1]);
271 EXPECT_EQ("c", Result[2]);
272}
273
274TEST(unescapeJsonCommandLine, MungesMultipleSpaces) {
275 std::vector<std::string> Result = unescapeJsonCommandLine(" a b ");
276 ASSERT_EQ(2ul, Result.size());
277 EXPECT_EQ("a", Result[0]);
278 EXPECT_EQ("b", Result[1]);
279}
280
281TEST(unescapeJsonCommandLine, UnescapesBackslashCharacters) {
282 std::vector<std::string> Backslash = unescapeJsonCommandLine("a\\\\\\\\");
283 ASSERT_EQ(1ul, Backslash.size());
284 EXPECT_EQ("a\\", Backslash[0]);
285 std::vector<std::string> Quote = unescapeJsonCommandLine("a\\\\\\\"");
286 ASSERT_EQ(1ul, Quote.size());
287 EXPECT_EQ("a\"", Quote[0]);
288}
289
290TEST(unescapeJsonCommandLine, DoesNotMungeSpacesBetweenQuotes) {
291 std::vector<std::string> Result = unescapeJsonCommandLine("\\\" a b \\\"");
292 ASSERT_EQ(1ul, Result.size());
293 EXPECT_EQ(" a b ", Result[0]);
294}
295
296TEST(unescapeJsonCommandLine, AllowsMultipleQuotedArguments) {
297 std::vector<std::string> Result = unescapeJsonCommandLine(
298 " \\\" a \\\" \\\" b \\\" ");
299 ASSERT_EQ(2ul, Result.size());
300 EXPECT_EQ(" a ", Result[0]);
301 EXPECT_EQ(" b ", Result[1]);
302}
303
304TEST(unescapeJsonCommandLine, AllowsEmptyArgumentsInQuotes) {
305 std::vector<std::string> Result = unescapeJsonCommandLine(
306 "\\\"\\\"\\\"\\\"");
307 ASSERT_EQ(1ul, Result.size());
308 EXPECT_TRUE(Result[0].empty()) << Result[0];
309}
310
311TEST(unescapeJsonCommandLine, ParsesEscapedQuotesInQuotedStrings) {
312 std::vector<std::string> Result = unescapeJsonCommandLine(
313 "\\\"\\\\\\\"\\\"");
314 ASSERT_EQ(1ul, Result.size());
315 EXPECT_EQ("\"", Result[0]);
316}
317
318TEST(unescapeJsonCommandLine, ParsesMultipleArgumentsWithEscapedCharacters) {
319 std::vector<std::string> Result = unescapeJsonCommandLine(
320 " \\\\\\\" \\\"a \\\\\\\" b \\\" \\\"and\\\\\\\\c\\\" \\\\\\\"");
321 ASSERT_EQ(4ul, Result.size());
322 EXPECT_EQ("\"", Result[0]);
323 EXPECT_EQ("a \" b ", Result[1]);
324 EXPECT_EQ("and\\c", Result[2]);
325 EXPECT_EQ("\"", Result[3]);
326}
327
328TEST(unescapeJsonCommandLine, ParsesStringsWithoutSpacesIntoSingleArgument) {
329 std::vector<std::string> QuotedNoSpaces = unescapeJsonCommandLine(
330 "\\\"a\\\"\\\"b\\\"");
331 ASSERT_EQ(1ul, QuotedNoSpaces.size());
332 EXPECT_EQ("ab", QuotedNoSpaces[0]);
333
334 std::vector<std::string> MixedNoSpaces = unescapeJsonCommandLine(
335 "\\\"a\\\"bcd\\\"ef\\\"\\\"\\\"\\\"g\\\"");
336 ASSERT_EQ(1ul, MixedNoSpaces.size());
337 EXPECT_EQ("abcdefg", MixedNoSpaces[0]);
338}
339
340TEST(unescapeJsonCommandLine, ParsesQuotedStringWithoutClosingQuote) {
341 std::vector<std::string> Unclosed = unescapeJsonCommandLine("\\\"abc");
342 ASSERT_EQ(1ul, Unclosed.size());
343 EXPECT_EQ("abc", Unclosed[0]);
344
345 std::vector<std::string> Empty = unescapeJsonCommandLine("\\\"");
346 ASSERT_EQ(1ul, Empty.size());
347 EXPECT_EQ("", Empty[0]);
348}
349
Manuel Klimek30318e62012-04-18 07:41:50 +0000350TEST(FixedCompilationDatabase, ReturnsFixedCommandLine) {
351 std::vector<std::string> CommandLine;
352 CommandLine.push_back("one");
353 CommandLine.push_back("two");
354 FixedCompilationDatabase Database(".", CommandLine);
355 std::vector<CompileCommand> Result =
356 Database.getCompileCommands("source");
357 ASSERT_EQ(1ul, Result.size());
358 std::vector<std::string> ExpectedCommandLine(1, "clang-tool");
359 ExpectedCommandLine.insert(ExpectedCommandLine.end(),
360 CommandLine.begin(), CommandLine.end());
361 ExpectedCommandLine.push_back("source");
362 EXPECT_EQ(".", Result[0].Directory);
363 EXPECT_EQ(ExpectedCommandLine, Result[0].CommandLine);
364}
365
Manuel Klimeka3c70962012-07-13 12:31:45 +0000366TEST(FixedCompilationDatabase, GetAllFiles) {
367 std::vector<std::string> CommandLine;
368 CommandLine.push_back("one");
369 CommandLine.push_back("two");
370 FixedCompilationDatabase Database(".", CommandLine);
371
372 EXPECT_EQ(0ul, Database.getAllFiles().size());
373}
374
Manuel Klimek30318e62012-04-18 07:41:50 +0000375TEST(ParseFixedCompilationDatabase, ReturnsNullOnEmptyArgumentList) {
376 int Argc = 0;
377 llvm::OwningPtr<FixedCompilationDatabase> Database(
378 FixedCompilationDatabase::loadFromCommandLine(Argc, NULL));
379 EXPECT_FALSE(Database);
380 EXPECT_EQ(0, Argc);
381}
382
383TEST(ParseFixedCompilationDatabase, ReturnsNullWithoutDoubleDash) {
384 int Argc = 2;
385 const char *Argv[] = { "1", "2" };
386 llvm::OwningPtr<FixedCompilationDatabase> Database(
387 FixedCompilationDatabase::loadFromCommandLine(Argc, Argv));
388 EXPECT_FALSE(Database);
389 EXPECT_EQ(2, Argc);
390}
391
392TEST(ParseFixedCompilationDatabase, ReturnsArgumentsAfterDoubleDash) {
393 int Argc = 5;
394 const char *Argv[] = { "1", "2", "--\0no-constant-folding", "3", "4" };
395 llvm::OwningPtr<FixedCompilationDatabase> Database(
396 FixedCompilationDatabase::loadFromCommandLine(Argc, Argv));
397 ASSERT_TRUE(Database);
398 std::vector<CompileCommand> Result =
399 Database->getCompileCommands("source");
400 ASSERT_EQ(1ul, Result.size());
401 ASSERT_EQ(".", Result[0].Directory);
402 std::vector<std::string> CommandLine;
403 CommandLine.push_back("clang-tool");
404 CommandLine.push_back("3");
405 CommandLine.push_back("4");
406 CommandLine.push_back("source");
407 ASSERT_EQ(CommandLine, Result[0].CommandLine);
408 EXPECT_EQ(2, Argc);
409}
410
411TEST(ParseFixedCompilationDatabase, ReturnsEmptyCommandLine) {
412 int Argc = 3;
413 const char *Argv[] = { "1", "2", "--\0no-constant-folding" };
414 llvm::OwningPtr<FixedCompilationDatabase> Database(
415 FixedCompilationDatabase::loadFromCommandLine(Argc, Argv));
416 ASSERT_TRUE(Database);
417 std::vector<CompileCommand> Result =
418 Database->getCompileCommands("source");
419 ASSERT_EQ(1ul, Result.size());
420 ASSERT_EQ(".", Result[0].Directory);
421 std::vector<std::string> CommandLine;
422 CommandLine.push_back("clang-tool");
423 CommandLine.push_back("source");
424 ASSERT_EQ(CommandLine, Result[0].CommandLine);
425 EXPECT_EQ(2, Argc);
426}
427
Manuel Klimekcb971c62012-04-04 12:07:46 +0000428} // end namespace tooling
429} // end namespace clang