Home | History | Annotate | Download | only in Tooling
      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