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
18namespace clang {
19namespace tooling {
20
21static 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
35TEST(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
43TEST(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
71TEST(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
87TEST(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
101TEST(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
122static 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
133TEST(unescapeJsonCommandLine, ReturnsEmptyArrayOnEmptyString) {
134  std::vector<std::string> Result = unescapeJsonCommandLine("");
135  EXPECT_TRUE(Result.empty());
136}
137
138TEST(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
146TEST(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
153TEST(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
162TEST(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
168TEST(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
176TEST(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
183TEST(unescapeJsonCommandLine, ParsesEscapedQuotesInQuotedStrings) {
184  std::vector<std::string> Result = unescapeJsonCommandLine(
185      "\\\"\\\\\\\"\\\"");
186  ASSERT_EQ(1ul, Result.size());
187  EXPECT_EQ("\"", Result[0]);
188}
189
190TEST(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
200TEST(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
212TEST(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
222TEST(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
238TEST(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
246TEST(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
255TEST(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
274TEST(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