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 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 CompileCommand findCompileArgsInJsonDatabase(StringRef FileName, 42 StringRef JSONDatabase, 43 std::string &ErrorMessage) { 44 llvm::OwningPtr<CompilationDatabase> Database( 45 JSONCompilationDatabase::loadFromBuffer(JSONDatabase, ErrorMessage)); 46 if (!Database) 47 return CompileCommand(); 48 std::vector<CompileCommand> Commands = Database->getCompileCommands(FileName); 49 EXPECT_LE(Commands.size(), 1u); 50 if (Commands.empty()) 51 return CompileCommand(); 52 return Commands[0]; 53 } 54 55 TEST(findCompileArgsInJsonDatabase, FindsNothingIfEmpty) { 56 std::string ErrorMessage; 57 CompileCommand NotFound = findCompileArgsInJsonDatabase( 58 "a-file.cpp", "", ErrorMessage); 59 EXPECT_TRUE(NotFound.CommandLine.empty()) << ErrorMessage; 60 EXPECT_TRUE(NotFound.Directory.empty()) << ErrorMessage; 61 } 62 63 TEST(findCompileArgsInJsonDatabase, ReadsSingleEntry) { 64 StringRef Directory("/some/directory"); 65 StringRef FileName("/path/to/a-file.cpp"); 66 StringRef Command("/path/to/compiler and some arguments"); 67 std::string ErrorMessage; 68 CompileCommand FoundCommand = findCompileArgsInJsonDatabase( 69 FileName, 70 ("[{\"directory\":\"" + Directory + "\"," + 71 "\"command\":\"" + Command + "\"," 72 "\"file\":\"" + FileName + "\"}]").str(), 73 ErrorMessage); 74 EXPECT_EQ(Directory, FoundCommand.Directory) << ErrorMessage; 75 ASSERT_EQ(4u, FoundCommand.CommandLine.size()) << ErrorMessage; 76 EXPECT_EQ("/path/to/compiler", FoundCommand.CommandLine[0]) << ErrorMessage; 77 EXPECT_EQ("and", FoundCommand.CommandLine[1]) << ErrorMessage; 78 EXPECT_EQ("some", FoundCommand.CommandLine[2]) << ErrorMessage; 79 EXPECT_EQ("arguments", FoundCommand.CommandLine[3]) << ErrorMessage; 80 81 CompileCommand NotFound = findCompileArgsInJsonDatabase( 82 "a-file.cpp", 83 ("[{\"directory\":\"" + Directory + "\"," + 84 "\"command\":\"" + Command + "\"," 85 "\"file\":\"" + FileName + "\"}]").str(), 86 ErrorMessage); 87 EXPECT_TRUE(NotFound.Directory.empty()) << ErrorMessage; 88 EXPECT_TRUE(NotFound.CommandLine.empty()) << ErrorMessage; 89 } 90 91 TEST(findCompileArgsInJsonDatabase, ReadsCompileCommandLinesWithSpaces) { 92 StringRef Directory("/some/directory"); 93 StringRef FileName("/path/to/a-file.cpp"); 94 StringRef Command("\\\"/path to compiler\\\" \\\"and an argument\\\""); 95 std::string ErrorMessage; 96 CompileCommand FoundCommand = findCompileArgsInJsonDatabase( 97 FileName, 98 ("[{\"directory\":\"" + Directory + "\"," + 99 "\"command\":\"" + Command + "\"," 100 "\"file\":\"" + FileName + "\"}]").str(), 101 ErrorMessage); 102 ASSERT_EQ(2u, FoundCommand.CommandLine.size()); 103 EXPECT_EQ("/path to compiler", FoundCommand.CommandLine[0]) << ErrorMessage; 104 EXPECT_EQ("and an argument", FoundCommand.CommandLine[1]) << ErrorMessage; 105 } 106 107 TEST(findCompileArgsInJsonDatabase, ReadsDirectoryWithSpaces) { 108 StringRef Directory("/some directory / with spaces"); 109 StringRef FileName("/path/to/a-file.cpp"); 110 StringRef Command("a command"); 111 std::string ErrorMessage; 112 CompileCommand FoundCommand = findCompileArgsInJsonDatabase( 113 FileName, 114 ("[{\"directory\":\"" + Directory + "\"," + 115 "\"command\":\"" + Command + "\"," 116 "\"file\":\"" + FileName + "\"}]").str(), 117 ErrorMessage); 118 EXPECT_EQ(Directory, FoundCommand.Directory) << ErrorMessage; 119 } 120 121 TEST(findCompileArgsInJsonDatabase, FindsEntry) { 122 StringRef Directory("directory"); 123 StringRef FileName("file"); 124 StringRef Command("command"); 125 std::string JsonDatabase = "["; 126 for (int I = 0; I < 10; ++I) { 127 if (I > 0) JsonDatabase += ","; 128 JsonDatabase += 129 ("{\"directory\":\"" + Directory + Twine(I) + "\"," + 130 "\"command\":\"" + Command + Twine(I) + "\"," 131 "\"file\":\"" + FileName + Twine(I) + "\"}").str(); 132 } 133 JsonDatabase += "]"; 134 std::string ErrorMessage; 135 CompileCommand FoundCommand = findCompileArgsInJsonDatabase( 136 "file4", JsonDatabase, ErrorMessage); 137 EXPECT_EQ("directory4", FoundCommand.Directory) << ErrorMessage; 138 ASSERT_EQ(1u, FoundCommand.CommandLine.size()) << ErrorMessage; 139 EXPECT_EQ("command4", FoundCommand.CommandLine[0]) << ErrorMessage; 140 } 141 142 static std::vector<std::string> unescapeJsonCommandLine(StringRef Command) { 143 std::string JsonDatabase = 144 ("[{\"directory\":\"\", \"file\":\"test\", \"command\": \"" + 145 Command + "\"}]").str(); 146 std::string ErrorMessage; 147 CompileCommand FoundCommand = findCompileArgsInJsonDatabase( 148 "test", JsonDatabase, ErrorMessage); 149 EXPECT_TRUE(ErrorMessage.empty()) << ErrorMessage; 150 return FoundCommand.CommandLine; 151 } 152 153 TEST(unescapeJsonCommandLine, ReturnsEmptyArrayOnEmptyString) { 154 std::vector<std::string> Result = unescapeJsonCommandLine(""); 155 EXPECT_TRUE(Result.empty()); 156 } 157 158 TEST(unescapeJsonCommandLine, SplitsOnSpaces) { 159 std::vector<std::string> Result = unescapeJsonCommandLine("a b c"); 160 ASSERT_EQ(3ul, Result.size()); 161 EXPECT_EQ("a", Result[0]); 162 EXPECT_EQ("b", Result[1]); 163 EXPECT_EQ("c", Result[2]); 164 } 165 166 TEST(unescapeJsonCommandLine, MungesMultipleSpaces) { 167 std::vector<std::string> Result = unescapeJsonCommandLine(" a b "); 168 ASSERT_EQ(2ul, Result.size()); 169 EXPECT_EQ("a", Result[0]); 170 EXPECT_EQ("b", Result[1]); 171 } 172 173 TEST(unescapeJsonCommandLine, UnescapesBackslashCharacters) { 174 std::vector<std::string> Backslash = unescapeJsonCommandLine("a\\\\\\\\"); 175 ASSERT_EQ(1ul, Backslash.size()); 176 EXPECT_EQ("a\\", Backslash[0]); 177 std::vector<std::string> Quote = unescapeJsonCommandLine("a\\\\\\\""); 178 ASSERT_EQ(1ul, Quote.size()); 179 EXPECT_EQ("a\"", Quote[0]); 180 } 181 182 TEST(unescapeJsonCommandLine, DoesNotMungeSpacesBetweenQuotes) { 183 std::vector<std::string> Result = unescapeJsonCommandLine("\\\" a b \\\""); 184 ASSERT_EQ(1ul, Result.size()); 185 EXPECT_EQ(" a b ", Result[0]); 186 } 187 188 TEST(unescapeJsonCommandLine, AllowsMultipleQuotedArguments) { 189 std::vector<std::string> Result = unescapeJsonCommandLine( 190 " \\\" a \\\" \\\" b \\\" "); 191 ASSERT_EQ(2ul, Result.size()); 192 EXPECT_EQ(" a ", Result[0]); 193 EXPECT_EQ(" b ", Result[1]); 194 } 195 196 TEST(unescapeJsonCommandLine, AllowsEmptyArgumentsInQuotes) { 197 std::vector<std::string> Result = unescapeJsonCommandLine( 198 "\\\"\\\"\\\"\\\""); 199 ASSERT_EQ(1ul, Result.size()); 200 EXPECT_TRUE(Result[0].empty()) << Result[0]; 201 } 202 203 TEST(unescapeJsonCommandLine, ParsesEscapedQuotesInQuotedStrings) { 204 std::vector<std::string> Result = unescapeJsonCommandLine( 205 "\\\"\\\\\\\"\\\""); 206 ASSERT_EQ(1ul, Result.size()); 207 EXPECT_EQ("\"", Result[0]); 208 } 209 210 TEST(unescapeJsonCommandLine, ParsesMultipleArgumentsWithEscapedCharacters) { 211 std::vector<std::string> Result = unescapeJsonCommandLine( 212 " \\\\\\\" \\\"a \\\\\\\" b \\\" \\\"and\\\\\\\\c\\\" \\\\\\\""); 213 ASSERT_EQ(4ul, Result.size()); 214 EXPECT_EQ("\"", Result[0]); 215 EXPECT_EQ("a \" b ", Result[1]); 216 EXPECT_EQ("and\\c", Result[2]); 217 EXPECT_EQ("\"", Result[3]); 218 } 219 220 TEST(unescapeJsonCommandLine, ParsesStringsWithoutSpacesIntoSingleArgument) { 221 std::vector<std::string> QuotedNoSpaces = unescapeJsonCommandLine( 222 "\\\"a\\\"\\\"b\\\""); 223 ASSERT_EQ(1ul, QuotedNoSpaces.size()); 224 EXPECT_EQ("ab", QuotedNoSpaces[0]); 225 226 std::vector<std::string> MixedNoSpaces = unescapeJsonCommandLine( 227 "\\\"a\\\"bcd\\\"ef\\\"\\\"\\\"\\\"g\\\""); 228 ASSERT_EQ(1ul, MixedNoSpaces.size()); 229 EXPECT_EQ("abcdefg", MixedNoSpaces[0]); 230 } 231 232 TEST(unescapeJsonCommandLine, ParsesQuotedStringWithoutClosingQuote) { 233 std::vector<std::string> Unclosed = unescapeJsonCommandLine("\\\"abc"); 234 ASSERT_EQ(1ul, Unclosed.size()); 235 EXPECT_EQ("abc", Unclosed[0]); 236 237 std::vector<std::string> Empty = unescapeJsonCommandLine("\\\""); 238 ASSERT_EQ(1ul, Empty.size()); 239 EXPECT_EQ("", Empty[0]); 240 } 241 242 TEST(FixedCompilationDatabase, ReturnsFixedCommandLine) { 243 std::vector<std::string> CommandLine; 244 CommandLine.push_back("one"); 245 CommandLine.push_back("two"); 246 FixedCompilationDatabase Database(".", CommandLine); 247 std::vector<CompileCommand> Result = 248 Database.getCompileCommands("source"); 249 ASSERT_EQ(1ul, Result.size()); 250 std::vector<std::string> ExpectedCommandLine(1, "clang-tool"); 251 ExpectedCommandLine.insert(ExpectedCommandLine.end(), 252 CommandLine.begin(), CommandLine.end()); 253 ExpectedCommandLine.push_back("source"); 254 EXPECT_EQ(".", Result[0].Directory); 255 EXPECT_EQ(ExpectedCommandLine, Result[0].CommandLine); 256 } 257 258 TEST(ParseFixedCompilationDatabase, ReturnsNullOnEmptyArgumentList) { 259 int Argc = 0; 260 llvm::OwningPtr<FixedCompilationDatabase> Database( 261 FixedCompilationDatabase::loadFromCommandLine(Argc, NULL)); 262 EXPECT_FALSE(Database); 263 EXPECT_EQ(0, Argc); 264 } 265 266 TEST(ParseFixedCompilationDatabase, ReturnsNullWithoutDoubleDash) { 267 int Argc = 2; 268 const char *Argv[] = { "1", "2" }; 269 llvm::OwningPtr<FixedCompilationDatabase> Database( 270 FixedCompilationDatabase::loadFromCommandLine(Argc, Argv)); 271 EXPECT_FALSE(Database); 272 EXPECT_EQ(2, Argc); 273 } 274 275 TEST(ParseFixedCompilationDatabase, ReturnsArgumentsAfterDoubleDash) { 276 int Argc = 5; 277 const char *Argv[] = { "1", "2", "--\0no-constant-folding", "3", "4" }; 278 llvm::OwningPtr<FixedCompilationDatabase> Database( 279 FixedCompilationDatabase::loadFromCommandLine(Argc, Argv)); 280 ASSERT_TRUE(Database); 281 std::vector<CompileCommand> Result = 282 Database->getCompileCommands("source"); 283 ASSERT_EQ(1ul, Result.size()); 284 ASSERT_EQ(".", Result[0].Directory); 285 std::vector<std::string> CommandLine; 286 CommandLine.push_back("clang-tool"); 287 CommandLine.push_back("3"); 288 CommandLine.push_back("4"); 289 CommandLine.push_back("source"); 290 ASSERT_EQ(CommandLine, Result[0].CommandLine); 291 EXPECT_EQ(2, Argc); 292 } 293 294 TEST(ParseFixedCompilationDatabase, ReturnsEmptyCommandLine) { 295 int Argc = 3; 296 const char *Argv[] = { "1", "2", "--\0no-constant-folding" }; 297 llvm::OwningPtr<FixedCompilationDatabase> Database( 298 FixedCompilationDatabase::loadFromCommandLine(Argc, Argv)); 299 ASSERT_TRUE(Database); 300 std::vector<CompileCommand> Result = 301 Database->getCompileCommands("source"); 302 ASSERT_EQ(1ul, Result.size()); 303 ASSERT_EQ(".", Result[0].Directory); 304 std::vector<std::string> CommandLine; 305 CommandLine.push_back("clang-tool"); 306 CommandLine.push_back("source"); 307 ASSERT_EQ(CommandLine, Result[0].CommandLine); 308 EXPECT_EQ(2, Argc); 309 } 310 311 } // end namespace tooling 312 } // end namespace clang 313