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/FileMatchTrie.h" 15 #include "clang/Tooling/JSONCompilationDatabase.h" 16 #include "clang/Tooling/Tooling.h" 17 #include "llvm/Support/PathV2.h" 18 #include "gtest/gtest.h" 19 20 namespace clang { 21 namespace tooling { 22 23 static void expectFailure(StringRef JSONDatabase, StringRef Explanation) { 24 std::string ErrorMessage; 25 EXPECT_EQ(NULL, JSONCompilationDatabase::loadFromBuffer(JSONDatabase, 26 ErrorMessage)) 27 << "Expected an error because of: " << Explanation; 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"); 40 expectFailure("[{\"command\":\"\",\"file\":\"\"}]", "Missing directory"); 41 } 42 43 static std::vector<std::string> getAllFiles(StringRef JSONDatabase, 44 std::string &ErrorMessage) { 45 llvm::OwningPtr<CompilationDatabase> Database( 46 JSONCompilationDatabase::loadFromBuffer(JSONDatabase, ErrorMessage)); 47 if (!Database) { 48 ADD_FAILURE() << ErrorMessage; 49 return std::vector<std::string>(); 50 } 51 return Database->getAllFiles(); 52 } 53 54 TEST(JSONCompilationDatabase, GetAllFiles) { 55 std::string ErrorMessage; 56 EXPECT_EQ(std::vector<std::string>(), 57 getAllFiles("[]", ErrorMessage)) << ErrorMessage; 58 59 std::vector<std::string> expected_files; 60 SmallString<16> PathStorage; 61 llvm::sys::path::native("//net/dir/file1", PathStorage); 62 expected_files.push_back(PathStorage.str()); 63 llvm::sys::path::native("//net/dir/file2", PathStorage); 64 expected_files.push_back(PathStorage.str()); 65 EXPECT_EQ(expected_files, getAllFiles( 66 "[{\"directory\":\"//net/dir\"," 67 "\"command\":\"command\"," 68 "\"file\":\"file1\"}," 69 " {\"directory\":\"//net/dir\"," 70 "\"command\":\"command\"," 71 "\"file\":\"file2\"}]", 72 ErrorMessage)) << ErrorMessage; 73 } 74 75 static CompileCommand findCompileArgsInJsonDatabase(StringRef FileName, 76 StringRef JSONDatabase, 77 std::string &ErrorMessage) { 78 llvm::OwningPtr<CompilationDatabase> Database( 79 JSONCompilationDatabase::loadFromBuffer(JSONDatabase, ErrorMessage)); 80 if (!Database) 81 return CompileCommand(); 82 std::vector<CompileCommand> Commands = Database->getCompileCommands(FileName); 83 EXPECT_LE(Commands.size(), 1u); 84 if (Commands.empty()) 85 return CompileCommand(); 86 return Commands[0]; 87 } 88 89 struct FakeComparator : public PathComparator { 90 virtual ~FakeComparator() {} 91 virtual bool equivalent(StringRef FileA, StringRef FileB) const { 92 return FileA.equals_lower(FileB); 93 } 94 }; 95 96 class FileMatchTrieTest : public ::testing::Test { 97 protected: 98 FileMatchTrieTest() : Trie(new FakeComparator()) {} 99 100 StringRef find(StringRef Path) { 101 llvm::raw_string_ostream ES(Error); 102 return Trie.findEquivalent(Path, ES); 103 } 104 105 FileMatchTrie Trie; 106 std::string Error; 107 }; 108 109 TEST_F(FileMatchTrieTest, InsertingRelativePath) { 110 Trie.insert("//net/path/file.cc"); 111 Trie.insert("file.cc"); 112 EXPECT_EQ("//net/path/file.cc", find("//net/path/file.cc")); 113 } 114 115 TEST_F(FileMatchTrieTest, MatchingRelativePath) { 116 EXPECT_EQ("", find("file.cc")); 117 } 118 119 TEST_F(FileMatchTrieTest, ReturnsBestResults) { 120 Trie.insert("//net/d/c/b.cc"); 121 Trie.insert("//net/d/b/b.cc"); 122 EXPECT_EQ("//net/d/b/b.cc", find("//net/d/b/b.cc")); 123 } 124 125 TEST_F(FileMatchTrieTest, HandlesSymlinks) { 126 Trie.insert("//net/AA/file.cc"); 127 EXPECT_EQ("//net/AA/file.cc", find("//net/aa/file.cc")); 128 } 129 130 TEST_F(FileMatchTrieTest, ReportsSymlinkAmbiguity) { 131 Trie.insert("//net/Aa/file.cc"); 132 Trie.insert("//net/aA/file.cc"); 133 EXPECT_TRUE(find("//net/aa/file.cc").empty()); 134 EXPECT_EQ("Path is ambiguous", Error); 135 } 136 137 TEST_F(FileMatchTrieTest, LongerMatchingSuffixPreferred) { 138 Trie.insert("//net/src/Aa/file.cc"); 139 Trie.insert("//net/src/aA/file.cc"); 140 Trie.insert("//net/SRC/aa/file.cc"); 141 EXPECT_EQ("//net/SRC/aa/file.cc", find("//net/src/aa/file.cc")); 142 } 143 144 TEST_F(FileMatchTrieTest, EmptyTrie) { 145 EXPECT_TRUE(find("//net/some/path").empty()); 146 } 147 148 TEST_F(FileMatchTrieTest, NoResult) { 149 Trie.insert("//net/somepath/otherfile.cc"); 150 Trie.insert("//net/otherpath/somefile.cc"); 151 EXPECT_EQ("", find("//net/somepath/somefile.cc")); 152 } 153 154 TEST_F(FileMatchTrieTest, RootElementDifferent) { 155 Trie.insert("//net/path/file.cc"); 156 Trie.insert("//net/otherpath/file.cc"); 157 EXPECT_EQ("//net/path/file.cc", find("//net/path/file.cc")); 158 } 159 160 TEST_F(FileMatchTrieTest, CannotResolveRelativePath) { 161 EXPECT_EQ("", find("relative-path.cc")); 162 EXPECT_EQ("Cannot resolve relative paths", Error); 163 } 164 165 TEST(findCompileArgsInJsonDatabase, FindsNothingIfEmpty) { 166 std::string ErrorMessage; 167 CompileCommand NotFound = findCompileArgsInJsonDatabase( 168 "a-file.cpp", "", ErrorMessage); 169 EXPECT_TRUE(NotFound.CommandLine.empty()) << ErrorMessage; 170 EXPECT_TRUE(NotFound.Directory.empty()) << ErrorMessage; 171 } 172 173 TEST(findCompileArgsInJsonDatabase, ReadsSingleEntry) { 174 StringRef Directory("//net/some/directory"); 175 StringRef FileName("//net/path/to/a-file.cpp"); 176 StringRef Command("//net/path/to/compiler and some arguments"); 177 std::string ErrorMessage; 178 CompileCommand FoundCommand = findCompileArgsInJsonDatabase( 179 FileName, 180 ("[{\"directory\":\"" + Directory + "\"," + 181 "\"command\":\"" + Command + "\"," 182 "\"file\":\"" + FileName + "\"}]").str(), 183 ErrorMessage); 184 EXPECT_EQ(Directory, FoundCommand.Directory) << ErrorMessage; 185 ASSERT_EQ(4u, FoundCommand.CommandLine.size()) << ErrorMessage; 186 EXPECT_EQ("//net/path/to/compiler", 187 FoundCommand.CommandLine[0]) << ErrorMessage; 188 EXPECT_EQ("and", FoundCommand.CommandLine[1]) << ErrorMessage; 189 EXPECT_EQ("some", FoundCommand.CommandLine[2]) << ErrorMessage; 190 EXPECT_EQ("arguments", FoundCommand.CommandLine[3]) << ErrorMessage; 191 192 CompileCommand NotFound = findCompileArgsInJsonDatabase( 193 "a-file.cpp", 194 ("[{\"directory\":\"" + Directory + "\"," + 195 "\"command\":\"" + Command + "\"," 196 "\"file\":\"" + FileName + "\"}]").str(), 197 ErrorMessage); 198 EXPECT_TRUE(NotFound.Directory.empty()) << ErrorMessage; 199 EXPECT_TRUE(NotFound.CommandLine.empty()) << ErrorMessage; 200 } 201 202 TEST(findCompileArgsInJsonDatabase, ReadsCompileCommandLinesWithSpaces) { 203 StringRef Directory("//net/some/directory"); 204 StringRef FileName("//net/path/to/a-file.cpp"); 205 StringRef Command("\\\"//net/path to compiler\\\" \\\"and an argument\\\""); 206 std::string ErrorMessage; 207 CompileCommand FoundCommand = findCompileArgsInJsonDatabase( 208 FileName, 209 ("[{\"directory\":\"" + Directory + "\"," + 210 "\"command\":\"" + Command + "\"," 211 "\"file\":\"" + FileName + "\"}]").str(), 212 ErrorMessage); 213 ASSERT_EQ(2u, FoundCommand.CommandLine.size()); 214 EXPECT_EQ("//net/path to compiler", 215 FoundCommand.CommandLine[0]) << ErrorMessage; 216 EXPECT_EQ("and an argument", FoundCommand.CommandLine[1]) << ErrorMessage; 217 } 218 219 TEST(findCompileArgsInJsonDatabase, ReadsDirectoryWithSpaces) { 220 StringRef Directory("//net/some directory / with spaces"); 221 StringRef FileName("//net/path/to/a-file.cpp"); 222 StringRef Command("a command"); 223 std::string ErrorMessage; 224 CompileCommand FoundCommand = findCompileArgsInJsonDatabase( 225 FileName, 226 ("[{\"directory\":\"" + Directory + "\"," + 227 "\"command\":\"" + Command + "\"," 228 "\"file\":\"" + FileName + "\"}]").str(), 229 ErrorMessage); 230 EXPECT_EQ(Directory, FoundCommand.Directory) << ErrorMessage; 231 } 232 233 TEST(findCompileArgsInJsonDatabase, FindsEntry) { 234 StringRef Directory("//net/directory"); 235 StringRef FileName("file"); 236 StringRef Command("command"); 237 std::string JsonDatabase = "["; 238 for (int I = 0; I < 10; ++I) { 239 if (I > 0) JsonDatabase += ","; 240 JsonDatabase += 241 ("{\"directory\":\"" + Directory + Twine(I) + "\"," + 242 "\"command\":\"" + Command + Twine(I) + "\"," 243 "\"file\":\"" + FileName + Twine(I) + "\"}").str(); 244 } 245 JsonDatabase += "]"; 246 std::string ErrorMessage; 247 CompileCommand FoundCommand = findCompileArgsInJsonDatabase( 248 "//net/directory4/file4", JsonDatabase, ErrorMessage); 249 EXPECT_EQ("//net/directory4", FoundCommand.Directory) << ErrorMessage; 250 ASSERT_EQ(1u, FoundCommand.CommandLine.size()) << ErrorMessage; 251 EXPECT_EQ("command4", FoundCommand.CommandLine[0]) << ErrorMessage; 252 } 253 254 static std::vector<std::string> unescapeJsonCommandLine(StringRef Command) { 255 std::string JsonDatabase = 256 ("[{\"directory\":\"//net/root\", \"file\":\"test\", \"command\": \"" + 257 Command + "\"}]").str(); 258 std::string ErrorMessage; 259 CompileCommand FoundCommand = findCompileArgsInJsonDatabase( 260 "//net/root/test", JsonDatabase, ErrorMessage); 261 EXPECT_TRUE(ErrorMessage.empty()) << ErrorMessage; 262 return FoundCommand.CommandLine; 263 } 264 265 TEST(unescapeJsonCommandLine, ReturnsEmptyArrayOnEmptyString) { 266 std::vector<std::string> Result = unescapeJsonCommandLine(""); 267 EXPECT_TRUE(Result.empty()); 268 } 269 270 TEST(unescapeJsonCommandLine, SplitsOnSpaces) { 271 std::vector<std::string> Result = unescapeJsonCommandLine("a b c"); 272 ASSERT_EQ(3ul, Result.size()); 273 EXPECT_EQ("a", Result[0]); 274 EXPECT_EQ("b", Result[1]); 275 EXPECT_EQ("c", Result[2]); 276 } 277 278 TEST(unescapeJsonCommandLine, MungesMultipleSpaces) { 279 std::vector<std::string> Result = unescapeJsonCommandLine(" a b "); 280 ASSERT_EQ(2ul, Result.size()); 281 EXPECT_EQ("a", Result[0]); 282 EXPECT_EQ("b", Result[1]); 283 } 284 285 TEST(unescapeJsonCommandLine, UnescapesBackslashCharacters) { 286 std::vector<std::string> Backslash = unescapeJsonCommandLine("a\\\\\\\\"); 287 ASSERT_EQ(1ul, Backslash.size()); 288 EXPECT_EQ("a\\", Backslash[0]); 289 std::vector<std::string> Quote = unescapeJsonCommandLine("a\\\\\\\""); 290 ASSERT_EQ(1ul, Quote.size()); 291 EXPECT_EQ("a\"", Quote[0]); 292 } 293 294 TEST(unescapeJsonCommandLine, DoesNotMungeSpacesBetweenQuotes) { 295 std::vector<std::string> Result = unescapeJsonCommandLine("\\\" a b \\\""); 296 ASSERT_EQ(1ul, Result.size()); 297 EXPECT_EQ(" a b ", Result[0]); 298 } 299 300 TEST(unescapeJsonCommandLine, AllowsMultipleQuotedArguments) { 301 std::vector<std::string> Result = unescapeJsonCommandLine( 302 " \\\" a \\\" \\\" b \\\" "); 303 ASSERT_EQ(2ul, Result.size()); 304 EXPECT_EQ(" a ", Result[0]); 305 EXPECT_EQ(" b ", Result[1]); 306 } 307 308 TEST(unescapeJsonCommandLine, AllowsEmptyArgumentsInQuotes) { 309 std::vector<std::string> Result = unescapeJsonCommandLine( 310 "\\\"\\\"\\\"\\\""); 311 ASSERT_EQ(1ul, Result.size()); 312 EXPECT_TRUE(Result[0].empty()) << Result[0]; 313 } 314 315 TEST(unescapeJsonCommandLine, ParsesEscapedQuotesInQuotedStrings) { 316 std::vector<std::string> Result = unescapeJsonCommandLine( 317 "\\\"\\\\\\\"\\\""); 318 ASSERT_EQ(1ul, Result.size()); 319 EXPECT_EQ("\"", Result[0]); 320 } 321 322 TEST(unescapeJsonCommandLine, ParsesMultipleArgumentsWithEscapedCharacters) { 323 std::vector<std::string> Result = unescapeJsonCommandLine( 324 " \\\\\\\" \\\"a \\\\\\\" b \\\" \\\"and\\\\\\\\c\\\" \\\\\\\""); 325 ASSERT_EQ(4ul, Result.size()); 326 EXPECT_EQ("\"", Result[0]); 327 EXPECT_EQ("a \" b ", Result[1]); 328 EXPECT_EQ("and\\c", Result[2]); 329 EXPECT_EQ("\"", Result[3]); 330 } 331 332 TEST(unescapeJsonCommandLine, ParsesStringsWithoutSpacesIntoSingleArgument) { 333 std::vector<std::string> QuotedNoSpaces = unescapeJsonCommandLine( 334 "\\\"a\\\"\\\"b\\\""); 335 ASSERT_EQ(1ul, QuotedNoSpaces.size()); 336 EXPECT_EQ("ab", QuotedNoSpaces[0]); 337 338 std::vector<std::string> MixedNoSpaces = unescapeJsonCommandLine( 339 "\\\"a\\\"bcd\\\"ef\\\"\\\"\\\"\\\"g\\\""); 340 ASSERT_EQ(1ul, MixedNoSpaces.size()); 341 EXPECT_EQ("abcdefg", MixedNoSpaces[0]); 342 } 343 344 TEST(unescapeJsonCommandLine, ParsesQuotedStringWithoutClosingQuote) { 345 std::vector<std::string> Unclosed = unescapeJsonCommandLine("\\\"abc"); 346 ASSERT_EQ(1ul, Unclosed.size()); 347 EXPECT_EQ("abc", Unclosed[0]); 348 349 std::vector<std::string> Empty = unescapeJsonCommandLine("\\\""); 350 ASSERT_EQ(1ul, Empty.size()); 351 EXPECT_EQ("", Empty[0]); 352 } 353 354 TEST(FixedCompilationDatabase, ReturnsFixedCommandLine) { 355 std::vector<std::string> CommandLine; 356 CommandLine.push_back("one"); 357 CommandLine.push_back("two"); 358 FixedCompilationDatabase Database(".", CommandLine); 359 std::vector<CompileCommand> Result = 360 Database.getCompileCommands("source"); 361 ASSERT_EQ(1ul, Result.size()); 362 std::vector<std::string> ExpectedCommandLine(1, "clang-tool"); 363 ExpectedCommandLine.insert(ExpectedCommandLine.end(), 364 CommandLine.begin(), CommandLine.end()); 365 ExpectedCommandLine.push_back("source"); 366 EXPECT_EQ(".", Result[0].Directory); 367 EXPECT_EQ(ExpectedCommandLine, Result[0].CommandLine); 368 } 369 370 TEST(FixedCompilationDatabase, GetAllFiles) { 371 std::vector<std::string> CommandLine; 372 CommandLine.push_back("one"); 373 CommandLine.push_back("two"); 374 FixedCompilationDatabase Database(".", CommandLine); 375 376 EXPECT_EQ(0ul, Database.getAllFiles().size()); 377 } 378 379 TEST(ParseFixedCompilationDatabase, ReturnsNullOnEmptyArgumentList) { 380 int Argc = 0; 381 llvm::OwningPtr<FixedCompilationDatabase> Database( 382 FixedCompilationDatabase::loadFromCommandLine(Argc, NULL)); 383 EXPECT_FALSE(Database); 384 EXPECT_EQ(0, Argc); 385 } 386 387 TEST(ParseFixedCompilationDatabase, ReturnsNullWithoutDoubleDash) { 388 int Argc = 2; 389 const char *Argv[] = { "1", "2" }; 390 llvm::OwningPtr<FixedCompilationDatabase> Database( 391 FixedCompilationDatabase::loadFromCommandLine(Argc, Argv)); 392 EXPECT_FALSE(Database); 393 EXPECT_EQ(2, Argc); 394 } 395 396 TEST(ParseFixedCompilationDatabase, ReturnsArgumentsAfterDoubleDash) { 397 int Argc = 5; 398 const char *Argv[] = { "1", "2", "--\0no-constant-folding", "3", "4" }; 399 llvm::OwningPtr<FixedCompilationDatabase> Database( 400 FixedCompilationDatabase::loadFromCommandLine(Argc, Argv)); 401 ASSERT_TRUE(Database); 402 std::vector<CompileCommand> Result = 403 Database->getCompileCommands("source"); 404 ASSERT_EQ(1ul, Result.size()); 405 ASSERT_EQ(".", Result[0].Directory); 406 std::vector<std::string> CommandLine; 407 CommandLine.push_back("clang-tool"); 408 CommandLine.push_back("3"); 409 CommandLine.push_back("4"); 410 CommandLine.push_back("source"); 411 ASSERT_EQ(CommandLine, Result[0].CommandLine); 412 EXPECT_EQ(2, Argc); 413 } 414 415 TEST(ParseFixedCompilationDatabase, ReturnsEmptyCommandLine) { 416 int Argc = 3; 417 const char *Argv[] = { "1", "2", "--\0no-constant-folding" }; 418 llvm::OwningPtr<FixedCompilationDatabase> Database( 419 FixedCompilationDatabase::loadFromCommandLine(Argc, Argv)); 420 ASSERT_TRUE(Database); 421 std::vector<CompileCommand> Result = 422 Database->getCompileCommands("source"); 423 ASSERT_EQ(1ul, Result.size()); 424 ASSERT_EQ(".", Result[0].Directory); 425 std::vector<std::string> CommandLine; 426 CommandLine.push_back("clang-tool"); 427 CommandLine.push_back("source"); 428 ASSERT_EQ(CommandLine, Result[0].CommandLine); 429 EXPECT_EQ(2, Argc); 430 } 431 432 } // end namespace tooling 433 } // end namespace clang 434