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