8 #include "clang/Tooling/CompilationDatabase.h" 9 #include "llvm/Support/ScopedPrinter.h" 10 #include "llvm/Support/Threading.h" 11 #include "gmock/gmock.h" 12 #include "gtest/gtest.h" 17 using ::testing::AllOf;
18 using ::testing::Contains;
19 using ::testing::ElementsAre;
21 using ::testing::UnorderedElementsAre;
28 return !StringRef(arg.CanonicalDeclaration.FileURI).empty();
30 MATCHER(Defined,
"") {
return !StringRef(arg.Definition.FileURI).empty(); }
31 MATCHER_P(FileURI, F,
"") {
return StringRef(arg.Location.FileURI) == F; }
32 ::testing::Matcher<const RefSlab &>
33 RefsAre(std::vector<::testing::Matcher<Ref>> Matchers) {
34 return ElementsAre(::testing::Pair(_, UnorderedElementsAreArray(Matchers)));
39 arg.Digest ==
FileDigest{{0}} && arg.DirectIncludes.empty();
46 MATCHER_P(NumReferences, N,
"") {
return arg.References == N; }
49 mutable std::mutex StorageMu;
50 llvm::StringMap<std::string> &Storage;
55 : Storage(Storage), CacheHits(CacheHits) {}
58 std::lock_guard<std::mutex> Lock(StorageMu);
60 Storage[ShardIdentifier] = llvm::to_string(Shard);
61 return llvm::Error::success();
63 std::unique_ptr<IndexFileIn>
64 loadShard(llvm::StringRef ShardIdentifier)
const override {
65 std::lock_guard<std::mutex> Lock(StorageMu);
67 if (Storage.find(ShardIdentifier) == Storage.end()) {
72 ADD_FAILURE() <<
"Error while reading " << ShardIdentifier <<
':' 73 << IndexFile.takeError();
77 return llvm::make_unique<IndexFileIn>(std::move(*IndexFile));
91 llvm::StringMap<std::string> Storage;
96 [&](llvm::StringRef) {
return &MSS; });
98 tooling::CompileCommand Cmd;
99 Cmd.Filename =
testPath(
"root/A.cc");
101 Cmd.CommandLine = {
"clang++",
"-DA=1",
testPath(
"root/A.cc")};
104 ASSERT_TRUE(Idx.blockUntilIdleForTest());
120 "#include \"A.h\"\nvoid g() { (void)common; }";
131 llvm::StringMap<std::string> Storage; 132 size_t CacheHits = 0;
136 [&](llvm::StringRef) {
return &MSS; });
138 tooling::CompileCommand Cmd;
139 Cmd.Filename =
testPath(
"root/A.cc");
141 Cmd.CommandLine = {
"clang++",
"-DA=1",
testPath(
"root/A.cc")};
144 ASSERT_TRUE(Idx.blockUntilIdleForTest());
146 UnorderedElementsAre(AllOf(Named(
"common"), NumReferences(1U)),
147 AllOf(Named(
"A_CC"), NumReferences(0U)),
148 AllOf(Named(
"g"), NumReferences(0U)),
149 AllOf(Named(
"f_b"), Declared(),
150 Not(Defined()), NumReferences(0U))));
152 Cmd.Filename =
testPath(
"root/B.cc");
153 Cmd.CommandLine = {
"clang++", Cmd.Filename};
156 ASSERT_TRUE(Idx.blockUntilIdleForTest());
159 UnorderedElementsAre(AllOf(Named(
"common"), NumReferences(5U)),
160 AllOf(Named(
"A_CC"), NumReferences(0U)),
161 AllOf(Named(
"g"), NumReferences(0U)),
162 AllOf(Named(
"f_b"), Declared(), Defined(),
163 NumReferences(1U))));
166 EXPECT_THAT(Syms, UnorderedElementsAre(Named(
"common")));
167 auto Common = *Syms.begin();
168 EXPECT_THAT(
getRefs(Idx, Common.ID),
169 RefsAre({FileURI(
"unittest:///root/A.h"),
170 FileURI(
"unittest:///root/A.cc"),
171 FileURI(
"unittest:///root/B.cc"),
172 FileURI(
"unittest:///root/B.cc"),
173 FileURI(
"unittest:///root/B.cc"),
174 FileURI(
"unittest:///root/B.cc")}));
184 std::string A_CC = "";
187 void g() { (void)common; } 188 class B_CC : public A_CC {}; 191 llvm::StringMap<std::string> Storage; 192 size_t CacheHits = 0;
195 tooling::CompileCommand Cmd;
196 Cmd.Filename =
testPath(
"root/A.cc");
198 Cmd.CommandLine = {
"clang++",
testPath(
"root/A.cc")};
203 [&](llvm::StringRef) {
return &MSS; });
205 ASSERT_TRUE(Idx.blockUntilIdleForTest());
207 EXPECT_EQ(CacheHits, 0U);
208 EXPECT_EQ(Storage.size(), 2U);
213 [&](llvm::StringRef) {
return &MSS; });
215 ASSERT_TRUE(Idx.blockUntilIdleForTest());
217 EXPECT_EQ(CacheHits, 2U);
218 EXPECT_EQ(Storage.size(), 2U);
221 EXPECT_NE(ShardHeader,
nullptr);
223 *ShardHeader->Symbols,
224 UnorderedElementsAre(Named(
"common"), Named(
"A_CC"),
225 AllOf(Named(
"f_b"), Declared(), Not(Defined()))));
226 for (
const auto &
Ref : *ShardHeader->Refs)
227 EXPECT_THAT(
Ref.second,
228 UnorderedElementsAre(FileURI(
"unittest:///root/A.h")));
231 EXPECT_NE(ShardSource,
nullptr);
232 EXPECT_THAT(*ShardSource->Symbols,
233 UnorderedElementsAre(Named(
"g"), Named(
"B_CC")));
234 for (
const auto &
Ref : *ShardSource->Refs)
235 EXPECT_THAT(
Ref.second,
236 UnorderedElementsAre(FileURI(
"unittest:///root/A.cc")));
243 *ShardHeader->Relations,
244 UnorderedElementsAre(
Relation{A, index::SymbolRole::RelationBaseOf, B}));
246 EXPECT_EQ(ShardSource->Relations->size(), 0u);
258 std::string A_CC = "#include \"A.h\"\nvoid g() { (void)common; }";
261 llvm::StringMap<std::string> Storage;
262 size_t CacheHits = 0;
265 tooling::CompileCommand Cmd;
266 Cmd.Filename =
testPath(
"root/A.cc");
268 Cmd.CommandLine = {
"clang++",
testPath(
"root/A.cc")};
272 [&](llvm::StringRef) {
return &MSS; });
274 ASSERT_TRUE(Idx.blockUntilIdleForTest());
278 EXPECT_TRUE(ShardSource->Sources);
279 EXPECT_EQ(ShardSource->Sources->size(), 2U);
281 ShardSource->Sources->lookup(
"unittest:///root/A.cc").DirectIncludes,
282 UnorderedElementsAre(
"unittest:///root/A.h"));
283 EXPECT_NE(ShardSource->Sources->lookup(
"unittest:///root/A.cc").Digest,
285 EXPECT_THAT(ShardSource->Sources->lookup(
"unittest:///root/A.h"),
289 EXPECT_TRUE(ShardHeader->Sources);
290 EXPECT_EQ(ShardHeader->Sources->size(), 2U);
292 ShardHeader->Sources->lookup(
"unittest:///root/A.h").DirectIncludes,
293 UnorderedElementsAre(
"unittest:///root/B.h"));
294 EXPECT_NE(ShardHeader->Sources->lookup(
"unittest:///root/A.h").Digest,
296 EXPECT_THAT(ShardHeader->Sources->lookup(
"unittest:///root/B.h"),
308 "#include \"A.h\"\nvoid g() { (void)common; }";
310 llvm::StringMap<std::string> Storage;
311 size_t CacheHits = 0;
314 tooling::CompileCommand Cmd;
315 Cmd.Filename =
testPath(
"root/A.cc");
317 Cmd.CommandLine = {
"clang++",
testPath(
"root/A.cc")};
322 [&](llvm::StringRef) {
return &MSS; });
324 ASSERT_TRUE(Idx.blockUntilIdleForTest());
337 [&](llvm::StringRef) {
return &MSS; });
339 ASSERT_TRUE(Idx.blockUntilIdleForTest());
341 EXPECT_EQ(CacheHits, 2U);
345 EXPECT_NE(ShardHeader,
nullptr);
346 EXPECT_THAT(*ShardHeader->Symbols, Contains(Named(
"A_CCnew")));
350 "#include \"A.h\"\nvoid g() { (void)common; }\nvoid f_b() {}";
355 [&](llvm::StringRef) {
return &MSS; });
357 ASSERT_TRUE(Idx.blockUntilIdleForTest());
359 EXPECT_EQ(CacheHits, 2U);
363 EXPECT_NE(ShardHeader,
nullptr);
364 EXPECT_THAT(*ShardHeader->Symbols, Contains(Named(
"A_CCnew")));
366 EXPECT_NE(ShardSource,
nullptr);
367 EXPECT_THAT(*ShardSource->Symbols,
368 Contains(AllOf(Named(
"f_b"), Declared(), Defined())));
382 "#include \"B.h\"\nvoid g() { (void)common; }";
384 llvm::StringMap<std::string> Storage;
385 size_t CacheHits = 0;
388 tooling::CompileCommand Cmd;
389 Cmd.Filename =
testPath(
"root/A.cc");
391 Cmd.CommandLine = {
"clang++",
testPath(
"root/A.cc")};
396 [&](llvm::StringRef) {
return &MSS; });
398 ASSERT_TRUE(Idx.blockUntilIdleForTest());
400 EXPECT_THAT(Storage.keys(),
404 EXPECT_NE(ShardHeader,
nullptr);
405 EXPECT_TRUE(ShardHeader->Symbols->empty());
412 [&](llvm::StringRef) {
return &MSS; });
414 ASSERT_TRUE(Idx.blockUntilIdleForTest());
416 EXPECT_EQ(CacheHits, 3U);
428 [&](llvm::StringRef) {
return &MSS; });
430 ASSERT_TRUE(Idx.blockUntilIdleForTest());
432 EXPECT_EQ(CacheHits, 3U);
434 EXPECT_NE(ShardHeader,
nullptr);
435 EXPECT_THAT(*ShardHeader->Symbols,
436 Contains(AllOf(Named(
"new_func"), Declared(), Not(Defined()))));
441 llvm::StringMap<std::string> Storage;
442 size_t CacheHits = 0;
446 [&](llvm::StringRef) {
return &MSS; });
448 tooling::CompileCommand Cmd;
450 Cmd.Filename =
"../A.cc";
451 Cmd.Directory =
testPath(
"root/build");
452 Cmd.CommandLine = {
"clang++",
"../A.cc"};
456 Cmd.Filename =
"./B.cc";
458 Cmd.CommandLine = {
"clang++",
"./B.cc"};
461 ASSERT_TRUE(Idx.blockUntilIdleForTest());
463 EXPECT_FALSE(AbsPath.contains(
"./")) << AbsPath;
464 EXPECT_FALSE(AbsPath.contains(
"../")) << AbsPath;
470 llvm::StringMap<std::string> Storage;
471 size_t CacheHits = 0;
475 [&](llvm::StringRef) {
return &MSS; });
477 tooling::CompileCommand Cmd;
484 #include "not_found_header.h" 488 Cmd.Filename = "../A.cc";
490 Cmd.CommandLine = {
"clang++",
"../A.cc"};
492 ASSERT_TRUE(Idx.blockUntilIdleForTest());
499 EXPECT_THAT(*Shard->Symbols, UnorderedElementsAre(Named(
"foo")));
500 EXPECT_THAT(Shard->Sources->keys(),
501 UnorderedElementsAre(
"unittest:///A.cc",
"unittest:///A.h",
503 EXPECT_THAT(Shard->Sources->lookup(
"unittest:///A.cc"), HadErrors());
508 EXPECT_THAT(*Shard->Symbols, UnorderedElementsAre(Named(
"foo")));
509 EXPECT_THAT(Shard->Sources->keys(),
510 UnorderedElementsAre(
"unittest:///A.h"));
511 EXPECT_THAT(Shard->Sources->lookup(
"unittest:///A.h"), HadErrors());
516 EXPECT_THAT(*Shard->Symbols, UnorderedElementsAre(Named(
"asdf")));
517 EXPECT_THAT(Shard->Sources->keys(),
518 UnorderedElementsAre(
"unittest:///B.h",
"unittest:///C.h"));
519 EXPECT_THAT(Shard->Sources->lookup(
"unittest:///B.h"), HadErrors());
524 EXPECT_THAT(*Shard->Symbols, UnorderedElementsAre());
525 EXPECT_THAT(Shard->Sources->keys(),
526 UnorderedElementsAre(
"unittest:///C.h"));
527 EXPECT_THAT(Shard->Sources->lookup(
"unittest:///C.h"), HadErrors());
533 llvm::StringMap<std::string> Storage;
534 size_t CacheHits = 0;
539 [&](llvm::StringRef) {
return &MSS; });
541 tooling::CompileCommand Cmd;
544 Cmd.Filename =
"../A.cc";
546 Cmd.CommandLine = {
"clang++",
"../A.cc",
"-fsyntax-only"};
547 CDB.setCompileCommand(
testPath(
"build/../A.cc"), Cmd);
548 ASSERT_TRUE(Idx.blockUntilIdleForTest());
550 EXPECT_THAT(Storage.keys(), ElementsAre(
testPath(
"A.cc"),
testPath(
"A.h")));
556 EXPECT_EQ(CmdStored.CommandLine, Cmd.CommandLine);
557 EXPECT_EQ(CmdStored.Directory, Cmd.Directory);
562 Cmd.CommandLine = {
"clang++",
"../A.cc",
"-Dfoo",
"-fsyntax-only"};
563 CDB.setCompileCommand(
testPath(
"build/../A.cc"), Cmd);
564 ASSERT_TRUE(Idx.blockUntilIdleForTest());
570 EXPECT_EQ(CmdStored.CommandLine, Cmd.CommandLine);
571 EXPECT_EQ(CmdStored.Directory, Cmd.Directory);
579 Rebuilder(&Target, &Source, 10) {
587 VersionStorage.push_back(
"Sym" + std::to_string(++VersionCounter));
588 TestSymbol.Name = VersionStorage.back();
591 Source.update(
"", llvm::make_unique<SymbolSlab>(std::move(SB).build()),
592 nullptr,
nullptr,
false);
596 std::string ReadName;
598 Req.
IDs.insert(TestSymbol.ID);
599 Target.lookup(Req, [&](
const Symbol &S) { ReadName = S.
Name; });
601 return ReadName == VersionStorage.back();
609 unsigned VersionCounter = 0;
614 for (
unsigned I = 0; I < Rebuilder.TUsBeforeFirstBuild - 1; ++I)
615 EXPECT_FALSE(checkRebuild([&] { Rebuilder.indexedTU(); }));
616 EXPECT_TRUE(checkRebuild([&] { Rebuilder.indexedTU(); }));
617 for (
unsigned I = 0; I < Rebuilder.TUsBeforeRebuild - 1; ++I)
618 EXPECT_FALSE(checkRebuild([&] { Rebuilder.indexedTU(); }));
619 EXPECT_TRUE(checkRebuild([&] { Rebuilder.indexedTU(); }));
623 Rebuilder.startLoading();
624 Rebuilder.loadedShard(10);
625 Rebuilder.loadedShard(20);
626 EXPECT_TRUE(checkRebuild([&] { Rebuilder.doneLoading(); }));
629 Rebuilder.startLoading();
630 EXPECT_FALSE(checkRebuild([&] { Rebuilder.doneLoading(); }));
633 Rebuilder.startLoading();
634 Rebuilder.loadedShard(1);
635 Rebuilder.startLoading();
636 Rebuilder.loadedShard(1);
637 EXPECT_FALSE(checkRebuild([&] { Rebuilder.doneLoading(); }));
638 Rebuilder.loadedShard(1);
639 EXPECT_TRUE(checkRebuild([&] { Rebuilder.doneLoading(); }));
642 Rebuilder.startLoading();
643 for (
unsigned I = 0; I < 3 * Rebuilder.TUsBeforeRebuild; ++I)
644 EXPECT_FALSE(checkRebuild([&] { Rebuilder.indexedTU(); }));
646 EXPECT_TRUE(checkRebuild([&] { Rebuilder.doneLoading(); }));
649 TEST(BackgroundQueueTest, Priority) {
654 std::atomic<unsigned> HiRan(0), LoRan(0);
663 Q.
append(std::vector<BackgroundQueue::Task>(30, Lo));
664 for (
unsigned I = 0; I < 30; ++I)
668 for (
unsigned I = 0; I < 5; ++I)
669 ThreadPool.
runAsync(
"worker", [&] { Q.work(); });
673 Q.
append(std::vector<BackgroundQueue::Task>(2, Hi));
677 EXPECT_GE(HiRan, 10u);
678 EXPECT_EQ(LoRan, 0u);
681 TEST(BackgroundQueueTest, Boost) {
682 std::string Sequence;
696 EXPECT_EQ(
"BA", Sequence) <<
"priority order";
704 EXPECT_EQ(
"AB", Sequence) <<
"A was boosted before enqueueing";
712 EXPECT_EQ(
"AB", Sequence) <<
"A was boosted after enqueueing";
::testing::Matcher< const RefSlab & > RefsAre(std::vector<::testing::Matcher< Ref >> Matchers)
Some operations such as code completion produce a set of candidates.
Represents a relation between two symbols.
llvm::StringMap< std::string > Files
std::array< uint8_t, 8 > FileDigest
A container of Symbols from several source files.
llvm::Error storeShard(llvm::StringRef ShardIdentifier, IndexFileOut Shard) const override
llvm::DenseSet< SymbolID > IDs
bool checkRebuild(std::function< void()> Action)
Represents a symbol occurrence in the source file.
void insert(const Symbol &S)
Adds a symbol, overwriting any existing one with the same ID.
llvm::Expected< IndexFileIn > readIndexFile(llvm::StringRef Data)
llvm::StringSet AccessedPaths
SymbolSlab::Builder is a mutable container that can 'freeze' to SymbolSlab.
SymbolID ID
The ID of the symbol.
void boost(llvm::StringRef Tag, unsigned NewPriority)
TEST_F(BackgroundIndexTest, NoCrashOnErrorFile)
static void preventThreadStarvationInTests()
void runAsync(const llvm::Twine &Name, llvm::unique_function< void()> Action)
TEST(BackgroundQueueTest, Priority)
std::string testPath(PathRef File)
llvm::unique_function< void()> Action
MemIndex is a naive in-memory index suitable for a small set of symbols.
SymbolSlab runFuzzyFind(const SymbolIndex &Index, llvm::StringRef Query)
Runs tasks on separate (detached) threads and wait for all tasks to finish.
void setCompileCommand(PathRef File, llvm::Optional< tooling::CompileCommand > CompilationCommand)
Sets or clears the compilation command for a particular file.
BackgroundIndexRebuilderTest()
The class presents a C++ symbol, e.g.
MemoryShardStorage(llvm::StringMap< std::string > &Storage, size_t &CacheHits)
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
llvm::StringRef Name
The unqualified name of the symbol, e.g. "bar" (for ns::bar).
const Symbol & findSymbol(const SymbolSlab &Slab, llvm::StringRef QName)
std::unique_ptr< IndexFileIn > loadShard(llvm::StringRef ShardIdentifier) const override
BackgroundIndexRebuilder Rebuilder
void append(std::vector< Task >)
std::deque< std::string > VersionStorage
A work item on the thread pool's queue.
Wraps another compilation database, and supports overriding the commands using an in-memory mapping...
static Context empty()
Returns an empty root context that contains no data.
std::array< uint8_t, 20 > SymbolID
RefSlab getRefs(const SymbolIndex &Index, SymbolID ID)
void work(std::function< void()> OnIdle=nullptr)