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/JSONCompilationDatabase.h" 15 #include "clang/Tooling/Tooling.h" 16 #include "gtest/gtest.h" 17 18 namespace clang { 19 namespace tooling { 20 21 static void expectFailure(StringRef JSONDatabase, StringRef Explanation) { 22 std::string ErrorMessage; 23 EXPECT_EQ(NULL, JSONCompilationDatabase::loadFromBuffer(JSONDatabase, 24 ErrorMessage)) 25 << "Expected an error because of: " << Explanation; 26 } 27 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"); 39 } 40 41 static std::vector<std::string> getAllFiles(StringRef JSONDatabase, 42 std::string &ErrorMessage) { 43 llvm::OwningPtr<CompilationDatabase> Database( 44 JSONCompilationDatabase::loadFromBuffer(JSONDatabase, ErrorMessage)); 45 if (!Database) { 46 ADD_FAILURE() << ErrorMessage; 47 return std::vector<std::string>(); 48 } 49 return Database->getAllFiles(); 50 } 51 52 TEST(JSONCompilationDatabase, GetAllFiles) { 53 std::string ErrorMessage; 54 EXPECT_EQ(std::vector<std::string>(), 55 getAllFiles("[]", ErrorMessage)) << ErrorMessage; 56 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; 68 } 69 70 static CompileCommand findCompileArgsInJsonDatabase(StringRef FileName, 71 StringRef JSONDatabase, 72 std::string &ErrorMessage) { 73 llvm::OwningPtr<CompilationDatabase> Database( 74 JSONCompilationDatabase::loadFromBuffer(JSONDatabase, ErrorMessage)); 75 if (!Database) 76 return CompileCommand(); 77 std::vector<CompileCommand> Commands = Database->getCompileCommands(FileName); 78 EXPECT_LE(Commands.size(), 1u); 79 if (Commands.empty()) 80 return CompileCommand(); 81 return Commands[0]; 82 } 83 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; 90 } 91 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( 98 FileName, 99 ("[{\"directory\":\"" + Directory + "\"," + 100 "\"command\":\"" + Command + "\"," 101 "\"file\":\"" + FileName + "\"}]").str(), 102 ErrorMessage); 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; 109 110 CompileCommand NotFound = findCompileArgsInJsonDatabase( 111 "a-file.cpp", 112 ("[{\"directory\":\"" + Directory + "\"," + 113 "\"command\":\"" + Command + "\"," 114 "\"file\":\"" + FileName + "\"}]").str(), 115 ErrorMessage); 116 EXPECT_TRUE(NotFound.Directory.empty()) << ErrorMessage; 117 EXPECT_TRUE(NotFound.CommandLine.empty()) << ErrorMessage; 118 } 119 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( 126 FileName, 127 ("[{\"directory\":\"" + Directory + "\"," + 128 "\"command\":\"" + Command + "\"," 129 "\"file\":\"" + FileName + "\"}]").str(), 130 ErrorMessage); 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; 134 } 135 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( 142 FileName, 143 ("[{\"directory\":\"" + Directory + "\"," + 144 "\"command\":\"" + Command + "\"," 145 "\"file\":\"" + FileName + "\"}]").str(), 146 ErrorMessage); 147 EXPECT_EQ(Directory, FoundCommand.Directory) << ErrorMessage; 148 } 149 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 += ","; 157 JsonDatabase += 158 ("{\"directory\":\"" + Directory + Twine(I) + "\"," + 159 "\"command\":\"" + Command + Twine(I) + "\"," 160 "\"file\":\"" + FileName + Twine(I) + "\"}").str(); 161 } 162 JsonDatabase += "]"; 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; 169 } 170 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; 180 } 181 182 TEST(unescapeJsonCommandLine, ReturnsEmptyArrayOnEmptyString) { 183 std::vector<std::string> Result = unescapeJsonCommandLine(""); 184 EXPECT_TRUE(Result.empty()); 185 } 186 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]); 193 } 194 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]); 200 } 201 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]); 209 } 210 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]); 215 } 216 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]); 223 } 224 225 TEST(unescapeJsonCommandLine, AllowsEmptyArgumentsInQuotes) { 226 std::vector<std::string> Result = unescapeJsonCommandLine( 227 "\\\"\\\"\\\"\\\""); 228 ASSERT_EQ(1ul, Result.size()); 229 EXPECT_TRUE(Result[0].empty()) << Result[0]; 230 } 231 232 TEST(unescapeJsonCommandLine, ParsesEscapedQuotesInQuotedStrings) { 233 std::vector<std::string> Result = unescapeJsonCommandLine( 234 "\\\"\\\\\\\"\\\""); 235 ASSERT_EQ(1ul, Result.size()); 236 EXPECT_EQ("\"", Result[0]); 237 } 238 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]); 247 } 248 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]); 254 255 std::vector<std::string> MixedNoSpaces = unescapeJsonCommandLine( 256 "\\\"a\\\"bcd\\\"ef\\\"\\\"\\\"\\\"g\\\""); 257 ASSERT_EQ(1ul, MixedNoSpaces.size()); 258 EXPECT_EQ("abcdefg", MixedNoSpaces[0]); 259 } 260 261 TEST(unescapeJsonCommandLine, ParsesQuotedStringWithoutClosingQuote) { 262 std::vector<std::string> Unclosed = unescapeJsonCommandLine("\\\"abc"); 263 ASSERT_EQ(1ul, Unclosed.size()); 264 EXPECT_EQ("abc", Unclosed[0]); 265 266 std::vector<std::string> Empty = unescapeJsonCommandLine("\\\""); 267 ASSERT_EQ(1ul, Empty.size()); 268 EXPECT_EQ("", Empty[0]); 269 } 270 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); 285 } 286 287 TEST(FixedCompilationDatabase, GetAllFiles) { 288 std::vector<std::string> CommandLine; 289 CommandLine.push_back("one"); 290 CommandLine.push_back("two"); 291 FixedCompilationDatabase Database(".", CommandLine); 292 293 EXPECT_EQ(0ul, Database.getAllFiles().size()); 294 } 295 296 TEST(ParseFixedCompilationDatabase, ReturnsNullOnEmptyArgumentList) { 297 int Argc = 0; 298 llvm::OwningPtr<FixedCompilationDatabase> Database( 299 FixedCompilationDatabase::loadFromCommandLine(Argc, NULL)); 300 EXPECT_FALSE(Database); 301 EXPECT_EQ(0, Argc); 302 } 303 304 TEST(ParseFixedCompilationDatabase, ReturnsNullWithoutDoubleDash) { 305 int Argc = 2; 306 const char *Argv[] = { "1", "2" }; 307 llvm::OwningPtr<FixedCompilationDatabase> Database( 308 FixedCompilationDatabase::loadFromCommandLine(Argc, Argv)); 309 EXPECT_FALSE(Database); 310 EXPECT_EQ(2, Argc); 311 } 312 313 TEST(ParseFixedCompilationDatabase, ReturnsArgumentsAfterDoubleDash) { 314 int Argc = 5; 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); 329 EXPECT_EQ(2, Argc); 330 } 331 332 TEST(ParseFixedCompilationDatabase, ReturnsEmptyCommandLine) { 333 int Argc = 3; 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); 346 EXPECT_EQ(2, Argc); 347 } 348 349 } // end namespace tooling 350 } // end namespace clang 351