1 //===- unittest/Tooling/CompilationDatabaseTest.cpp -----------------------===//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
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"
21 static void expectFailure(StringRef JSONDatabase, StringRef Explanation) {
22 std::string ErrorMessage;
23 EXPECT_EQ(NULL, JSONCompilationDatabase::loadFromBuffer(JSONDatabase,
25 << "Expected an error because of: " << Explanation;
28 TEST(JSONCompilationDatabase, ErrsOnInvalidFormat) {
29 expectFailure("", "Empty database");
30 expectFailure("{", "Invalid JSON");
31 expectFailure("[[]]", "Array instead of object");
32 expectFailure("[{\"a\":[]}]", "Array instead of value");
33 expectFailure("[{\"a\":\"b\"}]", "Unknown key");
34 expectFailure("[{[]:\"\"}]", "Incorrectly typed entry");
35 expectFailure("[{}]", "Empty entry");
36 expectFailure("[{\"directory\":\"\",\"command\":\"\"}]", "Missing file");
37 expectFailure("[{\"directory\":\"\",\"file\":\"\"}]", "Missing command");
38 expectFailure("[{\"command\":\"\",\"file\":\"\"}]", "Missing directory");
41 static std::vector<std::string> getAllFiles(StringRef JSONDatabase,
42 std::string &ErrorMessage) {
43 llvm::OwningPtr<CompilationDatabase> Database(
44 JSONCompilationDatabase::loadFromBuffer(JSONDatabase, ErrorMessage));
46 ADD_FAILURE() << ErrorMessage;
47 return std::vector<std::string>();
49 return Database->getAllFiles();
52 TEST(JSONCompilationDatabase, GetAllFiles) {
53 std::string ErrorMessage;
54 EXPECT_EQ(std::vector<std::string>(),
55 getAllFiles("[]", ErrorMessage)) << ErrorMessage;
57 std::vector<std::string> expected_files;
58 expected_files.push_back("file1");
59 expected_files.push_back("file2");
60 EXPECT_EQ(expected_files, getAllFiles(
61 "[{\"directory\":\"dir\","
62 "\"command\":\"command\","
63 "\"file\":\"file1\"},"
64 " {\"directory\":\"dir\","
65 "\"command\":\"command\","
66 "\"file\":\"file2\"}]",
67 ErrorMessage)) << ErrorMessage;
70 static CompileCommand findCompileArgsInJsonDatabase(StringRef FileName,
71 StringRef JSONDatabase,
72 std::string &ErrorMessage) {
73 llvm::OwningPtr<CompilationDatabase> Database(
74 JSONCompilationDatabase::loadFromBuffer(JSONDatabase, ErrorMessage));
76 return CompileCommand();
77 std::vector<CompileCommand> Commands = Database->getCompileCommands(FileName);
78 EXPECT_LE(Commands.size(), 1u);
80 return CompileCommand();
84 TEST(findCompileArgsInJsonDatabase, FindsNothingIfEmpty) {
85 std::string ErrorMessage;
86 CompileCommand NotFound = findCompileArgsInJsonDatabase(
87 "a-file.cpp", "", ErrorMessage);
88 EXPECT_TRUE(NotFound.CommandLine.empty()) << ErrorMessage;
89 EXPECT_TRUE(NotFound.Directory.empty()) << ErrorMessage;
92 TEST(findCompileArgsInJsonDatabase, ReadsSingleEntry) {
93 StringRef Directory("/some/directory");
94 StringRef FileName("/path/to/a-file.cpp");
95 StringRef Command("/path/to/compiler and some arguments");
96 std::string ErrorMessage;
97 CompileCommand FoundCommand = findCompileArgsInJsonDatabase(
99 ("[{\"directory\":\"" + Directory + "\"," +
100 "\"command\":\"" + Command + "\","
101 "\"file\":\"" + FileName + "\"}]").str(),
103 EXPECT_EQ(Directory, FoundCommand.Directory) << ErrorMessage;
104 ASSERT_EQ(4u, FoundCommand.CommandLine.size()) << ErrorMessage;
105 EXPECT_EQ("/path/to/compiler", FoundCommand.CommandLine[0]) << ErrorMessage;
106 EXPECT_EQ("and", FoundCommand.CommandLine[1]) << ErrorMessage;
107 EXPECT_EQ("some", FoundCommand.CommandLine[2]) << ErrorMessage;
108 EXPECT_EQ("arguments", FoundCommand.CommandLine[3]) << ErrorMessage;
110 CompileCommand NotFound = findCompileArgsInJsonDatabase(
112 ("[{\"directory\":\"" + Directory + "\"," +
113 "\"command\":\"" + Command + "\","
114 "\"file\":\"" + FileName + "\"}]").str(),
116 EXPECT_TRUE(NotFound.Directory.empty()) << ErrorMessage;
117 EXPECT_TRUE(NotFound.CommandLine.empty()) << ErrorMessage;
120 TEST(findCompileArgsInJsonDatabase, ReadsCompileCommandLinesWithSpaces) {
121 StringRef Directory("/some/directory");
122 StringRef FileName("/path/to/a-file.cpp");
123 StringRef Command("\\\"/path to compiler\\\" \\\"and an argument\\\"");
124 std::string ErrorMessage;
125 CompileCommand FoundCommand = findCompileArgsInJsonDatabase(
127 ("[{\"directory\":\"" + Directory + "\"," +
128 "\"command\":\"" + Command + "\","
129 "\"file\":\"" + FileName + "\"}]").str(),
131 ASSERT_EQ(2u, FoundCommand.CommandLine.size());
132 EXPECT_EQ("/path to compiler", FoundCommand.CommandLine[0]) << ErrorMessage;
133 EXPECT_EQ("and an argument", FoundCommand.CommandLine[1]) << ErrorMessage;
136 TEST(findCompileArgsInJsonDatabase, ReadsDirectoryWithSpaces) {
137 StringRef Directory("/some directory / with spaces");
138 StringRef FileName("/path/to/a-file.cpp");
139 StringRef Command("a command");
140 std::string ErrorMessage;
141 CompileCommand FoundCommand = findCompileArgsInJsonDatabase(
143 ("[{\"directory\":\"" + Directory + "\"," +
144 "\"command\":\"" + Command + "\","
145 "\"file\":\"" + FileName + "\"}]").str(),
147 EXPECT_EQ(Directory, FoundCommand.Directory) << ErrorMessage;
150 TEST(findCompileArgsInJsonDatabase, FindsEntry) {
151 StringRef Directory("directory");
152 StringRef FileName("file");
153 StringRef Command("command");
154 std::string JsonDatabase = "[";
155 for (int I = 0; I < 10; ++I) {
156 if (I > 0) JsonDatabase += ",";
158 ("{\"directory\":\"" + Directory + Twine(I) + "\"," +
159 "\"command\":\"" + Command + Twine(I) + "\","
160 "\"file\":\"" + FileName + Twine(I) + "\"}").str();
163 std::string ErrorMessage;
164 CompileCommand FoundCommand = findCompileArgsInJsonDatabase(
165 "file4", JsonDatabase, ErrorMessage);
166 EXPECT_EQ("directory4", FoundCommand.Directory) << ErrorMessage;
167 ASSERT_EQ(1u, FoundCommand.CommandLine.size()) << ErrorMessage;
168 EXPECT_EQ("command4", FoundCommand.CommandLine[0]) << ErrorMessage;
171 static std::vector<std::string> unescapeJsonCommandLine(StringRef Command) {
172 std::string JsonDatabase =
173 ("[{\"directory\":\"\", \"file\":\"test\", \"command\": \"" +
174 Command + "\"}]").str();
175 std::string ErrorMessage;
176 CompileCommand FoundCommand = findCompileArgsInJsonDatabase(
177 "test", JsonDatabase, ErrorMessage);
178 EXPECT_TRUE(ErrorMessage.empty()) << ErrorMessage;
179 return FoundCommand.CommandLine;
182 TEST(unescapeJsonCommandLine, ReturnsEmptyArrayOnEmptyString) {
183 std::vector<std::string> Result = unescapeJsonCommandLine("");
184 EXPECT_TRUE(Result.empty());
187 TEST(unescapeJsonCommandLine, SplitsOnSpaces) {
188 std::vector<std::string> Result = unescapeJsonCommandLine("a b c");
189 ASSERT_EQ(3ul, Result.size());
190 EXPECT_EQ("a", Result[0]);
191 EXPECT_EQ("b", Result[1]);
192 EXPECT_EQ("c", Result[2]);
195 TEST(unescapeJsonCommandLine, MungesMultipleSpaces) {
196 std::vector<std::string> Result = unescapeJsonCommandLine(" a b ");
197 ASSERT_EQ(2ul, Result.size());
198 EXPECT_EQ("a", Result[0]);
199 EXPECT_EQ("b", Result[1]);
202 TEST(unescapeJsonCommandLine, UnescapesBackslashCharacters) {
203 std::vector<std::string> Backslash = unescapeJsonCommandLine("a\\\\\\\\");
204 ASSERT_EQ(1ul, Backslash.size());
205 EXPECT_EQ("a\\", Backslash[0]);
206 std::vector<std::string> Quote = unescapeJsonCommandLine("a\\\\\\\"");
207 ASSERT_EQ(1ul, Quote.size());
208 EXPECT_EQ("a\"", Quote[0]);
211 TEST(unescapeJsonCommandLine, DoesNotMungeSpacesBetweenQuotes) {
212 std::vector<std::string> Result = unescapeJsonCommandLine("\\\" a b \\\"");
213 ASSERT_EQ(1ul, Result.size());
214 EXPECT_EQ(" a b ", Result[0]);
217 TEST(unescapeJsonCommandLine, AllowsMultipleQuotedArguments) {
218 std::vector<std::string> Result = unescapeJsonCommandLine(
219 " \\\" a \\\" \\\" b \\\" ");
220 ASSERT_EQ(2ul, Result.size());
221 EXPECT_EQ(" a ", Result[0]);
222 EXPECT_EQ(" b ", Result[1]);
225 TEST(unescapeJsonCommandLine, AllowsEmptyArgumentsInQuotes) {
226 std::vector<std::string> Result = unescapeJsonCommandLine(
228 ASSERT_EQ(1ul, Result.size());
229 EXPECT_TRUE(Result[0].empty()) << Result[0];
232 TEST(unescapeJsonCommandLine, ParsesEscapedQuotesInQuotedStrings) {
233 std::vector<std::string> Result = unescapeJsonCommandLine(
235 ASSERT_EQ(1ul, Result.size());
236 EXPECT_EQ("\"", Result[0]);
239 TEST(unescapeJsonCommandLine, ParsesMultipleArgumentsWithEscapedCharacters) {
240 std::vector<std::string> Result = unescapeJsonCommandLine(
241 " \\\\\\\" \\\"a \\\\\\\" b \\\" \\\"and\\\\\\\\c\\\" \\\\\\\"");
242 ASSERT_EQ(4ul, Result.size());
243 EXPECT_EQ("\"", Result[0]);
244 EXPECT_EQ("a \" b ", Result[1]);
245 EXPECT_EQ("and\\c", Result[2]);
246 EXPECT_EQ("\"", Result[3]);
249 TEST(unescapeJsonCommandLine, ParsesStringsWithoutSpacesIntoSingleArgument) {
250 std::vector<std::string> QuotedNoSpaces = unescapeJsonCommandLine(
251 "\\\"a\\\"\\\"b\\\"");
252 ASSERT_EQ(1ul, QuotedNoSpaces.size());
253 EXPECT_EQ("ab", QuotedNoSpaces[0]);
255 std::vector<std::string> MixedNoSpaces = unescapeJsonCommandLine(
256 "\\\"a\\\"bcd\\\"ef\\\"\\\"\\\"\\\"g\\\"");
257 ASSERT_EQ(1ul, MixedNoSpaces.size());
258 EXPECT_EQ("abcdefg", MixedNoSpaces[0]);
261 TEST(unescapeJsonCommandLine, ParsesQuotedStringWithoutClosingQuote) {
262 std::vector<std::string> Unclosed = unescapeJsonCommandLine("\\\"abc");
263 ASSERT_EQ(1ul, Unclosed.size());
264 EXPECT_EQ("abc", Unclosed[0]);
266 std::vector<std::string> Empty = unescapeJsonCommandLine("\\\"");
267 ASSERT_EQ(1ul, Empty.size());
268 EXPECT_EQ("", Empty[0]);
271 TEST(FixedCompilationDatabase, ReturnsFixedCommandLine) {
272 std::vector<std::string> CommandLine;
273 CommandLine.push_back("one");
274 CommandLine.push_back("two");
275 FixedCompilationDatabase Database(".", CommandLine);
276 std::vector<CompileCommand> Result =
277 Database.getCompileCommands("source");
278 ASSERT_EQ(1ul, Result.size());
279 std::vector<std::string> ExpectedCommandLine(1, "clang-tool");
280 ExpectedCommandLine.insert(ExpectedCommandLine.end(),
281 CommandLine.begin(), CommandLine.end());
282 ExpectedCommandLine.push_back("source");
283 EXPECT_EQ(".", Result[0].Directory);
284 EXPECT_EQ(ExpectedCommandLine, Result[0].CommandLine);
287 TEST(FixedCompilationDatabase, GetAllFiles) {
288 std::vector<std::string> CommandLine;
289 CommandLine.push_back("one");
290 CommandLine.push_back("two");
291 FixedCompilationDatabase Database(".", CommandLine);
293 EXPECT_EQ(0ul, Database.getAllFiles().size());
296 TEST(ParseFixedCompilationDatabase, ReturnsNullOnEmptyArgumentList) {
298 llvm::OwningPtr<FixedCompilationDatabase> Database(
299 FixedCompilationDatabase::loadFromCommandLine(Argc, NULL));
300 EXPECT_FALSE(Database);
304 TEST(ParseFixedCompilationDatabase, ReturnsNullWithoutDoubleDash) {
306 const char *Argv[] = { "1", "2" };
307 llvm::OwningPtr<FixedCompilationDatabase> Database(
308 FixedCompilationDatabase::loadFromCommandLine(Argc, Argv));
309 EXPECT_FALSE(Database);
313 TEST(ParseFixedCompilationDatabase, ReturnsArgumentsAfterDoubleDash) {
315 const char *Argv[] = { "1", "2", "--\0no-constant-folding", "3", "4" };
316 llvm::OwningPtr<FixedCompilationDatabase> Database(
317 FixedCompilationDatabase::loadFromCommandLine(Argc, Argv));
318 ASSERT_TRUE(Database);
319 std::vector<CompileCommand> Result =
320 Database->getCompileCommands("source");
321 ASSERT_EQ(1ul, Result.size());
322 ASSERT_EQ(".", Result[0].Directory);
323 std::vector<std::string> CommandLine;
324 CommandLine.push_back("clang-tool");
325 CommandLine.push_back("3");
326 CommandLine.push_back("4");
327 CommandLine.push_back("source");
328 ASSERT_EQ(CommandLine, Result[0].CommandLine);
332 TEST(ParseFixedCompilationDatabase, ReturnsEmptyCommandLine) {
334 const char *Argv[] = { "1", "2", "--\0no-constant-folding" };
335 llvm::OwningPtr<FixedCompilationDatabase> Database(
336 FixedCompilationDatabase::loadFromCommandLine(Argc, Argv));
337 ASSERT_TRUE(Database);
338 std::vector<CompileCommand> Result =
339 Database->getCompileCommands("source");
340 ASSERT_EQ(1ul, Result.size());
341 ASSERT_EQ(".", Result[0].Directory);
342 std::vector<std::string> CommandLine;
343 CommandLine.push_back("clang-tool");
344 CommandLine.push_back("source");
345 ASSERT_EQ(CommandLine, Result[0].CommandLine);
349 } // end namespace tooling
350 } // end namespace clang