14 #include "clang/Tooling/CompilationDatabase.h"
15 #include "llvm/ADT/Optional.h"
16 #include "llvm/ADT/SmallString.h"
17 #include "llvm/ADT/StringExtras.h"
18 #include "llvm/ADT/StringRef.h"
19 #include "llvm/Support/FileSystem.h"
20 #include "llvm/Support/FormatVariadic.h"
21 #include "llvm/Support/MemoryBuffer.h"
22 #include "llvm/Support/Path.h"
23 #include "llvm/Support/raw_ostream.h"
24 #include "gmock/gmock.h"
25 #include "gtest/gtest.h"
32 using ::testing::AllOf;
33 using ::testing::Contains;
34 using ::testing::ElementsAre;
35 using ::testing::EndsWith;
36 using ::testing::HasSubstr;
37 using ::testing::IsEmpty;
39 using ::testing::StartsWith;
40 using ::testing::UnorderedElementsAre;
42 TEST(GlobalCompilationDatabaseTest, FallbackCommand) {
43 DirectoryBasedGlobalCompilationDatabase DB(None);
44 auto Cmd = DB.getFallbackCommand(
testPath(
"foo/bar.cc"));
45 EXPECT_EQ(Cmd.Directory,
testPath(
"foo"));
46 EXPECT_THAT(Cmd.CommandLine, ElementsAre(
"clang",
testPath(
"foo/bar.cc")));
47 EXPECT_EQ(Cmd.Output,
"");
50 Cmd = DB.getFallbackCommand(
testPath(
"foo/bar.h"));
51 EXPECT_THAT(Cmd.CommandLine, ElementsAre(
"clang",
"-xobjective-c++-header",
53 Cmd = DB.getFallbackCommand(
testPath(
"foo/bar"));
54 EXPECT_THAT(Cmd.CommandLine, ElementsAre(
"clang",
"-xobjective-c++-header",
58 static tooling::CompileCommand cmd(llvm::StringRef File, llvm::StringRef Arg) {
59 return tooling::CompileCommand(
60 testRoot(), File, {
"clang", std::string(Arg), std::string(File)},
"");
63 class OverlayCDBTest :
public ::testing::Test {
64 class BaseCDB :
public GlobalCompilationDatabase {
66 llvm::Optional<tooling::CompileCommand>
67 getCompileCommand(llvm::StringRef
File)
const override {
69 return cmd(
File,
"-DA=1");
73 tooling::CompileCommand
74 getFallbackCommand(llvm::StringRef
File)
const override {
75 return cmd(
File,
"-DA=2");
78 llvm::Optional<ProjectInfo> getProjectInfo(
PathRef File)
const override {
84 OverlayCDBTest() :
Base(std::make_unique<BaseCDB>()) {}
85 std::unique_ptr<GlobalCompilationDatabase>
Base;
88 TEST_F(OverlayCDBTest, GetCompileCommand) {
89 OverlayCDB CDB(
Base.get());
91 AllOf(Contains(
testPath(
"foo.cc")), Contains(
"-DA=1")));
94 auto Override = cmd(
testPath(
"foo.cc"),
"-DA=3");
95 CDB.setCompileCommand(
testPath(
"foo.cc"), Override);
99 CDB.setCompileCommand(
testPath(
"missing.cc"), Override);
104 TEST_F(OverlayCDBTest, GetFallbackCommand) {
105 OverlayCDB CDB(
Base.get(), {
"-DA=4"});
107 ElementsAre(
"clang",
"-DA=2",
testPath(
"bar.cc"),
"-DA=4"));
110 TEST_F(OverlayCDBTest, NoBase) {
111 OverlayCDB CDB(
nullptr, {
"-DA=6"});
113 auto Override = cmd(
testPath(
"bar.cc"),
"-DA=5");
114 CDB.setCompileCommand(
testPath(
"bar.cc"), Override);
119 ElementsAre(
"clang",
testPath(
"foo.cc"),
"-DA=6"));
122 TEST_F(OverlayCDBTest, Watch) {
123 OverlayCDB
Inner(
nullptr);
126 std::vector<std::vector<std::string>>
Changes;
127 auto Sub =
Outer.watch([&](
const std::vector<std::string> &ChangedFiles) {
128 Changes.push_back(ChangedFiles);
131 Inner.setCompileCommand(
"A.cpp", tooling::CompileCommand());
132 Outer.setCompileCommand(
"B.cpp", tooling::CompileCommand());
135 EXPECT_THAT(
Changes, ElementsAre(ElementsAre(
"A.cpp"), ElementsAre(
"B.cpp"),
136 ElementsAre(
"A.cpp"), ElementsAre(
"C.cpp")));
139 TEST_F(OverlayCDBTest, Adjustments) {
140 OverlayCDB CDB(
Base.get(), {
"-DFallback"},
141 [](
const std::vector<std::string> &Cmd, llvm::StringRef File) {
144 (
"-DAdjust_" + llvm::sys::path::filename(File)).str());
149 EXPECT_THAT(Cmd.CommandLine, ElementsAre(
"clang",
"-DA=1",
testPath(
"foo.cc"),
153 tooling::CompileCommand BarCommand;
154 BarCommand.Filename =
testPath(
"bar.cc");
155 BarCommand.CommandLine = {
"clang++",
"-DB=1",
testPath(
"bar.cc")};
156 CDB.setCompileCommand(
testPath(
"bar.cc"), BarCommand);
160 ElementsAre(
"clang++",
"-DB=1",
testPath(
"bar.cc"),
"-DAdjust_bar.cc"));
164 EXPECT_THAT(Cmd.CommandLine, ElementsAre(
"clang",
"-DA=2",
"baz.cc",
165 "-DFallback",
"-DAdjust_baz.cc"));
170 llvm::SmallString<128> Root;
174 EXPECT_FALSE(llvm::sys::fs::createUniqueDirectory(
"clangd-cdb-test", Root))
175 <<
"Failed to create unique directory";
179 EXPECT_FALSE(llvm::sys::fs::remove_directories(Root))
180 <<
"Failed to cleanup " << Root;
183 llvm::StringRef root()
const {
return Root; }
186 std::string AbsPath = path(RelativePath);
187 EXPECT_FALSE(llvm::sys::fs::create_directories(
188 llvm::sys::path::parent_path(AbsPath)))
189 <<
"Failed to create directories for: " << AbsPath;
192 llvm::raw_fd_ostream
OS(AbsPath, EC);
193 EXPECT_FALSE(EC) <<
"Failed to open " << AbsPath <<
" for writing";
195 llvm::sys::path::convert_to_slash(Root));
198 EXPECT_FALSE(
OS.has_error());
201 std::string path(
PathRef RelativePath)
const {
202 llvm::SmallString<128> AbsPath(Root);
203 llvm::sys::path::append(AbsPath, RelativePath);
204 llvm::sys::path::native(AbsPath);
205 return AbsPath.str().str();
209 TEST(GlobalCompilationDatabaseTest, DiscoveryWithNestedCDBs) {
210 const char *
const CDBOuter =
219 "file": "build/gen.cc",
224 "file": "build/gen2.cc",
230 const char *
const CDBInner =
236 "directory": "{0}/build",
241 FS.write("compile_commands.json", CDBOuter);
242 FS.write(
"build/compile_commands.json", CDBInner);
247 DirectoryBasedGlobalCompilationDatabase DB(
llvm::None);
248 std::vector<std::string> DiscoveredFiles;
250 DB.watch([&DiscoveredFiles](
const std::vector<std::string>
Changes) {
254 DB.getCompileCommand(
FS.path(
"build/../a.cc"));
255 EXPECT_THAT(DiscoveredFiles, UnorderedElementsAre(AllOf(
256 EndsWith(
"a.cc"), Not(HasSubstr(
"..")))));
257 DiscoveredFiles.clear();
259 DB.getCompileCommand(
FS.path(
"build/gen.cc"));
260 EXPECT_THAT(DiscoveredFiles, UnorderedElementsAre(EndsWith(
"gen.cc")));
265 DirectoryBasedGlobalCompilationDatabase DB(
FS.root().str());
266 std::vector<std::string> DiscoveredFiles;
268 DB.watch([&DiscoveredFiles](
const std::vector<std::string>
Changes) {
272 DB.getCompileCommand(
FS.path(
"a.cc"));
273 EXPECT_THAT(DiscoveredFiles,
274 UnorderedElementsAre(EndsWith(
"a.cc"), EndsWith(
"gen.cc"),
275 EndsWith(
"gen2.cc")));
276 DiscoveredFiles.clear();
278 DB.getCompileCommand(
FS.path(
"build/gen.cc"));
279 EXPECT_THAT(DiscoveredFiles, IsEmpty());
283 TEST(GlobalCompilationDatabaseTest, BuildDir) {
285 auto Command = [&](llvm::StringRef Relative) {
286 return DirectoryBasedGlobalCompilationDatabase(
llvm::None)
287 .getCompileCommand(
FS.path(Relative))
288 .getValueOr(tooling::CompileCommand())
291 EXPECT_THAT(Command(
"x/foo.cc"), IsEmpty());
292 FS.write(
"x/build/compile_flags.txt",
"-DXYZZY");
293 EXPECT_THAT(Command(
"x/foo.cc"), Contains(
"-DXYZZY"));
294 EXPECT_THAT(Command(
"bar.cc"), IsEmpty())
295 <<
"x/build/compile_flags.txt only applicable to x/";
298 TEST(GlobalCompilationDatabaseTest, NonCanonicalFilenames) {
299 OverlayCDB DB(
nullptr);
300 std::vector<std::string> DiscoveredFiles;
302 DB.watch([&DiscoveredFiles](
const std::vector<std::string>
Changes) {
306 llvm::SmallString<128> Root(
testRoot());
307 llvm::sys::path::append(Root,
"build",
"..",
"a.cc");
308 DB.setCompileCommand(Root.str(), tooling::CompileCommand());
309 EXPECT_THAT(DiscoveredFiles, UnorderedElementsAre(
testPath(
"a.cc")));
310 DiscoveredFiles.clear();
312 llvm::SmallString<128> File(
testRoot());
313 llvm::sys::path::append(File,
"blabla",
"..",
"a.cc");
315 EXPECT_TRUE(DB.getCompileCommand(File));
316 EXPECT_FALSE(DB.getProjectInfo(File));
319 TEST_F(OverlayCDBTest, GetProjectInfo) {
320 OverlayCDB DB(
Base.get());
324 EXPECT_EQ(DB.getProjectInfo(File)->SourceRoot,
testRoot());
325 EXPECT_EQ(DB.getProjectInfo(Header)->SourceRoot,
testRoot());
328 DB.setCompileCommand(File, tooling::CompileCommand());
329 EXPECT_EQ(DB.getProjectInfo(File)->SourceRoot,
testRoot());
330 EXPECT_EQ(DB.getProjectInfo(Header)->SourceRoot,
testRoot());