xref: /llvm-project/clang/unittests/Tooling/CompilationDatabaseTest.cpp (revision 5167e2d1afc5a6c89cbbfea4243864b58a8ce37a)
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/DeclCXX.h"
11 #include "clang/AST/DeclGroup.h"
12 #include "clang/Frontend/FrontendAction.h"
13 #include "clang/Tooling/FileMatchTrie.h"
14 #include "clang/Tooling/JSONCompilationDatabase.h"
15 #include "clang/Tooling/Tooling.h"
16 #include "llvm/Support/Path.h"
17 #include "gtest/gtest.h"
18 
19 namespace clang {
20 namespace tooling {
21 
22 static void expectFailure(StringRef JSONDatabase, StringRef Explanation) {
23   std::string ErrorMessage;
24   EXPECT_EQ(nullptr,
25             JSONCompilationDatabase::loadFromBuffer(JSONDatabase, ErrorMessage,
26                                                     JSONCommandLineSyntax::Gnu))
27       << "Expected an error because of: " << Explanation.str();
28 }
29 
30 TEST(JSONCompilationDatabase, ErrsOnInvalidFormat) {
31   expectFailure("", "Empty database");
32   expectFailure("{", "Invalid JSON");
33   expectFailure("[[]]", "Array instead of object");
34   expectFailure("[{\"a\":[]}]", "Array instead of value");
35   expectFailure("[{\"a\":\"b\"}]", "Unknown key");
36   expectFailure("[{[]:\"\"}]", "Incorrectly typed entry");
37   expectFailure("[{}]", "Empty entry");
38   expectFailure("[{\"directory\":\"\",\"command\":\"\"}]", "Missing file");
39   expectFailure("[{\"directory\":\"\",\"file\":\"\"}]", "Missing command or arguments");
40   expectFailure("[{\"command\":\"\",\"file\":\"\"}]", "Missing directory");
41   expectFailure("[{\"directory\":\"\",\"arguments\":[]}]", "Missing file");
42   expectFailure("[{\"arguments\":\"\",\"file\":\"\"}]", "Missing directory");
43   expectFailure("[{\"directory\":\"\",\"arguments\":\"\",\"file\":\"\"}]", "Arguments not array");
44   expectFailure("[{\"directory\":\"\",\"command\":[],\"file\":\"\"}]", "Command not string");
45   expectFailure("[{\"directory\":\"\",\"arguments\":[[]],\"file\":\"\"}]",
46                 "Arguments contain non-string");
47   expectFailure("[{\"output\":[]}]", "Expected strings as value.");
48 }
49 
50 static std::vector<std::string> getAllFiles(StringRef JSONDatabase,
51                                             std::string &ErrorMessage,
52                                             JSONCommandLineSyntax Syntax) {
53   std::unique_ptr<CompilationDatabase> Database(
54       JSONCompilationDatabase::loadFromBuffer(JSONDatabase, ErrorMessage,
55                                               Syntax));
56   if (!Database) {
57     ADD_FAILURE() << ErrorMessage;
58     return std::vector<std::string>();
59   }
60   return Database->getAllFiles();
61 }
62 
63 static std::vector<CompileCommand>
64 getAllCompileCommands(JSONCommandLineSyntax Syntax, StringRef JSONDatabase,
65                       std::string &ErrorMessage) {
66   std::unique_ptr<CompilationDatabase> Database(
67       JSONCompilationDatabase::loadFromBuffer(JSONDatabase, ErrorMessage,
68                                               Syntax));
69   if (!Database) {
70     ADD_FAILURE() << ErrorMessage;
71     return std::vector<CompileCommand>();
72   }
73   return Database->getAllCompileCommands();
74 }
75 
76 TEST(JSONCompilationDatabase, GetAllFiles) {
77   std::string ErrorMessage;
78   EXPECT_EQ(std::vector<std::string>(),
79             getAllFiles("[]", ErrorMessage, JSONCommandLineSyntax::Gnu))
80       << ErrorMessage;
81 
82   std::vector<std::string> expected_files;
83   SmallString<16> PathStorage;
84   llvm::sys::path::native("//net/dir/file1", PathStorage);
85   expected_files.push_back(PathStorage.str());
86   llvm::sys::path::native("//net/dir/file2", PathStorage);
87   expected_files.push_back(PathStorage.str());
88   EXPECT_EQ(expected_files,
89             getAllFiles("[{\"directory\":\"//net/dir\","
90                         "\"command\":\"command\","
91                         "\"file\":\"file1\"},"
92                         " {\"directory\":\"//net/dir\","
93                         "\"command\":\"command\","
94                         "\"file\":\"file2\"}]",
95                         ErrorMessage, JSONCommandLineSyntax::Gnu))
96       << ErrorMessage;
97 }
98 
99 TEST(JSONCompilationDatabase, GetAllCompileCommands) {
100   std::string ErrorMessage;
101   EXPECT_EQ(
102       0u, getAllCompileCommands(JSONCommandLineSyntax::Gnu, "[]", ErrorMessage)
103               .size())
104       << ErrorMessage;
105 
106   StringRef Directory1("//net/dir1");
107   StringRef FileName1("file1");
108   StringRef Command1("command1");
109   StringRef Output1("file1.o");
110   StringRef Directory2("//net/dir2");
111   StringRef FileName2("file2");
112   StringRef Command2("command2");
113   StringRef Output2("");
114 
115   std::vector<CompileCommand> Commands = getAllCompileCommands(
116       JSONCommandLineSyntax::Gnu,
117       ("[{\"directory\":\"" + Directory1 + "\"," + "\"command\":\"" + Command1 +
118        "\","
119        "\"file\":\"" +
120        FileName1 + "\", \"output\":\"" +
121        Output1 + "\"},"
122                    " {\"directory\":\"" +
123        Directory2 + "\"," + "\"command\":\"" + Command2 + "\","
124                                                           "\"file\":\"" +
125        FileName2 + "\"}]")
126           .str(),
127       ErrorMessage);
128   EXPECT_EQ(2U, Commands.size()) << ErrorMessage;
129   EXPECT_EQ(Directory1, Commands[0].Directory) << ErrorMessage;
130   EXPECT_EQ(FileName1, Commands[0].Filename) << ErrorMessage;
131   EXPECT_EQ(Output1, Commands[0].Output) << ErrorMessage;
132   ASSERT_EQ(1u, Commands[0].CommandLine.size());
133   EXPECT_EQ(Command1, Commands[0].CommandLine[0]) << ErrorMessage;
134   EXPECT_EQ(Directory2, Commands[1].Directory) << ErrorMessage;
135   EXPECT_EQ(FileName2, Commands[1].Filename) << ErrorMessage;
136   EXPECT_EQ(Output2, Commands[1].Output) << ErrorMessage;
137   ASSERT_EQ(1u, Commands[1].CommandLine.size());
138   EXPECT_EQ(Command2, Commands[1].CommandLine[0]) << ErrorMessage;
139 
140   // Check that order is preserved.
141   Commands = getAllCompileCommands(
142       JSONCommandLineSyntax::Gnu,
143       ("[{\"directory\":\"" + Directory2 + "\"," + "\"command\":\"" + Command2 +
144        "\","
145        "\"file\":\"" +
146        FileName2 + "\"},"
147                    " {\"directory\":\"" +
148        Directory1 + "\"," + "\"command\":\"" + Command1 + "\","
149                                                           "\"file\":\"" +
150        FileName1 + "\"}]")
151           .str(),
152       ErrorMessage);
153   EXPECT_EQ(2U, Commands.size()) << ErrorMessage;
154   EXPECT_EQ(Directory2, Commands[0].Directory) << ErrorMessage;
155   EXPECT_EQ(FileName2, Commands[0].Filename) << ErrorMessage;
156   ASSERT_EQ(1u, Commands[0].CommandLine.size());
157   EXPECT_EQ(Command2, Commands[0].CommandLine[0]) << ErrorMessage;
158   EXPECT_EQ(Directory1, Commands[1].Directory) << ErrorMessage;
159   EXPECT_EQ(FileName1, Commands[1].Filename) << ErrorMessage;
160   ASSERT_EQ(1u, Commands[1].CommandLine.size());
161   EXPECT_EQ(Command1, Commands[1].CommandLine[0]) << ErrorMessage;
162 }
163 
164 static CompileCommand findCompileArgsInJsonDatabase(StringRef FileName,
165                                                     StringRef JSONDatabase,
166                                                     std::string &ErrorMessage) {
167   std::unique_ptr<CompilationDatabase> Database(
168       JSONCompilationDatabase::loadFromBuffer(JSONDatabase, ErrorMessage,
169                                               JSONCommandLineSyntax::Gnu));
170   if (!Database)
171     return CompileCommand();
172   std::vector<CompileCommand> Commands = Database->getCompileCommands(FileName);
173   EXPECT_LE(Commands.size(), 1u);
174   if (Commands.empty())
175     return CompileCommand();
176   return Commands[0];
177 }
178 
179 TEST(JSONCompilationDatabase, ArgumentsPreferredOverCommand) {
180    StringRef Directory("//net/dir");
181    StringRef FileName("//net/dir/filename");
182    StringRef Command("command");
183    StringRef Arguments = "arguments";
184    Twine ArgumentsAccumulate;
185    std::string ErrorMessage;
186    CompileCommand FoundCommand = findCompileArgsInJsonDatabase(
187       FileName,
188       ("[{\"directory\":\"" + Directory + "\","
189          "\"arguments\":[\"" + Arguments + "\"],"
190          "\"command\":\"" + Command + "\","
191          "\"file\":\"" + FileName + "\"}]").str(),
192       ErrorMessage);
193    EXPECT_EQ(Directory, FoundCommand.Directory) << ErrorMessage;
194    EXPECT_EQ(1u, FoundCommand.CommandLine.size()) << ErrorMessage;
195    EXPECT_EQ(Arguments, FoundCommand.CommandLine[0]) << ErrorMessage;
196 }
197 
198 struct FakeComparator : public PathComparator {
199   ~FakeComparator() override {}
200   bool equivalent(StringRef FileA, StringRef FileB) const override {
201     return FileA.equals_lower(FileB);
202   }
203 };
204 
205 class FileMatchTrieTest : public ::testing::Test {
206 protected:
207   FileMatchTrieTest() : Trie(new FakeComparator()) {}
208 
209   StringRef find(StringRef Path) {
210     llvm::raw_string_ostream ES(Error);
211     return Trie.findEquivalent(Path, ES);
212   }
213 
214   FileMatchTrie Trie;
215   std::string Error;
216 };
217 
218 TEST_F(FileMatchTrieTest, InsertingRelativePath) {
219   Trie.insert("//net/path/file.cc");
220   Trie.insert("file.cc");
221   EXPECT_EQ("//net/path/file.cc", find("//net/path/file.cc"));
222 }
223 
224 TEST_F(FileMatchTrieTest, MatchingRelativePath) {
225   EXPECT_EQ("", find("file.cc"));
226 }
227 
228 TEST_F(FileMatchTrieTest, ReturnsBestResults) {
229   Trie.insert("//net/d/c/b.cc");
230   Trie.insert("//net/d/b/b.cc");
231   EXPECT_EQ("//net/d/b/b.cc", find("//net/d/b/b.cc"));
232 }
233 
234 TEST_F(FileMatchTrieTest, HandlesSymlinks) {
235   Trie.insert("//net/AA/file.cc");
236   EXPECT_EQ("//net/AA/file.cc", find("//net/aa/file.cc"));
237 }
238 
239 TEST_F(FileMatchTrieTest, ReportsSymlinkAmbiguity) {
240   Trie.insert("//net/Aa/file.cc");
241   Trie.insert("//net/aA/file.cc");
242   EXPECT_TRUE(find("//net/aa/file.cc").empty());
243   EXPECT_EQ("Path is ambiguous", Error);
244 }
245 
246 TEST_F(FileMatchTrieTest, LongerMatchingSuffixPreferred) {
247   Trie.insert("//net/src/Aa/file.cc");
248   Trie.insert("//net/src/aA/file.cc");
249   Trie.insert("//net/SRC/aa/file.cc");
250   EXPECT_EQ("//net/SRC/aa/file.cc", find("//net/src/aa/file.cc"));
251 }
252 
253 TEST_F(FileMatchTrieTest, EmptyTrie) {
254   EXPECT_TRUE(find("//net/some/path").empty());
255 }
256 
257 TEST_F(FileMatchTrieTest, NoResult) {
258   Trie.insert("//net/somepath/otherfile.cc");
259   Trie.insert("//net/otherpath/somefile.cc");
260   EXPECT_EQ("", find("//net/somepath/somefile.cc"));
261 }
262 
263 TEST_F(FileMatchTrieTest, RootElementDifferent) {
264   Trie.insert("//net/path/file.cc");
265   Trie.insert("//net/otherpath/file.cc");
266   EXPECT_EQ("//net/path/file.cc", find("//net/path/file.cc"));
267 }
268 
269 TEST_F(FileMatchTrieTest, CannotResolveRelativePath) {
270   EXPECT_EQ("", find("relative-path.cc"));
271   EXPECT_EQ("Cannot resolve relative paths", Error);
272 }
273 
274 TEST(findCompileArgsInJsonDatabase, FindsNothingIfEmpty) {
275   std::string ErrorMessage;
276   CompileCommand NotFound = findCompileArgsInJsonDatabase(
277     "a-file.cpp", "", ErrorMessage);
278   EXPECT_TRUE(NotFound.CommandLine.empty()) << ErrorMessage;
279   EXPECT_TRUE(NotFound.Directory.empty()) << ErrorMessage;
280 }
281 
282 TEST(findCompileArgsInJsonDatabase, ReadsSingleEntry) {
283   StringRef Directory("//net/some/directory");
284   StringRef FileName("//net/path/to/a-file.cpp");
285   StringRef Command("//net/path/to/compiler and some arguments");
286   std::string ErrorMessage;
287   CompileCommand FoundCommand = findCompileArgsInJsonDatabase(
288     FileName,
289     ("[{\"directory\":\"" + Directory + "\"," +
290        "\"command\":\"" + Command + "\","
291        "\"file\":\"" + FileName + "\"}]").str(),
292     ErrorMessage);
293   EXPECT_EQ(Directory, FoundCommand.Directory) << ErrorMessage;
294   ASSERT_EQ(4u, FoundCommand.CommandLine.size()) << ErrorMessage;
295   EXPECT_EQ("//net/path/to/compiler",
296             FoundCommand.CommandLine[0]) << ErrorMessage;
297   EXPECT_EQ("and", FoundCommand.CommandLine[1]) << ErrorMessage;
298   EXPECT_EQ("some", FoundCommand.CommandLine[2]) << ErrorMessage;
299   EXPECT_EQ("arguments", FoundCommand.CommandLine[3]) << ErrorMessage;
300 
301   CompileCommand NotFound = findCompileArgsInJsonDatabase(
302     "a-file.cpp",
303     ("[{\"directory\":\"" + Directory + "\"," +
304        "\"command\":\"" + Command + "\","
305        "\"file\":\"" + FileName + "\"}]").str(),
306     ErrorMessage);
307   EXPECT_TRUE(NotFound.Directory.empty()) << ErrorMessage;
308   EXPECT_TRUE(NotFound.CommandLine.empty()) << ErrorMessage;
309 }
310 
311 TEST(findCompileArgsInJsonDatabase, ReadsCompileCommandLinesWithSpaces) {
312   StringRef Directory("//net/some/directory");
313   StringRef FileName("//net/path/to/a-file.cpp");
314   StringRef Command("\\\"//net/path to compiler\\\" \\\"and an argument\\\"");
315   std::string ErrorMessage;
316   CompileCommand FoundCommand = findCompileArgsInJsonDatabase(
317     FileName,
318     ("[{\"directory\":\"" + Directory + "\"," +
319        "\"command\":\"" + Command + "\","
320        "\"file\":\"" + FileName + "\"}]").str(),
321     ErrorMessage);
322   ASSERT_EQ(2u, FoundCommand.CommandLine.size());
323   EXPECT_EQ("//net/path to compiler",
324             FoundCommand.CommandLine[0]) << ErrorMessage;
325   EXPECT_EQ("and an argument", FoundCommand.CommandLine[1]) << ErrorMessage;
326 }
327 
328 TEST(findCompileArgsInJsonDatabase, ReadsDirectoryWithSpaces) {
329   StringRef Directory("//net/some directory / with spaces");
330   StringRef FileName("//net/path/to/a-file.cpp");
331   StringRef Command("a command");
332   std::string ErrorMessage;
333   CompileCommand FoundCommand = findCompileArgsInJsonDatabase(
334     FileName,
335     ("[{\"directory\":\"" + Directory + "\"," +
336        "\"command\":\"" + Command + "\","
337        "\"file\":\"" + FileName + "\"}]").str(),
338     ErrorMessage);
339   EXPECT_EQ(Directory, FoundCommand.Directory) << ErrorMessage;
340 }
341 
342 TEST(findCompileArgsInJsonDatabase, FindsEntry) {
343   StringRef Directory("//net/directory");
344   StringRef FileName("file");
345   StringRef Command("command");
346   std::string JsonDatabase = "[";
347   for (int I = 0; I < 10; ++I) {
348     if (I > 0) JsonDatabase += ",";
349     JsonDatabase +=
350       ("{\"directory\":\"" + Directory + Twine(I) + "\"," +
351         "\"command\":\"" + Command + Twine(I) + "\","
352         "\"file\":\"" + FileName + Twine(I) + "\"}").str();
353   }
354   JsonDatabase += "]";
355   std::string ErrorMessage;
356   CompileCommand FoundCommand = findCompileArgsInJsonDatabase(
357     "//net/directory4/file4", JsonDatabase, ErrorMessage);
358   EXPECT_EQ("//net/directory4", FoundCommand.Directory) << ErrorMessage;
359   ASSERT_EQ(1u, FoundCommand.CommandLine.size()) << ErrorMessage;
360   EXPECT_EQ("command4", FoundCommand.CommandLine[0]) << ErrorMessage;
361 }
362 
363 static std::vector<std::string> unescapeJsonCommandLine(StringRef Command) {
364   std::string JsonDatabase =
365     ("[{\"directory\":\"//net/root\", \"file\":\"test\", \"command\": \"" +
366      Command + "\"}]").str();
367   std::string ErrorMessage;
368   CompileCommand FoundCommand = findCompileArgsInJsonDatabase(
369     "//net/root/test", JsonDatabase, ErrorMessage);
370   EXPECT_TRUE(ErrorMessage.empty()) << ErrorMessage;
371   return FoundCommand.CommandLine;
372 }
373 
374 TEST(unescapeJsonCommandLine, ReturnsEmptyArrayOnEmptyString) {
375   std::vector<std::string> Result = unescapeJsonCommandLine("");
376   EXPECT_TRUE(Result.empty());
377 }
378 
379 TEST(unescapeJsonCommandLine, SplitsOnSpaces) {
380   std::vector<std::string> Result = unescapeJsonCommandLine("a b c");
381   ASSERT_EQ(3ul, Result.size());
382   EXPECT_EQ("a", Result[0]);
383   EXPECT_EQ("b", Result[1]);
384   EXPECT_EQ("c", Result[2]);
385 }
386 
387 TEST(unescapeJsonCommandLine, MungesMultipleSpaces) {
388   std::vector<std::string> Result = unescapeJsonCommandLine("   a   b   ");
389   ASSERT_EQ(2ul, Result.size());
390   EXPECT_EQ("a", Result[0]);
391   EXPECT_EQ("b", Result[1]);
392 }
393 
394 TEST(unescapeJsonCommandLine, UnescapesBackslashCharacters) {
395   std::vector<std::string> Backslash = unescapeJsonCommandLine("a\\\\\\\\");
396   ASSERT_EQ(1ul, Backslash.size());
397   EXPECT_EQ("a\\", Backslash[0]);
398   std::vector<std::string> Quote = unescapeJsonCommandLine("a\\\\\\\"");
399   ASSERT_EQ(1ul, Quote.size());
400   EXPECT_EQ("a\"", Quote[0]);
401 }
402 
403 TEST(unescapeJsonCommandLine, DoesNotMungeSpacesBetweenQuotes) {
404   std::vector<std::string> Result = unescapeJsonCommandLine("\\\"  a  b  \\\"");
405   ASSERT_EQ(1ul, Result.size());
406   EXPECT_EQ("  a  b  ", Result[0]);
407 }
408 
409 TEST(unescapeJsonCommandLine, AllowsMultipleQuotedArguments) {
410   std::vector<std::string> Result = unescapeJsonCommandLine(
411       "  \\\" a \\\"  \\\" b \\\"  ");
412   ASSERT_EQ(2ul, Result.size());
413   EXPECT_EQ(" a ", Result[0]);
414   EXPECT_EQ(" b ", Result[1]);
415 }
416 
417 TEST(unescapeJsonCommandLine, AllowsEmptyArgumentsInQuotes) {
418   std::vector<std::string> Result = unescapeJsonCommandLine(
419       "\\\"\\\"\\\"\\\"");
420   ASSERT_EQ(1ul, Result.size());
421   EXPECT_TRUE(Result[0].empty()) << Result[0];
422 }
423 
424 TEST(unescapeJsonCommandLine, ParsesEscapedQuotesInQuotedStrings) {
425   std::vector<std::string> Result = unescapeJsonCommandLine(
426       "\\\"\\\\\\\"\\\"");
427   ASSERT_EQ(1ul, Result.size());
428   EXPECT_EQ("\"", Result[0]);
429 }
430 
431 TEST(unescapeJsonCommandLine, ParsesMultipleArgumentsWithEscapedCharacters) {
432   std::vector<std::string> Result = unescapeJsonCommandLine(
433       "  \\\\\\\"  \\\"a \\\\\\\" b \\\"     \\\"and\\\\\\\\c\\\"   \\\\\\\"");
434   ASSERT_EQ(4ul, Result.size());
435   EXPECT_EQ("\"", Result[0]);
436   EXPECT_EQ("a \" b ", Result[1]);
437   EXPECT_EQ("and\\c", Result[2]);
438   EXPECT_EQ("\"", Result[3]);
439 }
440 
441 TEST(unescapeJsonCommandLine, ParsesStringsWithoutSpacesIntoSingleArgument) {
442   std::vector<std::string> QuotedNoSpaces = unescapeJsonCommandLine(
443       "\\\"a\\\"\\\"b\\\"");
444   ASSERT_EQ(1ul, QuotedNoSpaces.size());
445   EXPECT_EQ("ab", QuotedNoSpaces[0]);
446 
447   std::vector<std::string> MixedNoSpaces = unescapeJsonCommandLine(
448       "\\\"a\\\"bcd\\\"ef\\\"\\\"\\\"\\\"g\\\"");
449   ASSERT_EQ(1ul, MixedNoSpaces.size());
450   EXPECT_EQ("abcdefg", MixedNoSpaces[0]);
451 }
452 
453 TEST(unescapeJsonCommandLine, ParsesQuotedStringWithoutClosingQuote) {
454   std::vector<std::string> Unclosed = unescapeJsonCommandLine("\\\"abc");
455   ASSERT_EQ(1ul, Unclosed.size());
456   EXPECT_EQ("abc", Unclosed[0]);
457 
458   std::vector<std::string> Empty = unescapeJsonCommandLine("\\\"");
459   ASSERT_EQ(1ul, Empty.size());
460   EXPECT_EQ("", Empty[0]);
461 }
462 
463 TEST(unescapeJsonCommandLine, ParsesSingleQuotedString) {
464   std::vector<std::string> Args = unescapeJsonCommandLine("a'\\\\b \\\"c\\\"'");
465   ASSERT_EQ(1ul, Args.size());
466   EXPECT_EQ("a\\b \"c\"", Args[0]);
467 }
468 
469 TEST(FixedCompilationDatabase, ReturnsFixedCommandLine) {
470   std::vector<std::string> CommandLine;
471   CommandLine.push_back("one");
472   CommandLine.push_back("two");
473   FixedCompilationDatabase Database(".", CommandLine);
474   StringRef FileName("source");
475   std::vector<CompileCommand> Result =
476     Database.getCompileCommands(FileName);
477   ASSERT_EQ(1ul, Result.size());
478   std::vector<std::string> ExpectedCommandLine(1, "clang-tool");
479   ExpectedCommandLine.insert(ExpectedCommandLine.end(),
480                              CommandLine.begin(), CommandLine.end());
481   ExpectedCommandLine.push_back("source");
482   EXPECT_EQ(".", Result[0].Directory);
483   EXPECT_EQ(FileName, Result[0].Filename);
484   EXPECT_EQ(ExpectedCommandLine, Result[0].CommandLine);
485 }
486 
487 TEST(FixedCompilationDatabase, GetAllFiles) {
488   std::vector<std::string> CommandLine;
489   CommandLine.push_back("one");
490   CommandLine.push_back("two");
491   FixedCompilationDatabase Database(".", CommandLine);
492 
493   EXPECT_EQ(0ul, Database.getAllFiles().size());
494 }
495 
496 TEST(FixedCompilationDatabase, GetAllCompileCommands) {
497   std::vector<std::string> CommandLine;
498   CommandLine.push_back("one");
499   CommandLine.push_back("two");
500   FixedCompilationDatabase Database(".", CommandLine);
501 
502   EXPECT_EQ(0ul, Database.getAllCompileCommands().size());
503 }
504 
505 TEST(ParseFixedCompilationDatabase, ReturnsNullOnEmptyArgumentList) {
506   int Argc = 0;
507   std::string ErrorMsg;
508   std::unique_ptr<FixedCompilationDatabase> Database =
509       FixedCompilationDatabase::loadFromCommandLine(Argc, nullptr, ErrorMsg);
510   EXPECT_FALSE(Database);
511   EXPECT_TRUE(ErrorMsg.empty());
512   EXPECT_EQ(0, Argc);
513 }
514 
515 TEST(ParseFixedCompilationDatabase, ReturnsNullWithoutDoubleDash) {
516   int Argc = 2;
517   const char *Argv[] = { "1", "2" };
518   std::string ErrorMsg;
519   std::unique_ptr<FixedCompilationDatabase> Database(
520       FixedCompilationDatabase::loadFromCommandLine(Argc, Argv, ErrorMsg));
521   EXPECT_FALSE(Database);
522   EXPECT_TRUE(ErrorMsg.empty());
523   EXPECT_EQ(2, Argc);
524 }
525 
526 TEST(ParseFixedCompilationDatabase, ReturnsArgumentsAfterDoubleDash) {
527   int Argc = 5;
528   const char *Argv[] = {
529     "1", "2", "--\0no-constant-folding", "-DDEF3", "-DDEF4"
530   };
531   std::string ErrorMsg;
532   std::unique_ptr<FixedCompilationDatabase> Database(
533       FixedCompilationDatabase::loadFromCommandLine(Argc, Argv, ErrorMsg));
534   ASSERT_TRUE((bool)Database);
535   ASSERT_TRUE(ErrorMsg.empty());
536   std::vector<CompileCommand> Result =
537     Database->getCompileCommands("source");
538   ASSERT_EQ(1ul, Result.size());
539   ASSERT_EQ(".", Result[0].Directory);
540   std::vector<std::string> CommandLine;
541   CommandLine.push_back("clang-tool");
542   CommandLine.push_back("-DDEF3");
543   CommandLine.push_back("-DDEF4");
544   CommandLine.push_back("source");
545   ASSERT_EQ(CommandLine, Result[0].CommandLine);
546   EXPECT_EQ(2, Argc);
547 }
548 
549 TEST(ParseFixedCompilationDatabase, ReturnsEmptyCommandLine) {
550   int Argc = 3;
551   const char *Argv[] = { "1", "2", "--\0no-constant-folding" };
552   std::string ErrorMsg;
553   std::unique_ptr<FixedCompilationDatabase> Database =
554       FixedCompilationDatabase::loadFromCommandLine(Argc, Argv, ErrorMsg);
555   ASSERT_TRUE((bool)Database);
556   ASSERT_TRUE(ErrorMsg.empty());
557   std::vector<CompileCommand> Result =
558     Database->getCompileCommands("source");
559   ASSERT_EQ(1ul, Result.size());
560   ASSERT_EQ(".", Result[0].Directory);
561   std::vector<std::string> CommandLine;
562   CommandLine.push_back("clang-tool");
563   CommandLine.push_back("source");
564   ASSERT_EQ(CommandLine, Result[0].CommandLine);
565   EXPECT_EQ(2, Argc);
566 }
567 
568 TEST(ParseFixedCompilationDatabase, HandlesPositionalArgs) {
569   const char *Argv[] = {"1", "2", "--", "-c", "somefile.cpp", "-DDEF3"};
570   int Argc = sizeof(Argv) / sizeof(char*);
571   std::string ErrorMsg;
572   std::unique_ptr<FixedCompilationDatabase> Database =
573       FixedCompilationDatabase::loadFromCommandLine(Argc, Argv, ErrorMsg);
574   ASSERT_TRUE((bool)Database);
575   ASSERT_TRUE(ErrorMsg.empty());
576   std::vector<CompileCommand> Result =
577     Database->getCompileCommands("source");
578   ASSERT_EQ(1ul, Result.size());
579   ASSERT_EQ(".", Result[0].Directory);
580   std::vector<std::string> Expected;
581   Expected.push_back("clang-tool");
582   Expected.push_back("-c");
583   Expected.push_back("-DDEF3");
584   Expected.push_back("source");
585   ASSERT_EQ(Expected, Result[0].CommandLine);
586   EXPECT_EQ(2, Argc);
587 }
588 
589 TEST(ParseFixedCompilationDatabase, HandlesPositionalArgsSyntaxOnly) {
590   // Adjust the given command line arguments to ensure that any positional
591   // arguments in them are stripped.
592   const char *Argv[] = {"--", "somefile.cpp", "-fsyntax-only", "-DDEF3"};
593   int Argc = llvm::array_lengthof(Argv);
594   std::string ErrorMessage;
595   std::unique_ptr<CompilationDatabase> Database =
596       FixedCompilationDatabase::loadFromCommandLine(Argc, Argv, ErrorMessage);
597   ASSERT_TRUE((bool)Database);
598   ASSERT_TRUE(ErrorMessage.empty());
599   std::vector<CompileCommand> Result = Database->getCompileCommands("source");
600   ASSERT_EQ(1ul, Result.size());
601   ASSERT_EQ(".", Result[0].Directory);
602   std::vector<std::string> Expected;
603   Expected.push_back("clang-tool");
604   Expected.push_back("-fsyntax-only");
605   Expected.push_back("-DDEF3");
606   Expected.push_back("source");
607   ASSERT_EQ(Expected, Result[0].CommandLine);
608 }
609 
610 TEST(ParseFixedCompilationDatabase, HandlesArgv0) {
611   const char *Argv[] = {"1", "2", "--", "mytool", "somefile.cpp"};
612   int Argc = sizeof(Argv) / sizeof(char*);
613   std::string ErrorMsg;
614   std::unique_ptr<FixedCompilationDatabase> Database =
615       FixedCompilationDatabase::loadFromCommandLine(Argc, Argv, ErrorMsg);
616   ASSERT_TRUE((bool)Database);
617   ASSERT_TRUE(ErrorMsg.empty());
618   std::vector<CompileCommand> Result =
619     Database->getCompileCommands("source");
620   ASSERT_EQ(1ul, Result.size());
621   ASSERT_EQ(".", Result[0].Directory);
622   std::vector<std::string> Expected;
623   Expected.push_back("clang-tool");
624   Expected.push_back("source");
625   ASSERT_EQ(Expected, Result[0].CommandLine);
626   EXPECT_EQ(2, Argc);
627 }
628 
629 struct MemCDB : public CompilationDatabase {
630   using EntryMap = llvm::StringMap<SmallVector<CompileCommand, 1>>;
631   EntryMap Entries;
632   MemCDB(const EntryMap &E) : Entries(E) {}
633 
634   std::vector<CompileCommand> getCompileCommands(StringRef F) const override {
635     auto Ret = Entries.lookup(F);
636     return {Ret.begin(), Ret.end()};
637   }
638 
639   std::vector<std::string> getAllFiles() const override {
640     std::vector<std::string> Result;
641     for (const auto &Entry : Entries)
642       Result.push_back(Entry.first());
643     return Result;
644   }
645 };
646 
647 class InterpolateTest : public ::testing::Test {
648 protected:
649   // Adds an entry to the underlying compilation database.
650   // A flag is injected: -D <File>, so the command used can be identified.
651   void add(llvm::StringRef File, llvm::StringRef Flags = "") {
652     llvm::SmallVector<StringRef, 8> Argv = {"clang", File, "-D", File};
653     llvm::SplitString(Flags, Argv);
654     llvm::SmallString<32> Dir;
655     llvm::sys::path::system_temp_directory(false, Dir);
656     Entries[path(File)].push_back(
657         {Dir, path(File), {Argv.begin(), Argv.end()}, "foo.o"});
658   }
659 
660   // Turn a unix path fragment (foo/bar.h) into a native path (C:\tmp\foo\bar.h)
661   std::string path(llvm::SmallString<32> File) {
662     llvm::SmallString<32> Dir;
663     llvm::sys::path::system_temp_directory(false, Dir);
664     llvm::sys::path::native(File);
665     llvm::SmallString<64> Result;
666     llvm::sys::path::append(Result, Dir, File);
667     return Result.str();
668   }
669 
670   // Look up the command from a relative path, and return it in string form.
671   // The input file is not included in the returned command.
672   std::string getCommand(llvm::StringRef F) {
673     auto Results =
674         inferMissingCompileCommands(llvm::make_unique<MemCDB>(Entries))
675             ->getCompileCommands(path(F));
676     if (Results.empty())
677       return "none";
678     // drop the input file argument, so tests don't have to deal with path().
679     EXPECT_EQ(Results[0].CommandLine.back(), path(F))
680         << "Last arg should be the file";
681     Results[0].CommandLine.pop_back();
682     return llvm::join(Results[0].CommandLine, " ");
683   }
684 
685   MemCDB::EntryMap Entries;
686 };
687 
688 TEST_F(InterpolateTest, Nearby) {
689   add("dir/foo.cpp");
690   add("dir/bar.cpp");
691   add("an/other/foo.cpp");
692 
693   // great: dir and name both match (prefix or full, case insensitive)
694   EXPECT_EQ(getCommand("dir/f.cpp"), "clang -D dir/foo.cpp");
695   EXPECT_EQ(getCommand("dir/FOO.cpp"), "clang -D dir/foo.cpp");
696   // no name match. prefer matching dir, break ties by alpha
697   EXPECT_EQ(getCommand("dir/a.cpp"), "clang -D dir/bar.cpp");
698   // an exact name match beats one segment of directory match
699   EXPECT_EQ(getCommand("some/other/bar.h"),
700             "clang -D dir/bar.cpp -x c++-header");
701   // two segments of directory match beat a prefix name match
702   EXPECT_EQ(getCommand("an/other/b.cpp"), "clang -D an/other/foo.cpp");
703   // if nothing matches at all, we still get the closest alpha match
704   EXPECT_EQ(getCommand("below/some/obscure/path.cpp"),
705             "clang -D an/other/foo.cpp");
706 }
707 
708 TEST_F(InterpolateTest, Language) {
709   add("dir/foo.cpp", "-std=c++17");
710   add("dir/bar.c", "");
711   add("dir/baz.cee", "-x c");
712 
713   // .h is ambiguous, so we add explicit language flags
714   EXPECT_EQ(getCommand("foo.h"),
715             "clang -D dir/foo.cpp -x c++-header -std=c++17");
716   // and don't add -x if the inferred language is correct.
717   EXPECT_EQ(getCommand("foo.hpp"), "clang -D dir/foo.cpp -std=c++17");
718   // respect -x if it's already there.
719   EXPECT_EQ(getCommand("baz.h"), "clang -D dir/baz.cee -x c-header");
720   // prefer a worse match with the right extension.
721   EXPECT_EQ(getCommand("foo.c"), "clang -D dir/bar.c");
722   // make sure we don't crash on queries with invalid extensions.
723   EXPECT_EQ(getCommand("foo.cce"), "clang -D dir/foo.cpp");
724   Entries.erase(path(StringRef("dir/bar.c")));
725   // Now we transfer across languages, so drop -std too.
726   EXPECT_EQ(getCommand("foo.c"), "clang -D dir/foo.cpp");
727 }
728 
729 TEST_F(InterpolateTest, Strip) {
730   add("dir/foo.cpp", "-o foo.o -Wall");
731   // the -o option and the input file are removed, but -Wall is preserved.
732   EXPECT_EQ(getCommand("dir/bar.cpp"), "clang -D dir/foo.cpp -Wall");
733 }
734 
735 TEST_F(InterpolateTest, Case) {
736   add("FOO/BAR/BAZ/SHOUT.cc");
737   add("foo/bar/baz/quiet.cc");
738   // Case mismatches are completely ignored, so we choose the name match.
739   EXPECT_EQ(getCommand("foo/bar/baz/shout.C"), "clang -D FOO/BAR/BAZ/SHOUT.cc");
740 }
741 
742 TEST(CompileCommandTest, EqualityOperator) {
743   CompileCommand CCRef("/foo/bar", "hello.c", {"a", "b"}, "hello.o");
744   CompileCommand CCTest = CCRef;
745 
746   EXPECT_TRUE(CCRef == CCTest);
747   EXPECT_FALSE(CCRef != CCTest);
748 
749   CCTest = CCRef;
750   CCTest.Directory = "/foo/baz";
751   EXPECT_FALSE(CCRef == CCTest);
752   EXPECT_TRUE(CCRef != CCTest);
753 
754   CCTest = CCRef;
755   CCTest.Filename = "bonjour.c";
756   EXPECT_FALSE(CCRef == CCTest);
757   EXPECT_TRUE(CCRef != CCTest);
758 
759   CCTest = CCRef;
760   CCTest.CommandLine.push_back("c");
761   EXPECT_FALSE(CCRef == CCTest);
762   EXPECT_TRUE(CCRef != CCTest);
763 
764   CCTest = CCRef;
765   CCTest.Output = "bonjour.o";
766   EXPECT_FALSE(CCRef == CCTest);
767   EXPECT_TRUE(CCRef != CCTest);
768 }
769 
770 } // end namespace tooling
771 } // end namespace clang
772