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