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 std::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")));
242 EXPECT_THAT(*ShardHeader->Relations,
245 EXPECT_EQ(ShardSource->Relations->size(), 0u);
257 std::string A_CC = "#include \"A.h\"\nvoid g() { (void)common; }";
260 llvm::StringMap<std::string> Storage;
261 size_t CacheHits = 0;
264 tooling::CompileCommand Cmd;
265 Cmd.Filename =
testPath(
"root/A.cc");
267 Cmd.CommandLine = {
"clang++",
testPath(
"root/A.cc")};
271 [&](llvm::StringRef) {
return &MSS; });
273 ASSERT_TRUE(Idx.blockUntilIdleForTest());
277 EXPECT_TRUE(ShardSource->Sources);
278 EXPECT_EQ(ShardSource->Sources->size(), 2U);
280 ShardSource->Sources->lookup(
"unittest:///root/A.cc").DirectIncludes,
281 UnorderedElementsAre(
"unittest:///root/A.h"));
282 EXPECT_NE(ShardSource->Sources->lookup(
"unittest:///root/A.cc").Digest,
284 EXPECT_THAT(ShardSource->Sources->lookup(
"unittest:///root/A.h"),
288 EXPECT_TRUE(ShardHeader->Sources);
289 EXPECT_EQ(ShardHeader->Sources->size(), 2U);
291 ShardHeader->Sources->lookup(
"unittest:///root/A.h").DirectIncludes,
292 UnorderedElementsAre(
"unittest:///root/B.h"));
293 EXPECT_NE(ShardHeader->Sources->lookup(
"unittest:///root/A.h").Digest,
295 EXPECT_THAT(ShardHeader->Sources->lookup(
"unittest:///root/B.h"),
307 "#include \"A.h\"\nvoid g() { (void)common; }";
309 llvm::StringMap<std::string> Storage;
310 size_t CacheHits = 0;
313 tooling::CompileCommand Cmd;
314 Cmd.Filename =
testPath(
"root/A.cc");
316 Cmd.CommandLine = {
"clang++",
testPath(
"root/A.cc")};
321 [&](llvm::StringRef) {
return &MSS; });
323 ASSERT_TRUE(Idx.blockUntilIdleForTest());
336 [&](llvm::StringRef) {
return &MSS; });
338 ASSERT_TRUE(Idx.blockUntilIdleForTest());
340 EXPECT_EQ(CacheHits, 2U);
344 EXPECT_NE(ShardHeader,
nullptr);
345 EXPECT_THAT(*ShardHeader->Symbols, Contains(Named(
"A_CCnew")));
349 "#include \"A.h\"\nvoid g() { (void)common; }\nvoid f_b() {}";
354 [&](llvm::StringRef) {
return &MSS; });
356 ASSERT_TRUE(Idx.blockUntilIdleForTest());
358 EXPECT_EQ(CacheHits, 2U);
362 EXPECT_NE(ShardHeader,
nullptr);
363 EXPECT_THAT(*ShardHeader->Symbols, Contains(Named(
"A_CCnew")));
365 EXPECT_NE(ShardSource,
nullptr);
366 EXPECT_THAT(*ShardSource->Symbols,
367 Contains(AllOf(Named(
"f_b"), Declared(), Defined())));
381 "#include \"B.h\"\nvoid g() { (void)common; }";
383 llvm::StringMap<std::string> Storage;
384 size_t CacheHits = 0;
387 tooling::CompileCommand Cmd;
388 Cmd.Filename =
testPath(
"root/A.cc");
390 Cmd.CommandLine = {
"clang++",
testPath(
"root/A.cc")};
395 [&](llvm::StringRef) {
return &MSS; });
397 ASSERT_TRUE(Idx.blockUntilIdleForTest());
399 EXPECT_THAT(Storage.keys(),
403 EXPECT_NE(ShardHeader,
nullptr);
404 EXPECT_TRUE(ShardHeader->Symbols->empty());
411 [&](llvm::StringRef) {
return &MSS; });
413 ASSERT_TRUE(Idx.blockUntilIdleForTest());
415 EXPECT_EQ(CacheHits, 3U);
427 [&](llvm::StringRef) {
return &MSS; });
429 ASSERT_TRUE(Idx.blockUntilIdleForTest());
431 EXPECT_EQ(CacheHits, 3U);
433 EXPECT_NE(ShardHeader,
nullptr);
434 EXPECT_THAT(*ShardHeader->Symbols,
435 Contains(AllOf(Named(
"new_func"), Declared(), Not(Defined()))));
440 llvm::StringMap<std::string> Storage;
441 size_t CacheHits = 0;
445 [&](llvm::StringRef) {
return &MSS; });
447 tooling::CompileCommand Cmd;
449 Cmd.Filename =
"../A.cc";
450 Cmd.Directory =
testPath(
"root/build");
451 Cmd.CommandLine = {
"clang++",
"../A.cc"};
455 Cmd.Filename =
"./B.cc";
457 Cmd.CommandLine = {
"clang++",
"./B.cc"};
460 ASSERT_TRUE(Idx.blockUntilIdleForTest());
462 EXPECT_FALSE(AbsPath.contains(
"./")) << AbsPath;
463 EXPECT_FALSE(AbsPath.contains(
"../")) << AbsPath;
469 llvm::StringMap<std::string> Storage;
470 size_t CacheHits = 0;
474 [&](llvm::StringRef) {
return &MSS; });
476 tooling::CompileCommand Cmd;
483 #include "not_found_header.h" 487 Cmd.Filename = "../A.cc";
489 Cmd.CommandLine = {
"clang++",
"../A.cc"};
491 ASSERT_TRUE(Idx.blockUntilIdleForTest());
498 EXPECT_THAT(*Shard->Symbols, UnorderedElementsAre(Named(
"foo")));
499 EXPECT_THAT(Shard->Sources->keys(),
500 UnorderedElementsAre(
"unittest:///A.cc",
"unittest:///A.h",
502 EXPECT_THAT(Shard->Sources->lookup(
"unittest:///A.cc"), HadErrors());
507 EXPECT_THAT(*Shard->Symbols, UnorderedElementsAre(Named(
"foo")));
508 EXPECT_THAT(Shard->Sources->keys(),
509 UnorderedElementsAre(
"unittest:///A.h"));
510 EXPECT_THAT(Shard->Sources->lookup(
"unittest:///A.h"), HadErrors());
515 EXPECT_THAT(*Shard->Symbols, UnorderedElementsAre(Named(
"asdf")));
516 EXPECT_THAT(Shard->Sources->keys(),
517 UnorderedElementsAre(
"unittest:///B.h",
"unittest:///C.h"));
518 EXPECT_THAT(Shard->Sources->lookup(
"unittest:///B.h"), HadErrors());
523 EXPECT_THAT(*Shard->Symbols, UnorderedElementsAre());
524 EXPECT_THAT(Shard->Sources->keys(),
525 UnorderedElementsAre(
"unittest:///C.h"));
526 EXPECT_THAT(Shard->Sources->lookup(
"unittest:///C.h"), HadErrors());
532 llvm::StringMap<std::string> Storage;
533 size_t CacheHits = 0;
537 [&](llvm::StringRef) {
return &MSS; });
539 tooling::CompileCommand Cmd;
542 Cmd.Filename =
"../A.cc";
544 Cmd.CommandLine = {
"clang++",
"../A.cc",
"-fsyntax-only"};
546 ASSERT_TRUE(Idx.blockUntilIdleForTest());
548 EXPECT_THAT(Storage.keys(), ElementsAre(
testPath(
"A.cc"),
testPath(
"A.h")));
554 EXPECT_EQ(CmdStored.CommandLine, Cmd.CommandLine);
555 EXPECT_EQ(CmdStored.Directory, Cmd.Directory);
560 Cmd.CommandLine = {
"clang++",
"../A.cc",
"-Dfoo",
"-fsyntax-only"};
562 ASSERT_TRUE(Idx.blockUntilIdleForTest());
568 EXPECT_EQ(CmdStored.CommandLine, Cmd.CommandLine);
569 EXPECT_EQ(CmdStored.Directory, Cmd.Directory);
576 : Target(std::make_unique<
MemIndex>()),
577 Rebuilder(&Target, &Source, 10) {
585 VersionStorage.push_back(
"Sym" + std::to_string(++VersionCounter));
586 TestSymbol.Name = VersionStorage.back();
589 Source.update(
"", std::make_unique<SymbolSlab>(std::move(SB).build()),
590 nullptr,
nullptr,
false);
594 std::string ReadName;
596 Req.
IDs.insert(TestSymbol.ID);
597 Target.lookup(Req, [&](
const Symbol &S) { ReadName = S.
Name; });
599 return ReadName == VersionStorage.back();
607 unsigned VersionCounter = 0;
612 for (
unsigned I = 0; I < Rebuilder.TUsBeforeFirstBuild - 1; ++I)
613 EXPECT_FALSE(checkRebuild([&] { Rebuilder.indexedTU(); }));
614 EXPECT_TRUE(checkRebuild([&] { Rebuilder.indexedTU(); }));
615 for (
unsigned I = 0; I < Rebuilder.TUsBeforeRebuild - 1; ++I)
616 EXPECT_FALSE(checkRebuild([&] { Rebuilder.indexedTU(); }));
617 EXPECT_TRUE(checkRebuild([&] { Rebuilder.indexedTU(); }));
621 Rebuilder.startLoading();
622 Rebuilder.loadedShard(10);
623 Rebuilder.loadedShard(20);
624 EXPECT_TRUE(checkRebuild([&] { Rebuilder.doneLoading(); }));
627 Rebuilder.startLoading();
628 EXPECT_FALSE(checkRebuild([&] { Rebuilder.doneLoading(); }));
631 Rebuilder.startLoading();
632 Rebuilder.loadedShard(1);
633 Rebuilder.startLoading();
634 Rebuilder.loadedShard(1);
635 EXPECT_FALSE(checkRebuild([&] { Rebuilder.doneLoading(); }));
636 Rebuilder.loadedShard(1);
637 EXPECT_TRUE(checkRebuild([&] { Rebuilder.doneLoading(); }));
640 Rebuilder.startLoading();
641 for (
unsigned I = 0; I < 3 * Rebuilder.TUsBeforeRebuild; ++I)
642 EXPECT_FALSE(checkRebuild([&] { Rebuilder.indexedTU(); }));
644 EXPECT_TRUE(checkRebuild([&] { Rebuilder.doneLoading(); }));
647 TEST(BackgroundQueueTest, Priority) {
652 std::atomic<unsigned> HiRan(0), LoRan(0);
661 Q.
append(std::vector<BackgroundQueue::Task>(30, Lo));
662 for (
unsigned I = 0; I < 30; ++I)
666 for (
unsigned I = 0; I < 5; ++I)
667 ThreadPool.
runAsync(
"worker", [&] { Q.work(); });
671 Q.
append(std::vector<BackgroundQueue::Task>(2, Hi));
675 EXPECT_GE(HiRan, 10u);
676 EXPECT_EQ(LoRan, 0u);
679 TEST(BackgroundQueueTest, Boost) {
680 std::string Sequence;
694 EXPECT_EQ(
"BA", Sequence) <<
"priority order";
702 EXPECT_EQ(
"AB", Sequence) <<
"A was boosted before enqueueing";
710 EXPECT_EQ(
"AB", Sequence) <<
"A was boosted after enqueueing";
::testing::Matcher< const RefSlab & > RefsAre(std::vector<::testing::Matcher< Ref >> Matchers)
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)