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 }
48 
49 static std::vector<std::string> getAllFiles(StringRef JSONDatabase,
50                                             std::string &ErrorMessage,
51                                             JSONCommandLineSyntax Syntax) {
52   std::unique_ptr<CompilationDatabase> Database(
53       JSONCompilationDatabase::loadFromBuffer(JSONDatabase, ErrorMessage,
54                                               Syntax));
55   if (!Database) {
56     ADD_FAILURE() << ErrorMessage;
57     return std::vector<std::string>();
58   }
59   return Database->getAllFiles();
60 }
61 
62 static std::vector<CompileCommand>
63 getAllCompileCommands(JSONCommandLineSyntax Syntax, StringRef JSONDatabase,
64                       std::string &ErrorMessage) {
65   std::unique_ptr<CompilationDatabase> Database(
66       JSONCompilationDatabase::loadFromBuffer(JSONDatabase, ErrorMessage,
67                                               Syntax));
68   if (!Database) {
69     ADD_FAILURE() << ErrorMessage;
70     return std::vector<CompileCommand>();
71   }
72   return Database->getAllCompileCommands();
73 }
74 
75 TEST(JSONCompilationDatabase, GetAllFiles) {
76   std::string ErrorMessage;
77   EXPECT_EQ(std::vector<std::string>(),
78             getAllFiles("[]", ErrorMessage, JSONCommandLineSyntax::Gnu))
79       << ErrorMessage;
80 
81   std::vector<std::string> expected_files;
82   SmallString<16> PathStorage;
83   llvm::sys::path::native("//net/dir/file1", PathStorage);
84   expected_files.push_back(PathStorage.str());
85   llvm::sys::path::native("//net/dir/file2", PathStorage);
86   expected_files.push_back(PathStorage.str());
87   EXPECT_EQ(expected_files,
88             getAllFiles("[{\"directory\":\"//net/dir\","
89                         "\"command\":\"command\","
90                         "\"file\":\"file1\"},"
91                         " {\"directory\":\"//net/dir\","
92                         "\"command\":\"command\","
93                         "\"file\":\"file2\"}]",
94                         ErrorMessage, JSONCommandLineSyntax::Gnu))
95       << ErrorMessage;
96 }
97 
98 TEST(JSONCompilationDatabase, GetAllCompileCommands) {
99   std::string ErrorMessage;
100   EXPECT_EQ(
101       0u, getAllCompileCommands(JSONCommandLineSyntax::Gnu, "[]", ErrorMessage)
102               .size())
103       << ErrorMessage;
104 
105   StringRef Directory1("//net/dir1");
106   StringRef FileName1("file1");
107   StringRef Command1("command1");
108   StringRef Directory2("//net/dir2");
109   StringRef FileName2("file2");
110   StringRef Command2("command2");
111 
112   std::vector<CompileCommand> Commands = getAllCompileCommands(
113       JSONCommandLineSyntax::Gnu,
114       ("[{\"directory\":\"" + Directory1 + "\"," + "\"command\":\"" + Command1 +
115        "\","
116        "\"file\":\"" +
117        FileName1 + "\"},"
118                    " {\"directory\":\"" +
119        Directory2 + "\"," + "\"command\":\"" + Command2 + "\","
120                                                           "\"file\":\"" +
121        FileName2 + "\"}]")
122           .str(),
123       ErrorMessage);
124   EXPECT_EQ(2U, Commands.size()) << ErrorMessage;
125   EXPECT_EQ(Directory1, Commands[0].Directory) << ErrorMessage;
126   EXPECT_EQ(FileName1, Commands[0].Filename) << ErrorMessage;
127   ASSERT_EQ(1u, Commands[0].CommandLine.size());
128   EXPECT_EQ(Command1, Commands[0].CommandLine[0]) << ErrorMessage;
129   EXPECT_EQ(Directory2, Commands[1].Directory) << ErrorMessage;
130   EXPECT_EQ(FileName2, Commands[1].Filename) << ErrorMessage;
131   ASSERT_EQ(1u, Commands[1].CommandLine.size());
132   EXPECT_EQ(Command2, Commands[1].CommandLine[0]) << ErrorMessage;
133 
134   // Check that order is preserved.
135   Commands = getAllCompileCommands(
136       JSONCommandLineSyntax::Gnu,
137       ("[{\"directory\":\"" + Directory2 + "\"," + "\"command\":\"" + Command2 +
138        "\","
139        "\"file\":\"" +
140        FileName2 + "\"},"
141                    " {\"directory\":\"" +
142        Directory1 + "\"," + "\"command\":\"" + Command1 + "\","
143                                                           "\"file\":\"" +
144        FileName1 + "\"}]")
145           .str(),
146       ErrorMessage);
147   EXPECT_EQ(2U, Commands.size()) << ErrorMessage;
148   EXPECT_EQ(Directory2, Commands[0].Directory) << ErrorMessage;
149   EXPECT_EQ(FileName2, Commands[0].Filename) << ErrorMessage;
150   ASSERT_EQ(1u, Commands[0].CommandLine.size());
151   EXPECT_EQ(Command2, Commands[0].CommandLine[0]) << ErrorMessage;
152   EXPECT_EQ(Directory1, Commands[1].Directory) << ErrorMessage;
153   EXPECT_EQ(FileName1, Commands[1].Filename) << ErrorMessage;
154   ASSERT_EQ(1u, Commands[1].CommandLine.size());
155   EXPECT_EQ(Command1, Commands[1].CommandLine[0]) << ErrorMessage;
156 }
157 
158 static CompileCommand findCompileArgsInJsonDatabase(StringRef FileName,
159                                                     StringRef JSONDatabase,
160                                                     std::string &ErrorMessage) {
161   std::unique_ptr<CompilationDatabase> Database(
162       JSONCompilationDatabase::loadFromBuffer(JSONDatabase, ErrorMessage,
163                                               JSONCommandLineSyntax::Gnu));
164   if (!Database)
165     return CompileCommand();
166   std::vector<CompileCommand> Commands = Database->getCompileCommands(FileName);
167   EXPECT_LE(Commands.size(), 1u);
168   if (Commands.empty())
169     return CompileCommand();
170   return Commands[0];
171 }
172 
173 TEST(JSONCompilationDatabase, ArgumentsPreferredOverCommand) {
174    StringRef Directory("//net/dir");
175    StringRef FileName("//net/dir/filename");
176    StringRef Command("command");
177    StringRef Arguments = "arguments";
178    Twine ArgumentsAccumulate;
179    std::string ErrorMessage;
180    CompileCommand FoundCommand = findCompileArgsInJsonDatabase(
181       FileName,
182       ("[{\"directory\":\"" + Directory + "\","
183          "\"arguments\":[\"" + Arguments + "\"],"
184          "\"command\":\"" + Command + "\","
185          "\"file\":\"" + FileName + "\"}]").str(),
186       ErrorMessage);
187    EXPECT_EQ(Directory, FoundCommand.Directory) << ErrorMessage;
188    EXPECT_EQ(1u, FoundCommand.CommandLine.size()) << ErrorMessage;
189    EXPECT_EQ(Arguments, FoundCommand.CommandLine[0]) << ErrorMessage;
190 }
191 
192 struct FakeComparator : public PathComparator {
193   ~FakeComparator() override {}
194   bool equivalent(StringRef FileA, StringRef FileB) const override {
195     return FileA.equals_lower(FileB);
196   }
197 };
198 
199 class FileMatchTrieTest : public ::testing::Test {
200 protected:
201   FileMatchTrieTest() : Trie(new FakeComparator()) {}
202 
203   StringRef find(StringRef Path) {
204     llvm::raw_string_ostream ES(Error);
205     return Trie.findEquivalent(Path, ES);
206   }
207 
208   FileMatchTrie Trie;
209   std::string Error;
210 };
211 
212 TEST_F(FileMatchTrieTest, InsertingRelativePath) {
213   Trie.insert("//net/path/file.cc");
214   Trie.insert("file.cc");
215   EXPECT_EQ("//net/path/file.cc", find("//net/path/file.cc"));
216 }
217 
218 TEST_F(FileMatchTrieTest, MatchingRelativePath) {
219   EXPECT_EQ("", find("file.cc"));
220 }
221 
222 TEST_F(FileMatchTrieTest, ReturnsBestResults) {
223   Trie.insert("//net/d/c/b.cc");
224   Trie.insert("//net/d/b/b.cc");
225   EXPECT_EQ("//net/d/b/b.cc", find("//net/d/b/b.cc"));
226 }
227 
228 TEST_F(FileMatchTrieTest, HandlesSymlinks) {
229   Trie.insert("//net/AA/file.cc");
230   EXPECT_EQ("//net/AA/file.cc", find("//net/aa/file.cc"));
231 }
232 
233 TEST_F(FileMatchTrieTest, ReportsSymlinkAmbiguity) {
234   Trie.insert("//net/Aa/file.cc");
235   Trie.insert("//net/aA/file.cc");
236   EXPECT_TRUE(find("//net/aa/file.cc").empty());
237   EXPECT_EQ("Path is ambiguous", Error);
238 }
239 
240 TEST_F(FileMatchTrieTest, LongerMatchingSuffixPreferred) {
241   Trie.insert("//net/src/Aa/file.cc");
242   Trie.insert("//net/src/aA/file.cc");
243   Trie.insert("//net/SRC/aa/file.cc");
244   EXPECT_EQ("//net/SRC/aa/file.cc", find("//net/src/aa/file.cc"));
245 }
246 
247 TEST_F(FileMatchTrieTest, EmptyTrie) {
248   EXPECT_TRUE(find("//net/some/path").empty());
249 }
250 
251 TEST_F(FileMatchTrieTest, NoResult) {
252   Trie.insert("//net/somepath/otherfile.cc");
253   Trie.insert("//net/otherpath/somefile.cc");
254   EXPECT_EQ("", find("//net/somepath/somefile.cc"));
255 }
256 
257 TEST_F(FileMatchTrieTest, RootElementDifferent) {
258   Trie.insert("//net/path/file.cc");
259   Trie.insert("//net/otherpath/file.cc");
260   EXPECT_EQ("//net/path/file.cc", find("//net/path/file.cc"));
261 }
262 
263 TEST_F(FileMatchTrieTest, CannotResolveRelativePath) {
264   EXPECT_EQ("", find("relative-path.cc"));
265   EXPECT_EQ("Cannot resolve relative paths", Error);
266 }
267 
268 TEST(findCompileArgsInJsonDatabase, FindsNothingIfEmpty) {
269   std::string ErrorMessage;
270   CompileCommand NotFound = findCompileArgsInJsonDatabase(
271     "a-file.cpp", "", ErrorMessage);
272   EXPECT_TRUE(NotFound.CommandLine.empty()) << ErrorMessage;
273   EXPECT_TRUE(NotFound.Directory.empty()) << ErrorMessage;
274 }
275 
276 TEST(findCompileArgsInJsonDatabase, ReadsSingleEntry) {
277   StringRef Directory("//net/some/directory");
278   StringRef FileName("//net/path/to/a-file.cpp");
279   StringRef Command("//net/path/to/compiler and some arguments");
280   std::string ErrorMessage;
281   CompileCommand FoundCommand = findCompileArgsInJsonDatabase(
282     FileName,
283     ("[{\"directory\":\"" + Directory + "\"," +
284        "\"command\":\"" + Command + "\","
285        "\"file\":\"" + FileName + "\"}]").str(),
286     ErrorMessage);
287   EXPECT_EQ(Directory, FoundCommand.Directory) << ErrorMessage;
288   ASSERT_EQ(4u, FoundCommand.CommandLine.size()) << ErrorMessage;
289   EXPECT_EQ("//net/path/to/compiler",
290             FoundCommand.CommandLine[0]) << ErrorMessage;
291   EXPECT_EQ("and", FoundCommand.CommandLine[1]) << ErrorMessage;
292   EXPECT_EQ("some", FoundCommand.CommandLine[2]) << ErrorMessage;
293   EXPECT_EQ("arguments", FoundCommand.CommandLine[3]) << ErrorMessage;
294 
295   CompileCommand NotFound = findCompileArgsInJsonDatabase(
296     "a-file.cpp",
297     ("[{\"directory\":\"" + Directory + "\"," +
298        "\"command\":\"" + Command + "\","
299        "\"file\":\"" + FileName + "\"}]").str(),
300     ErrorMessage);
301   EXPECT_TRUE(NotFound.Directory.empty()) << ErrorMessage;
302   EXPECT_TRUE(NotFound.CommandLine.empty()) << ErrorMessage;
303 }
304 
305 TEST(findCompileArgsInJsonDatabase, ReadsCompileCommandLinesWithSpaces) {
306   StringRef Directory("//net/some/directory");
307   StringRef FileName("//net/path/to/a-file.cpp");
308   StringRef Command("\\\"//net/path to compiler\\\" \\\"and an argument\\\"");
309   std::string ErrorMessage;
310   CompileCommand FoundCommand = findCompileArgsInJsonDatabase(
311     FileName,
312     ("[{\"directory\":\"" + Directory + "\"," +
313        "\"command\":\"" + Command + "\","
314        "\"file\":\"" + FileName + "\"}]").str(),
315     ErrorMessage);
316   ASSERT_EQ(2u, FoundCommand.CommandLine.size());
317   EXPECT_EQ("//net/path to compiler",
318             FoundCommand.CommandLine[0]) << ErrorMessage;
319   EXPECT_EQ("and an argument", FoundCommand.CommandLine[1]) << ErrorMessage;
320 }
321 
322 TEST(findCompileArgsInJsonDatabase, ReadsDirectoryWithSpaces) {
323   StringRef Directory("//net/some directory / with spaces");
324   StringRef FileName("//net/path/to/a-file.cpp");
325   StringRef Command("a command");
326   std::string ErrorMessage;
327   CompileCommand FoundCommand = findCompileArgsInJsonDatabase(
328     FileName,
329     ("[{\"directory\":\"" + Directory + "\"," +
330        "\"command\":\"" + Command + "\","
331        "\"file\":\"" + FileName + "\"}]").str(),
332     ErrorMessage);
333   EXPECT_EQ(Directory, FoundCommand.Directory) << ErrorMessage;
334 }
335 
336 TEST(findCompileArgsInJsonDatabase, FindsEntry) {
337   StringRef Directory("//net/directory");
338   StringRef FileName("file");
339   StringRef Command("command");
340   std::string JsonDatabase = "[";
341   for (int I = 0; I < 10; ++I) {
342     if (I > 0) JsonDatabase += ",";
343     JsonDatabase +=
344       ("{\"directory\":\"" + Directory + Twine(I) + "\"," +
345         "\"command\":\"" + Command + Twine(I) + "\","
346         "\"file\":\"" + FileName + Twine(I) + "\"}").str();
347   }
348   JsonDatabase += "]";
349   std::string ErrorMessage;
350   CompileCommand FoundCommand = findCompileArgsInJsonDatabase(
351     "//net/directory4/file4", JsonDatabase, ErrorMessage);
352   EXPECT_EQ("//net/directory4", FoundCommand.Directory) << ErrorMessage;
353   ASSERT_EQ(1u, FoundCommand.CommandLine.size()) << ErrorMessage;
354   EXPECT_EQ("command4", FoundCommand.CommandLine[0]) << ErrorMessage;
355 }
356 
357 static std::vector<std::string> unescapeJsonCommandLine(StringRef Command) {
358   std::string JsonDatabase =
359     ("[{\"directory\":\"//net/root\", \"file\":\"test\", \"command\": \"" +
360      Command + "\"}]").str();
361   std::string ErrorMessage;
362   CompileCommand FoundCommand = findCompileArgsInJsonDatabase(
363     "//net/root/test", JsonDatabase, ErrorMessage);
364   EXPECT_TRUE(ErrorMessage.empty()) << ErrorMessage;
365   return FoundCommand.CommandLine;
366 }
367 
368 TEST(unescapeJsonCommandLine, ReturnsEmptyArrayOnEmptyString) {
369   std::vector<std::string> Result = unescapeJsonCommandLine("");
370   EXPECT_TRUE(Result.empty());
371 }
372 
373 TEST(unescapeJsonCommandLine, SplitsOnSpaces) {
374   std::vector<std::string> Result = unescapeJsonCommandLine("a b c");
375   ASSERT_EQ(3ul, Result.size());
376   EXPECT_EQ("a", Result[0]);
377   EXPECT_EQ("b", Result[1]);
378   EXPECT_EQ("c", Result[2]);
379 }
380 
381 TEST(unescapeJsonCommandLine, MungesMultipleSpaces) {
382   std::vector<std::string> Result = unescapeJsonCommandLine("   a   b   ");
383   ASSERT_EQ(2ul, Result.size());
384   EXPECT_EQ("a", Result[0]);
385   EXPECT_EQ("b", Result[1]);
386 }
387 
388 TEST(unescapeJsonCommandLine, UnescapesBackslashCharacters) {
389   std::vector<std::string> Backslash = unescapeJsonCommandLine("a\\\\\\\\");
390   ASSERT_EQ(1ul, Backslash.size());
391   EXPECT_EQ("a\\", Backslash[0]);
392   std::vector<std::string> Quote = unescapeJsonCommandLine("a\\\\\\\"");
393   ASSERT_EQ(1ul, Quote.size());
394   EXPECT_EQ("a\"", Quote[0]);
395 }
396 
397 TEST(unescapeJsonCommandLine, DoesNotMungeSpacesBetweenQuotes) {
398   std::vector<std::string> Result = unescapeJsonCommandLine("\\\"  a  b  \\\"");
399   ASSERT_EQ(1ul, Result.size());
400   EXPECT_EQ("  a  b  ", Result[0]);
401 }
402 
403 TEST(unescapeJsonCommandLine, AllowsMultipleQuotedArguments) {
404   std::vector<std::string> Result = unescapeJsonCommandLine(
405       "  \\\" a \\\"  \\\" b \\\"  ");
406   ASSERT_EQ(2ul, Result.size());
407   EXPECT_EQ(" a ", Result[0]);
408   EXPECT_EQ(" b ", Result[1]);
409 }
410 
411 TEST(unescapeJsonCommandLine, AllowsEmptyArgumentsInQuotes) {
412   std::vector<std::string> Result = unescapeJsonCommandLine(
413       "\\\"\\\"\\\"\\\"");
414   ASSERT_EQ(1ul, Result.size());
415   EXPECT_TRUE(Result[0].empty()) << Result[0];
416 }
417 
418 TEST(unescapeJsonCommandLine, ParsesEscapedQuotesInQuotedStrings) {
419   std::vector<std::string> Result = unescapeJsonCommandLine(
420       "\\\"\\\\\\\"\\\"");
421   ASSERT_EQ(1ul, Result.size());
422   EXPECT_EQ("\"", Result[0]);
423 }
424 
425 TEST(unescapeJsonCommandLine, ParsesMultipleArgumentsWithEscapedCharacters) {
426   std::vector<std::string> Result = unescapeJsonCommandLine(
427       "  \\\\\\\"  \\\"a \\\\\\\" b \\\"     \\\"and\\\\\\\\c\\\"   \\\\\\\"");
428   ASSERT_EQ(4ul, Result.size());
429   EXPECT_EQ("\"", Result[0]);
430   EXPECT_EQ("a \" b ", Result[1]);
431   EXPECT_EQ("and\\c", Result[2]);
432   EXPECT_EQ("\"", Result[3]);
433 }
434 
435 TEST(unescapeJsonCommandLine, ParsesStringsWithoutSpacesIntoSingleArgument) {
436   std::vector<std::string> QuotedNoSpaces = unescapeJsonCommandLine(
437       "\\\"a\\\"\\\"b\\\"");
438   ASSERT_EQ(1ul, QuotedNoSpaces.size());
439   EXPECT_EQ("ab", QuotedNoSpaces[0]);
440 
441   std::vector<std::string> MixedNoSpaces = unescapeJsonCommandLine(
442       "\\\"a\\\"bcd\\\"ef\\\"\\\"\\\"\\\"g\\\"");
443   ASSERT_EQ(1ul, MixedNoSpaces.size());
444   EXPECT_EQ("abcdefg", MixedNoSpaces[0]);
445 }
446 
447 TEST(unescapeJsonCommandLine, ParsesQuotedStringWithoutClosingQuote) {
448   std::vector<std::string> Unclosed = unescapeJsonCommandLine("\\\"abc");
449   ASSERT_EQ(1ul, Unclosed.size());
450   EXPECT_EQ("abc", Unclosed[0]);
451 
452   std::vector<std::string> Empty = unescapeJsonCommandLine("\\\"");
453   ASSERT_EQ(1ul, Empty.size());
454   EXPECT_EQ("", Empty[0]);
455 }
456 
457 TEST(unescapeJsonCommandLine, ParsesSingleQuotedString) {
458   std::vector<std::string> Args = unescapeJsonCommandLine("a'\\\\b \\\"c\\\"'");
459   ASSERT_EQ(1ul, Args.size());
460   EXPECT_EQ("a\\b \"c\"", Args[0]);
461 }
462 
463 TEST(FixedCompilationDatabase, ReturnsFixedCommandLine) {
464   std::vector<std::string> CommandLine;
465   CommandLine.push_back("one");
466   CommandLine.push_back("two");
467   FixedCompilationDatabase Database(".", CommandLine);
468   StringRef FileName("source");
469   std::vector<CompileCommand> Result =
470     Database.getCompileCommands(FileName);
471   ASSERT_EQ(1ul, Result.size());
472   std::vector<std::string> ExpectedCommandLine(1, "clang-tool");
473   ExpectedCommandLine.insert(ExpectedCommandLine.end(),
474                              CommandLine.begin(), CommandLine.end());
475   ExpectedCommandLine.push_back("source");
476   EXPECT_EQ(".", Result[0].Directory);
477   EXPECT_EQ(FileName, Result[0].Filename);
478   EXPECT_EQ(ExpectedCommandLine, Result[0].CommandLine);
479 }
480 
481 TEST(FixedCompilationDatabase, GetAllFiles) {
482   std::vector<std::string> CommandLine;
483   CommandLine.push_back("one");
484   CommandLine.push_back("two");
485   FixedCompilationDatabase Database(".", CommandLine);
486 
487   EXPECT_EQ(0ul, Database.getAllFiles().size());
488 }
489 
490 TEST(FixedCompilationDatabase, GetAllCompileCommands) {
491   std::vector<std::string> CommandLine;
492   CommandLine.push_back("one");
493   CommandLine.push_back("two");
494   FixedCompilationDatabase Database(".", CommandLine);
495 
496   EXPECT_EQ(0ul, Database.getAllCompileCommands().size());
497 }
498 
499 TEST(ParseFixedCompilationDatabase, ReturnsNullOnEmptyArgumentList) {
500   int Argc = 0;
501   std::unique_ptr<FixedCompilationDatabase> Database(
502       FixedCompilationDatabase::loadFromCommandLine(Argc, nullptr));
503   EXPECT_FALSE(Database);
504   EXPECT_EQ(0, Argc);
505 }
506 
507 TEST(ParseFixedCompilationDatabase, ReturnsNullWithoutDoubleDash) {
508   int Argc = 2;
509   const char *Argv[] = { "1", "2" };
510   std::unique_ptr<FixedCompilationDatabase> Database(
511       FixedCompilationDatabase::loadFromCommandLine(Argc, Argv));
512   EXPECT_FALSE(Database);
513   EXPECT_EQ(2, Argc);
514 }
515 
516 TEST(ParseFixedCompilationDatabase, ReturnsArgumentsAfterDoubleDash) {
517   int Argc = 5;
518   const char *Argv[] = {
519     "1", "2", "--\0no-constant-folding", "-DDEF3", "-DDEF4"
520   };
521   std::unique_ptr<FixedCompilationDatabase> Database(
522       FixedCompilationDatabase::loadFromCommandLine(Argc, Argv));
523   ASSERT_TRUE((bool)Database);
524   std::vector<CompileCommand> Result =
525     Database->getCompileCommands("source");
526   ASSERT_EQ(1ul, Result.size());
527   ASSERT_EQ(".", Result[0].Directory);
528   std::vector<std::string> CommandLine;
529   CommandLine.push_back("clang-tool");
530   CommandLine.push_back("-DDEF3");
531   CommandLine.push_back("-DDEF4");
532   CommandLine.push_back("source");
533   ASSERT_EQ(CommandLine, Result[0].CommandLine);
534   EXPECT_EQ(2, Argc);
535 }
536 
537 TEST(ParseFixedCompilationDatabase, ReturnsEmptyCommandLine) {
538   int Argc = 3;
539   const char *Argv[] = { "1", "2", "--\0no-constant-folding" };
540   std::unique_ptr<FixedCompilationDatabase> Database(
541       FixedCompilationDatabase::loadFromCommandLine(Argc, Argv));
542   ASSERT_TRUE((bool)Database);
543   std::vector<CompileCommand> Result =
544     Database->getCompileCommands("source");
545   ASSERT_EQ(1ul, Result.size());
546   ASSERT_EQ(".", Result[0].Directory);
547   std::vector<std::string> CommandLine;
548   CommandLine.push_back("clang-tool");
549   CommandLine.push_back("source");
550   ASSERT_EQ(CommandLine, Result[0].CommandLine);
551   EXPECT_EQ(2, Argc);
552 }
553 
554 TEST(ParseFixedCompilationDatabase, HandlesPositionalArgs) {
555   const char *Argv[] = {"1", "2", "--", "-c", "somefile.cpp", "-DDEF3"};
556   int Argc = sizeof(Argv) / sizeof(char*);
557   std::unique_ptr<FixedCompilationDatabase> Database(
558       FixedCompilationDatabase::loadFromCommandLine(Argc, Argv));
559   ASSERT_TRUE((bool)Database);
560   std::vector<CompileCommand> Result =
561     Database->getCompileCommands("source");
562   ASSERT_EQ(1ul, Result.size());
563   ASSERT_EQ(".", Result[0].Directory);
564   std::vector<std::string> Expected;
565   Expected.push_back("clang-tool");
566   Expected.push_back("-c");
567   Expected.push_back("-DDEF3");
568   Expected.push_back("source");
569   ASSERT_EQ(Expected, Result[0].CommandLine);
570   EXPECT_EQ(2, Argc);
571 }
572 
573 TEST(ParseFixedCompilationDatabase, HandlesArgv0) {
574   const char *Argv[] = {"1", "2", "--", "mytool", "somefile.cpp"};
575   int Argc = sizeof(Argv) / sizeof(char*);
576   std::unique_ptr<FixedCompilationDatabase> Database(
577       FixedCompilationDatabase::loadFromCommandLine(Argc, Argv));
578   ASSERT_TRUE((bool)Database);
579   std::vector<CompileCommand> Result =
580     Database->getCompileCommands("source");
581   ASSERT_EQ(1ul, Result.size());
582   ASSERT_EQ(".", Result[0].Directory);
583   std::vector<std::string> Expected;
584   Expected.push_back("clang-tool");
585   Expected.push_back("source");
586   ASSERT_EQ(Expected, Result[0].CommandLine);
587   EXPECT_EQ(2, Argc);
588 }
589 
590 } // end namespace tooling
591 } // end namespace clang
592