1 #include "CompileCommands.h" 2 #include "Config.h" 3 #include "Headers.h" 4 #include "SyncAPI.h" 5 #include "TestFS.h" 6 #include "TestTU.h" 7 #include "index/Background.h" 8 #include "index/BackgroundRebuild.h" 9 #include "index/MemIndex.h" 10 #include "clang/Tooling/ArgumentsAdjusters.h" 11 #include "clang/Tooling/CompilationDatabase.h" 12 #include "llvm/ADT/STLExtras.h" 13 #include "llvm/Support/ScopedPrinter.h" 14 #include "gmock/gmock.h" 15 #include "gtest/gtest.h" 16 #include <deque> 17 #include <thread> 18 19 using ::testing::_; 20 using ::testing::AllOf; 21 using ::testing::Contains; 22 using ::testing::ElementsAre; 23 using ::testing::Not; 24 using ::testing::Pair; 25 using ::testing::UnorderedElementsAre; 26 27 namespace clang { 28 namespace clangd { 29 30 MATCHER_P(named, N, "") { return arg.Name == N; } 31 MATCHER_P(qName, N, "") { return (arg.Scope + arg.Name).str() == N; } 32 MATCHER(declared, "") { 33 return !StringRef(arg.CanonicalDeclaration.FileURI).empty(); 34 } 35 MATCHER(defined, "") { return !StringRef(arg.Definition.FileURI).empty(); } 36 MATCHER_P(fileURI, F, "") { return StringRef(arg.Location.FileURI) == F; } 37 ::testing::Matcher<const RefSlab &> 38 refsAre(std::vector<::testing::Matcher<Ref>> Matchers) { 39 return ElementsAre(::testing::Pair(_, UnorderedElementsAreArray(Matchers))); 40 } 41 // URI cannot be empty since it references keys in the IncludeGraph. 42 MATCHER(emptyIncludeNode, "") { 43 return arg.Flags == IncludeGraphNode::SourceFlag::None && !arg.URI.empty() && 44 arg.Digest == FileDigest{{0}} && arg.DirectIncludes.empty(); 45 } 46 47 MATCHER(hadErrors, "") { 48 return arg.Flags & IncludeGraphNode::SourceFlag::HadErrors; 49 } 50 51 MATCHER_P(numReferences, N, "") { return arg.References == N; } 52 53 class MemoryShardStorage : public BackgroundIndexStorage { 54 mutable std::mutex StorageMu; 55 llvm::StringMap<std::string> &Storage; 56 size_t &CacheHits; 57 58 public: 59 MemoryShardStorage(llvm::StringMap<std::string> &Storage, size_t &CacheHits) 60 : Storage(Storage), CacheHits(CacheHits) {} 61 llvm::Error storeShard(llvm::StringRef ShardIdentifier, 62 IndexFileOut Shard) const override { 63 std::lock_guard<std::mutex> Lock(StorageMu); 64 AccessedPaths.insert(ShardIdentifier); 65 Storage[ShardIdentifier] = llvm::to_string(Shard); 66 return llvm::Error::success(); 67 } 68 std::unique_ptr<IndexFileIn> 69 loadShard(llvm::StringRef ShardIdentifier) const override { 70 std::lock_guard<std::mutex> Lock(StorageMu); 71 AccessedPaths.insert(ShardIdentifier); 72 if (!Storage.contains(ShardIdentifier)) { 73 return nullptr; 74 } 75 auto IndexFile = 76 readIndexFile(Storage[ShardIdentifier], SymbolOrigin::Background); 77 if (!IndexFile) { 78 ADD_FAILURE() << "Error while reading " << ShardIdentifier << ':' 79 << IndexFile.takeError(); 80 return nullptr; 81 } 82 CacheHits++; 83 return std::make_unique<IndexFileIn>(std::move(*IndexFile)); 84 } 85 86 mutable llvm::StringSet<> AccessedPaths; 87 }; 88 89 class BackgroundIndexTest : public ::testing::Test { 90 protected: 91 BackgroundIndexTest() { BackgroundQueue::preventThreadStarvationInTests(); } 92 }; 93 94 TEST_F(BackgroundIndexTest, NoCrashOnErrorFile) { 95 MockFS FS; 96 FS.Files[testPath("root/A.cc")] = "error file"; 97 llvm::StringMap<std::string> Storage; 98 size_t CacheHits = 0; 99 MemoryShardStorage MSS(Storage, CacheHits); 100 OverlayCDB CDB(/*Base=*/nullptr); 101 BackgroundIndex Idx(FS, CDB, [&](llvm::StringRef) { return &MSS; }, 102 /*Opts=*/{}); 103 104 tooling::CompileCommand Cmd; 105 Cmd.Filename = testPath("root/A.cc"); 106 Cmd.Directory = testPath("root"); 107 Cmd.CommandLine = {"clang++", "-DA=1", testPath("root/A.cc")}; 108 CDB.setCompileCommand(testPath("root/A.cc"), Cmd); 109 110 ASSERT_TRUE(Idx.blockUntilIdleForTest()); 111 } 112 113 TEST_F(BackgroundIndexTest, Config) { 114 MockFS FS; 115 // Set up two identical TUs, foo and bar. 116 // They define foo::one and bar::one. 117 std::vector<tooling::CompileCommand> Cmds; 118 for (std::string Name : {"foo", "bar", "baz"}) { 119 std::string Filename = Name + ".cpp"; 120 std::string Header = Name + ".h"; 121 FS.Files[Filename] = "#include \"" + Header + "\""; 122 FS.Files[Header] = "namespace " + Name + " { int one; }"; 123 tooling::CompileCommand Cmd; 124 Cmd.Filename = Filename; 125 Cmd.Directory = testRoot(); 126 Cmd.CommandLine = {"clang++", Filename}; 127 Cmds.push_back(std::move(Cmd)); 128 } 129 // Context provider that installs a configuration mutating foo's command. 130 // This causes it to define foo::two instead of foo::one. 131 // It also disables indexing of baz entirely. 132 BackgroundIndex::Options Opts; 133 Opts.ContextProvider = [](PathRef P) { 134 Config C; 135 if (P.ends_with("foo.cpp")) 136 C.CompileFlags.Edits.push_back([](std::vector<std::string> &Argv) { 137 Argv = tooling::getInsertArgumentAdjuster("-Done=two")(Argv, ""); 138 }); 139 if (P.ends_with("baz.cpp")) 140 C.Index.Background = Config::BackgroundPolicy::Skip; 141 return Context::current().derive(Config::Key, std::move(C)); 142 }; 143 // Create the background index. 144 llvm::StringMap<std::string> Storage; 145 size_t CacheHits = 0; 146 MemoryShardStorage MSS(Storage, CacheHits); 147 // We need the CommandMangler, because that applies the config we're testing. 148 OverlayCDB CDB(/*Base=*/nullptr, /*FallbackFlags=*/{}, 149 CommandMangler::forTests()); 150 151 BackgroundIndex Idx( 152 FS, CDB, [&](llvm::StringRef) { return &MSS; }, std::move(Opts)); 153 // Index the two files. 154 for (auto &Cmd : Cmds) { 155 std::string FullPath = testPath(Cmd.Filename); 156 CDB.setCompileCommand(FullPath, std::move(Cmd)); 157 } 158 // Wait for both files to be indexed. 159 ASSERT_TRUE(Idx.blockUntilIdleForTest()); 160 EXPECT_THAT(runFuzzyFind(Idx, ""), 161 UnorderedElementsAre(qName("foo"), qName("foo::two"), 162 qName("bar"), qName("bar::one"))); 163 } 164 165 TEST_F(BackgroundIndexTest, IndexTwoFiles) { 166 MockFS FS; 167 // a.h yields different symbols when included by A.cc vs B.cc. 168 FS.Files[testPath("root/A.h")] = R"cpp( 169 void common(); 170 void f_b(); 171 #if A 172 class A_CC {}; 173 #else 174 class B_CC{}; 175 #endif 176 )cpp"; 177 FS.Files[testPath("root/A.cc")] = 178 "#include \"A.h\"\nstatic void g() { (void)common; }"; 179 FS.Files[testPath("root/B.cc")] = 180 R"cpp( 181 #define A 0 182 #include "A.h" 183 void f_b() { 184 (void)common; 185 (void)common; 186 (void)common; 187 (void)common; 188 })cpp"; 189 llvm::StringMap<std::string> Storage; 190 size_t CacheHits = 0; 191 MemoryShardStorage MSS(Storage, CacheHits); 192 OverlayCDB CDB(/*Base=*/nullptr); 193 BackgroundIndex::Options Opts; 194 BackgroundIndex Idx( 195 FS, CDB, [&](llvm::StringRef) { return &MSS; }, Opts); 196 197 tooling::CompileCommand Cmd; 198 Cmd.Filename = testPath("root/A.cc"); 199 Cmd.Directory = testPath("root"); 200 Cmd.CommandLine = {"clang++", "-DA=1", testPath("root/A.cc")}; 201 CDB.setCompileCommand(testPath("root/A.cc"), Cmd); 202 203 ASSERT_TRUE(Idx.blockUntilIdleForTest()); 204 EXPECT_THAT(runFuzzyFind(Idx, ""), 205 UnorderedElementsAre(AllOf(named("common"), numReferences(1U)), 206 AllOf(named("A_CC"), numReferences(0U)), 207 AllOf(named("g"), numReferences(1U)), 208 AllOf(named("f_b"), declared(), 209 Not(defined()), numReferences(0U)))); 210 211 Cmd.Filename = testPath("root/B.cc"); 212 Cmd.CommandLine = {"clang++", Cmd.Filename}; 213 CDB.setCompileCommand(testPath("root/B.cc"), Cmd); 214 215 ASSERT_TRUE(Idx.blockUntilIdleForTest()); 216 // B_CC is dropped as we don't collect symbols from A.h in this compilation. 217 EXPECT_THAT(runFuzzyFind(Idx, ""), 218 UnorderedElementsAre(AllOf(named("common"), numReferences(5U)), 219 AllOf(named("A_CC"), numReferences(0U)), 220 AllOf(named("g"), numReferences(1U)), 221 AllOf(named("f_b"), declared(), defined(), 222 numReferences(1U)))); 223 224 auto Syms = runFuzzyFind(Idx, "common"); 225 EXPECT_THAT(Syms, UnorderedElementsAre(named("common"))); 226 auto Common = *Syms.begin(); 227 EXPECT_THAT(getRefs(Idx, Common.ID), 228 refsAre({fileURI("unittest:///root/A.h"), 229 fileURI("unittest:///root/A.cc"), 230 fileURI("unittest:///root/B.cc"), 231 fileURI("unittest:///root/B.cc"), 232 fileURI("unittest:///root/B.cc"), 233 fileURI("unittest:///root/B.cc")})); 234 } 235 236 TEST_F(BackgroundIndexTest, MainFileRefs) { 237 MockFS FS; 238 FS.Files[testPath("root/A.h")] = R"cpp( 239 void header_sym(); 240 )cpp"; 241 FS.Files[testPath("root/A.cc")] = 242 "#include \"A.h\"\nstatic void main_sym() { (void)header_sym; }"; 243 244 llvm::StringMap<std::string> Storage; 245 size_t CacheHits = 0; 246 MemoryShardStorage MSS(Storage, CacheHits); 247 OverlayCDB CDB(/*Base=*/nullptr); 248 BackgroundIndex::Options Opts; 249 BackgroundIndex Idx( 250 FS, CDB, [&](llvm::StringRef) { return &MSS; }, Opts); 251 252 tooling::CompileCommand Cmd; 253 Cmd.Filename = testPath("root/A.cc"); 254 Cmd.Directory = testPath("root"); 255 Cmd.CommandLine = {"clang++", testPath("root/A.cc")}; 256 CDB.setCompileCommand(testPath("root/A.cc"), Cmd); 257 258 ASSERT_TRUE(Idx.blockUntilIdleForTest()); 259 EXPECT_THAT( 260 runFuzzyFind(Idx, ""), 261 UnorderedElementsAre(AllOf(named("header_sym"), numReferences(1U)), 262 AllOf(named("main_sym"), numReferences(1U)))); 263 } 264 265 TEST_F(BackgroundIndexTest, ShardStorageTest) { 266 MockFS FS; 267 FS.Files[testPath("root/A.h")] = R"cpp( 268 void common(); 269 void f_b(); 270 class A_CC {}; 271 )cpp"; 272 FS.Files[testPath("root/A.cc")] = R"cpp( 273 #include "A.h" 274 void g() { (void)common; } 275 class B_CC : public A_CC {}; 276 )cpp"; 277 278 llvm::StringMap<std::string> Storage; 279 size_t CacheHits = 0; 280 MemoryShardStorage MSS(Storage, CacheHits); 281 282 tooling::CompileCommand Cmd; 283 Cmd.Filename = testPath("root/A.cc"); 284 Cmd.Directory = testPath("root"); 285 Cmd.CommandLine = {"clang++", testPath("root/A.cc")}; 286 // Check nothing is loaded from Storage, but A.cc and A.h has been stored. 287 { 288 OverlayCDB CDB(/*Base=*/nullptr); 289 BackgroundIndex Idx(FS, CDB, [&](llvm::StringRef) { return &MSS; }, 290 /*Opts=*/{}); 291 CDB.setCompileCommand(testPath("root/A.cc"), Cmd); 292 ASSERT_TRUE(Idx.blockUntilIdleForTest()); 293 } 294 EXPECT_EQ(CacheHits, 0U); 295 EXPECT_EQ(Storage.size(), 2U); 296 297 { 298 OverlayCDB CDB(/*Base=*/nullptr); 299 BackgroundIndex Idx(FS, CDB, [&](llvm::StringRef) { return &MSS; }, 300 /*Opts=*/{}); 301 CDB.setCompileCommand(testPath("root/A.cc"), Cmd); 302 ASSERT_TRUE(Idx.blockUntilIdleForTest()); 303 } 304 EXPECT_EQ(CacheHits, 2U); // Check both A.cc and A.h loaded from cache. 305 EXPECT_EQ(Storage.size(), 2U); 306 307 auto ShardHeader = MSS.loadShard(testPath("root/A.h")); 308 EXPECT_NE(ShardHeader, nullptr); 309 EXPECT_THAT( 310 *ShardHeader->Symbols, 311 UnorderedElementsAre(named("common"), named("A_CC"), 312 AllOf(named("f_b"), declared(), Not(defined())))); 313 for (const auto &Ref : *ShardHeader->Refs) 314 EXPECT_THAT(Ref.second, 315 UnorderedElementsAre(fileURI("unittest:///root/A.h"))); 316 317 auto ShardSource = MSS.loadShard(testPath("root/A.cc")); 318 EXPECT_NE(ShardSource, nullptr); 319 EXPECT_THAT(*ShardSource->Symbols, 320 UnorderedElementsAre(named("g"), named("B_CC"))); 321 for (const auto &Ref : *ShardSource->Refs) 322 EXPECT_THAT(Ref.second, 323 UnorderedElementsAre(fileURI("unittest:///root/A.cc"))); 324 325 // The BaseOf relationship between A_CC and B_CC is stored in both the file 326 // containing the definition of the subject (A_CC) and the file containing 327 // the definition of the object (B_CC). 328 SymbolID A = findSymbol(*ShardHeader->Symbols, "A_CC").ID; 329 SymbolID B = findSymbol(*ShardSource->Symbols, "B_CC").ID; 330 EXPECT_THAT(*ShardHeader->Relations, 331 UnorderedElementsAre(Relation{A, RelationKind::BaseOf, B})); 332 EXPECT_THAT(*ShardSource->Relations, 333 UnorderedElementsAre(Relation{A, RelationKind::BaseOf, B})); 334 } 335 336 TEST_F(BackgroundIndexTest, DirectIncludesTest) { 337 MockFS FS; 338 FS.Files[testPath("root/B.h")] = ""; 339 FS.Files[testPath("root/A.h")] = R"cpp( 340 #include "B.h" 341 void common(); 342 void f_b(); 343 class A_CC {}; 344 )cpp"; 345 FS.Files[testPath("root/A.cc")] = 346 "#include \"A.h\"\nvoid g() { (void)common; }"; 347 348 llvm::StringMap<std::string> Storage; 349 size_t CacheHits = 0; 350 MemoryShardStorage MSS(Storage, CacheHits); 351 352 tooling::CompileCommand Cmd; 353 Cmd.Filename = testPath("root/A.cc"); 354 Cmd.Directory = testPath("root"); 355 Cmd.CommandLine = {"clang++", testPath("root/A.cc")}; 356 { 357 OverlayCDB CDB(/*Base=*/nullptr); 358 BackgroundIndex Idx(FS, CDB, [&](llvm::StringRef) { return &MSS; }, 359 /*Opts=*/{}); 360 CDB.setCompileCommand(testPath("root/A.cc"), Cmd); 361 ASSERT_TRUE(Idx.blockUntilIdleForTest()); 362 } 363 364 auto ShardSource = MSS.loadShard(testPath("root/A.cc")); 365 EXPECT_TRUE(ShardSource->Sources); 366 EXPECT_EQ(ShardSource->Sources->size(), 2U); // A.cc, A.h 367 EXPECT_THAT( 368 ShardSource->Sources->lookup("unittest:///root/A.cc").DirectIncludes, 369 UnorderedElementsAre("unittest:///root/A.h")); 370 EXPECT_NE(ShardSource->Sources->lookup("unittest:///root/A.cc").Digest, 371 FileDigest{{0}}); 372 EXPECT_THAT(ShardSource->Sources->lookup("unittest:///root/A.h"), 373 emptyIncludeNode()); 374 375 auto ShardHeader = MSS.loadShard(testPath("root/A.h")); 376 EXPECT_TRUE(ShardHeader->Sources); 377 EXPECT_EQ(ShardHeader->Sources->size(), 2U); // A.h, B.h 378 EXPECT_THAT( 379 ShardHeader->Sources->lookup("unittest:///root/A.h").DirectIncludes, 380 UnorderedElementsAre("unittest:///root/B.h")); 381 EXPECT_NE(ShardHeader->Sources->lookup("unittest:///root/A.h").Digest, 382 FileDigest{{0}}); 383 EXPECT_THAT(ShardHeader->Sources->lookup("unittest:///root/B.h"), 384 emptyIncludeNode()); 385 } 386 387 TEST_F(BackgroundIndexTest, ShardStorageLoad) { 388 MockFS FS; 389 FS.Files[testPath("root/A.h")] = R"cpp( 390 void common(); 391 void f_b(); 392 class A_CC {}; 393 )cpp"; 394 FS.Files[testPath("root/A.cc")] = 395 "#include \"A.h\"\nvoid g() { (void)common; }"; 396 397 llvm::StringMap<std::string> Storage; 398 size_t CacheHits = 0; 399 MemoryShardStorage MSS(Storage, CacheHits); 400 401 tooling::CompileCommand Cmd; 402 Cmd.Filename = testPath("root/A.cc"); 403 Cmd.Directory = testPath("root"); 404 Cmd.CommandLine = {"clang++", testPath("root/A.cc")}; 405 // Check nothing is loaded from Storage, but A.cc and A.h has been stored. 406 { 407 OverlayCDB CDB(/*Base=*/nullptr); 408 BackgroundIndex Idx(FS, CDB, [&](llvm::StringRef) { return &MSS; }, 409 /*Opts=*/{}); 410 CDB.setCompileCommand(testPath("root/A.cc"), Cmd); 411 ASSERT_TRUE(Idx.blockUntilIdleForTest()); 412 } 413 414 // Change header. 415 FS.Files[testPath("root/A.h")] = R"cpp( 416 void common(); 417 void f_b(); 418 class A_CC {}; 419 class A_CCnew {}; 420 )cpp"; 421 { 422 OverlayCDB CDB(/*Base=*/nullptr); 423 BackgroundIndex Idx(FS, CDB, [&](llvm::StringRef) { return &MSS; }, 424 /*Opts=*/{}); 425 CDB.setCompileCommand(testPath("root/A.cc"), Cmd); 426 ASSERT_TRUE(Idx.blockUntilIdleForTest()); 427 } 428 EXPECT_EQ(CacheHits, 2U); // Check both A.cc and A.h loaded from cache. 429 430 // Check if the new symbol has arrived. 431 auto ShardHeader = MSS.loadShard(testPath("root/A.h")); 432 EXPECT_NE(ShardHeader, nullptr); 433 EXPECT_THAT(*ShardHeader->Symbols, Contains(named("A_CCnew"))); 434 435 // Change source. 436 FS.Files[testPath("root/A.cc")] = 437 "#include \"A.h\"\nvoid g() { (void)common; }\nvoid f_b() {}"; 438 { 439 CacheHits = 0; 440 OverlayCDB CDB(/*Base=*/nullptr); 441 BackgroundIndex Idx(FS, CDB, [&](llvm::StringRef) { return &MSS; }, 442 /*Opts=*/{}); 443 CDB.setCompileCommand(testPath("root/A.cc"), Cmd); 444 ASSERT_TRUE(Idx.blockUntilIdleForTest()); 445 } 446 EXPECT_EQ(CacheHits, 2U); // Check both A.cc and A.h loaded from cache. 447 448 // Check if the new symbol has arrived. 449 ShardHeader = MSS.loadShard(testPath("root/A.h")); 450 EXPECT_NE(ShardHeader, nullptr); 451 EXPECT_THAT(*ShardHeader->Symbols, Contains(named("A_CCnew"))); 452 auto ShardSource = MSS.loadShard(testPath("root/A.cc")); 453 EXPECT_NE(ShardSource, nullptr); 454 EXPECT_THAT(*ShardSource->Symbols, 455 Contains(AllOf(named("f_b"), declared(), defined()))); 456 } 457 458 TEST_F(BackgroundIndexTest, ShardStorageEmptyFile) { 459 MockFS FS; 460 FS.Files[testPath("root/A.h")] = R"cpp( 461 void common(); 462 void f_b(); 463 class A_CC {}; 464 )cpp"; 465 FS.Files[testPath("root/B.h")] = R"cpp( 466 #include "A.h" 467 )cpp"; 468 FS.Files[testPath("root/A.cc")] = 469 "#include \"B.h\"\nvoid g() { (void)common; }"; 470 471 llvm::StringMap<std::string> Storage; 472 size_t CacheHits = 0; 473 MemoryShardStorage MSS(Storage, CacheHits); 474 475 tooling::CompileCommand Cmd; 476 Cmd.Filename = testPath("root/A.cc"); 477 Cmd.Directory = testPath("root"); 478 Cmd.CommandLine = {"clang++", testPath("root/A.cc")}; 479 // Check that A.cc, A.h and B.h has been stored. 480 { 481 OverlayCDB CDB(/*Base=*/nullptr); 482 BackgroundIndex Idx(FS, CDB, [&](llvm::StringRef) { return &MSS; }, 483 /*Opts=*/{}); 484 CDB.setCompileCommand(testPath("root/A.cc"), Cmd); 485 ASSERT_TRUE(Idx.blockUntilIdleForTest()); 486 } 487 EXPECT_THAT(Storage.keys(), 488 UnorderedElementsAre(testPath("root/A.cc"), testPath("root/A.h"), 489 testPath("root/B.h"))); 490 auto ShardHeader = MSS.loadShard(testPath("root/B.h")); 491 EXPECT_NE(ShardHeader, nullptr); 492 EXPECT_TRUE(ShardHeader->Symbols->empty()); 493 494 // Check that A.cc, A.h and B.h has been loaded. 495 { 496 CacheHits = 0; 497 OverlayCDB CDB(/*Base=*/nullptr); 498 BackgroundIndex Idx(FS, CDB, [&](llvm::StringRef) { return &MSS; }, 499 /*Opts=*/{}); 500 CDB.setCompileCommand(testPath("root/A.cc"), Cmd); 501 ASSERT_TRUE(Idx.blockUntilIdleForTest()); 502 } 503 EXPECT_EQ(CacheHits, 3U); 504 505 // Update B.h to contain some symbols. 506 FS.Files[testPath("root/B.h")] = R"cpp( 507 #include "A.h" 508 void new_func(); 509 )cpp"; 510 // Check that B.h has been stored with new contents. 511 { 512 CacheHits = 0; 513 OverlayCDB CDB(/*Base=*/nullptr); 514 BackgroundIndex Idx(FS, CDB, [&](llvm::StringRef) { return &MSS; }, 515 /*Opts=*/{}); 516 CDB.setCompileCommand(testPath("root/A.cc"), Cmd); 517 ASSERT_TRUE(Idx.blockUntilIdleForTest()); 518 } 519 EXPECT_EQ(CacheHits, 3U); 520 ShardHeader = MSS.loadShard(testPath("root/B.h")); 521 EXPECT_NE(ShardHeader, nullptr); 522 EXPECT_THAT(*ShardHeader->Symbols, 523 Contains(AllOf(named("new_func"), declared(), Not(defined())))); 524 } 525 526 TEST_F(BackgroundIndexTest, NoDotsInAbsPath) { 527 MockFS FS; 528 llvm::StringMap<std::string> Storage; 529 size_t CacheHits = 0; 530 MemoryShardStorage MSS(Storage, CacheHits); 531 OverlayCDB CDB(/*Base=*/nullptr); 532 BackgroundIndex Idx(FS, CDB, [&](llvm::StringRef) { return &MSS; }, 533 /*Opts=*/{}); 534 ASSERT_TRUE(Idx.blockUntilIdleForTest()); 535 536 tooling::CompileCommand Cmd; 537 FS.Files[testPath("root/A.cc")] = ""; 538 Cmd.Filename = "../A.cc"; 539 Cmd.Directory = testPath("root/build"); 540 Cmd.CommandLine = {"clang++", "../A.cc"}; 541 CDB.setCompileCommand(testPath("root/build/../A.cc"), Cmd); 542 ASSERT_TRUE(Idx.blockUntilIdleForTest()); 543 544 FS.Files[testPath("root/B.cc")] = ""; 545 Cmd.Filename = "./B.cc"; 546 Cmd.Directory = testPath("root"); 547 Cmd.CommandLine = {"clang++", "./B.cc"}; 548 CDB.setCompileCommand(testPath("root/./B.cc"), Cmd); 549 ASSERT_TRUE(Idx.blockUntilIdleForTest()); 550 551 for (llvm::StringRef AbsPath : MSS.AccessedPaths.keys()) { 552 EXPECT_FALSE(AbsPath.contains("./")) << AbsPath; 553 EXPECT_FALSE(AbsPath.contains("../")) << AbsPath; 554 } 555 } 556 557 TEST_F(BackgroundIndexTest, UncompilableFiles) { 558 MockFS FS; 559 llvm::StringMap<std::string> Storage; 560 size_t CacheHits = 0; 561 MemoryShardStorage MSS(Storage, CacheHits); 562 OverlayCDB CDB(/*Base=*/nullptr); 563 BackgroundIndex Idx(FS, CDB, [&](llvm::StringRef) { return &MSS; }, 564 /*Opts=*/{}); 565 566 tooling::CompileCommand Cmd; 567 FS.Files[testPath("A.h")] = "void foo();"; 568 FS.Files[testPath("B.h")] = "#include \"C.h\"\nasdf;"; 569 FS.Files[testPath("C.h")] = ""; 570 FS.Files[testPath("A.cc")] = R"cpp( 571 #include "A.h" 572 #include "B.h" 573 #include "not_found_header.h" 574 575 void foo() {} 576 )cpp"; 577 Cmd.Filename = "../A.cc"; 578 Cmd.Directory = testPath("build"); 579 Cmd.CommandLine = {"clang++", "../A.cc"}; 580 CDB.setCompileCommand(testPath("build/../A.cc"), Cmd); 581 ASSERT_TRUE(Idx.blockUntilIdleForTest()); 582 583 EXPECT_THAT(Storage.keys(), 584 UnorderedElementsAre(testPath("A.cc"), testPath("A.h"), 585 testPath("B.h"), testPath("C.h"))); 586 587 { 588 auto Shard = MSS.loadShard(testPath("A.cc")); 589 EXPECT_THAT(*Shard->Symbols, UnorderedElementsAre(named("foo"))); 590 EXPECT_THAT(Shard->Sources->keys(), 591 UnorderedElementsAre("unittest:///A.cc", "unittest:///A.h", 592 "unittest:///B.h")); 593 EXPECT_THAT(Shard->Sources->lookup("unittest:///A.cc"), hadErrors()); 594 } 595 596 { 597 auto Shard = MSS.loadShard(testPath("A.h")); 598 EXPECT_THAT(*Shard->Symbols, UnorderedElementsAre(named("foo"))); 599 EXPECT_THAT(Shard->Sources->keys(), 600 UnorderedElementsAre("unittest:///A.h")); 601 EXPECT_THAT(Shard->Sources->lookup("unittest:///A.h"), hadErrors()); 602 } 603 604 { 605 auto Shard = MSS.loadShard(testPath("B.h")); 606 EXPECT_THAT(*Shard->Symbols, UnorderedElementsAre(named("asdf"))); 607 EXPECT_THAT(Shard->Sources->keys(), 608 UnorderedElementsAre("unittest:///B.h", "unittest:///C.h")); 609 EXPECT_THAT(Shard->Sources->lookup("unittest:///B.h"), hadErrors()); 610 } 611 612 { 613 auto Shard = MSS.loadShard(testPath("C.h")); 614 EXPECT_THAT(*Shard->Symbols, UnorderedElementsAre()); 615 EXPECT_THAT(Shard->Sources->keys(), 616 UnorderedElementsAre("unittest:///C.h")); 617 EXPECT_THAT(Shard->Sources->lookup("unittest:///C.h"), hadErrors()); 618 } 619 } 620 621 TEST_F(BackgroundIndexTest, CmdLineHash) { 622 MockFS FS; 623 llvm::StringMap<std::string> Storage; 624 size_t CacheHits = 0; 625 MemoryShardStorage MSS(Storage, CacheHits); 626 OverlayCDB CDB(/*Base=*/nullptr); 627 BackgroundIndex Idx(FS, CDB, [&](llvm::StringRef) { return &MSS; }, 628 /*Opts=*/{}); 629 630 tooling::CompileCommand Cmd; 631 FS.Files[testPath("A.cc")] = "#include \"A.h\""; 632 FS.Files[testPath("A.h")] = ""; 633 Cmd.Filename = "../A.cc"; 634 Cmd.Directory = testPath("build"); 635 Cmd.CommandLine = {"clang++", "../A.cc", "-fsyntax-only"}; 636 CDB.setCompileCommand(testPath("build/../A.cc"), Cmd); 637 ASSERT_TRUE(Idx.blockUntilIdleForTest()); 638 639 EXPECT_THAT(Storage.keys(), 640 UnorderedElementsAre(testPath("A.cc"), testPath("A.h"))); 641 // Make sure we only store the Cmd for main file. 642 EXPECT_FALSE(MSS.loadShard(testPath("A.h"))->Cmd); 643 644 tooling::CompileCommand CmdStored = *MSS.loadShard(testPath("A.cc"))->Cmd; 645 EXPECT_EQ(CmdStored.CommandLine, Cmd.CommandLine); 646 EXPECT_EQ(CmdStored.Directory, Cmd.Directory); 647 } 648 649 TEST_F(BackgroundIndexTest, Reindex) { 650 MockFS FS; 651 llvm::StringMap<std::string> Storage; 652 size_t CacheHits = 0; 653 MemoryShardStorage MSS(Storage, CacheHits); 654 OverlayCDB CDB(/*Base=*/nullptr); 655 BackgroundIndex Idx(FS, CDB, [&](llvm::StringRef) { return &MSS; }, 656 /*Opts=*/{}); 657 658 // Index a file. 659 FS.Files[testPath("A.cc")] = "int theOldFunction();"; 660 tooling::CompileCommand Cmd; 661 Cmd.Filename = "../A.cc"; 662 Cmd.Directory = testPath("build"); 663 Cmd.CommandLine = {"clang++", "../A.cc", "-fsyntax-only"}; 664 CDB.setCompileCommand(testPath("A.cc"), Cmd); 665 ASSERT_TRUE(Idx.blockUntilIdleForTest()); 666 667 // Verify the result is indexed and stored. 668 EXPECT_EQ(1u, runFuzzyFind(Idx, "theOldFunction").size()); 669 EXPECT_EQ(0u, runFuzzyFind(Idx, "theNewFunction").size()); 670 std::string OldShard = Storage.lookup(testPath("A.cc")); 671 EXPECT_NE("", OldShard); 672 673 // Change the content and command, and notify to reindex it. 674 Cmd.CommandLine.push_back("-DFOO"); 675 FS.Files[testPath("A.cc")] = "int theNewFunction();"; 676 CDB.setCompileCommand(testPath("A.cc"), Cmd); 677 ASSERT_TRUE(Idx.blockUntilIdleForTest()); 678 679 // Currently, we will never index the same main file again. 680 EXPECT_EQ(1u, runFuzzyFind(Idx, "theOldFunction").size()); 681 EXPECT_EQ(0u, runFuzzyFind(Idx, "theNewFunction").size()); 682 EXPECT_EQ(OldShard, Storage.lookup(testPath("A.cc"))); 683 } 684 685 class BackgroundIndexRebuilderTest : public testing::Test { 686 protected: 687 BackgroundIndexRebuilderTest() 688 : Source(IndexContents::All, /*SupportContainedRefs=*/true), 689 Target(std::make_unique<MemIndex>()), 690 Rebuilder(&Target, &Source, /*Threads=*/10) { 691 // Prepare FileSymbols with TestSymbol in it, for checkRebuild. 692 TestSymbol.ID = SymbolID("foo"); 693 } 694 695 // Perform Action and determine whether it rebuilt the index or not. 696 bool checkRebuild(std::function<void()> Action) { 697 // Update name so we can tell if the index updates. 698 VersionStorage.push_back("Sym" + std::to_string(++VersionCounter)); 699 TestSymbol.Name = VersionStorage.back(); 700 SymbolSlab::Builder SB; 701 SB.insert(TestSymbol); 702 Source.update("", std::make_unique<SymbolSlab>(std::move(SB).build()), 703 nullptr, nullptr, false); 704 // Now maybe update the index. 705 Action(); 706 // Now query the index to get the name count. 707 std::string ReadName; 708 LookupRequest Req; 709 Req.IDs.insert(TestSymbol.ID); 710 Target.lookup(Req, 711 [&](const Symbol &S) { ReadName = std::string(S.Name); }); 712 // The index was rebuild if the name is up to date. 713 return ReadName == VersionStorage.back(); 714 } 715 716 Symbol TestSymbol; 717 FileSymbols Source; 718 SwapIndex Target; 719 BackgroundIndexRebuilder Rebuilder; 720 721 unsigned VersionCounter = 0; 722 std::deque<std::string> VersionStorage; 723 }; 724 725 TEST_F(BackgroundIndexRebuilderTest, IndexingTUs) { 726 for (unsigned I = 0; I < Rebuilder.TUsBeforeFirstBuild - 1; ++I) 727 EXPECT_FALSE(checkRebuild([&] { Rebuilder.indexedTU(); })); 728 EXPECT_TRUE(checkRebuild([&] { Rebuilder.indexedTU(); })); 729 for (unsigned I = 0; I < Rebuilder.TUsBeforeRebuild - 1; ++I) 730 EXPECT_FALSE(checkRebuild([&] { Rebuilder.indexedTU(); })); 731 EXPECT_TRUE(checkRebuild([&] { Rebuilder.indexedTU(); })); 732 } 733 734 TEST_F(BackgroundIndexRebuilderTest, LoadingShards) { 735 Rebuilder.startLoading(); 736 Rebuilder.loadedShard(10); 737 Rebuilder.loadedShard(20); 738 EXPECT_TRUE(checkRebuild([&] { Rebuilder.doneLoading(); })); 739 740 // No rebuild for no shards. 741 Rebuilder.startLoading(); 742 EXPECT_FALSE(checkRebuild([&] { Rebuilder.doneLoading(); })); 743 744 // Loads can overlap. 745 Rebuilder.startLoading(); 746 Rebuilder.loadedShard(1); 747 Rebuilder.startLoading(); 748 Rebuilder.loadedShard(1); 749 EXPECT_FALSE(checkRebuild([&] { Rebuilder.doneLoading(); })); 750 Rebuilder.loadedShard(1); 751 EXPECT_TRUE(checkRebuild([&] { Rebuilder.doneLoading(); })); 752 753 // No rebuilding for indexed files while loading. 754 Rebuilder.startLoading(); 755 for (unsigned I = 0; I < 3 * Rebuilder.TUsBeforeRebuild; ++I) 756 EXPECT_FALSE(checkRebuild([&] { Rebuilder.indexedTU(); })); 757 // But they get indexed when we're done, even if no shards were loaded. 758 EXPECT_TRUE(checkRebuild([&] { Rebuilder.doneLoading(); })); 759 } 760 761 TEST(BackgroundQueueTest, Priority) { 762 // Create high and low priority tasks. 763 // Once a bunch of high priority tasks have run, the queue is stopped. 764 // So the low priority tasks should never run. 765 BackgroundQueue Q; 766 std::atomic<unsigned> HiRan(0), LoRan(0); 767 BackgroundQueue::Task Lo([&] { ++LoRan; }); 768 BackgroundQueue::Task Hi([&] { 769 if (++HiRan >= 10) 770 Q.stop(); 771 }); 772 Hi.QueuePri = 100; 773 774 // Enqueuing the low-priority ones first shouldn't make them run first. 775 Q.append(std::vector<BackgroundQueue::Task>(30, Lo)); 776 for (unsigned I = 0; I < 30; ++I) 777 Q.push(Hi); 778 779 AsyncTaskRunner ThreadPool; 780 for (unsigned I = 0; I < 5; ++I) 781 ThreadPool.runAsync("worker", [&] { Q.work(); }); 782 // We should test enqueue with active workers, but it's hard to avoid races. 783 // Just make sure we don't crash. 784 Q.push(Lo); 785 Q.append(std::vector<BackgroundQueue::Task>(2, Hi)); 786 787 // After finishing, check the tasks that ran. 788 ThreadPool.wait(); 789 EXPECT_GE(HiRan, 10u); 790 EXPECT_EQ(LoRan, 0u); 791 } 792 793 TEST(BackgroundQueueTest, Boost) { 794 std::string Sequence; 795 796 BackgroundQueue::Task A([&] { Sequence.push_back('A'); }); 797 A.Tag = "A"; 798 A.QueuePri = 1; 799 800 BackgroundQueue::Task B([&] { Sequence.push_back('B'); }); 801 B.QueuePri = 2; 802 B.Tag = "B"; 803 804 { 805 BackgroundQueue Q; 806 Q.append({A, B}); 807 Q.work([&] { Q.stop(); }); 808 EXPECT_EQ("BA", Sequence) << "priority order"; 809 } 810 Sequence.clear(); 811 { 812 BackgroundQueue Q; 813 Q.boost("A", 3); 814 Q.append({A, B}); 815 Q.work([&] { Q.stop(); }); 816 EXPECT_EQ("AB", Sequence) << "A was boosted before enqueueing"; 817 } 818 Sequence.clear(); 819 { 820 BackgroundQueue Q; 821 Q.append({A, B}); 822 Q.boost("A", 3); 823 Q.work([&] { Q.stop(); }); 824 EXPECT_EQ("AB", Sequence) << "A was boosted after enqueueing"; 825 } 826 } 827 828 TEST(BackgroundQueueTest, Duplicates) { 829 std::string Sequence; 830 BackgroundQueue::Task A([&] { Sequence.push_back('A'); }); 831 A.QueuePri = 100; 832 A.Key = 1; 833 BackgroundQueue::Task B([&] { Sequence.push_back('B'); }); 834 // B has no key, and is not subject to duplicate detection. 835 B.QueuePri = 50; 836 837 BackgroundQueue Q; 838 Q.append({A, B, A, B}); // One A is dropped, the other is high priority. 839 Q.work(/*OnIdle=*/[&] { 840 // The first time we go idle, we enqueue the same task again. 841 if (!llvm::is_contained(Sequence, ' ')) { 842 Sequence.push_back(' '); 843 Q.append({A, B, A, B}); // Both As are dropped. 844 } else { 845 Q.stop(); 846 } 847 }); 848 849 // This could reasonably be "ABB BBA", if we had good *re*indexing support. 850 EXPECT_EQ("ABB BB", Sequence); 851 } 852 853 TEST(BackgroundQueueTest, Progress) { 854 using testing::AnyOf; 855 BackgroundQueue::Stats S; 856 BackgroundQueue Q([&](BackgroundQueue::Stats New) { 857 // Verify values are sane. 858 // Items are enqueued one at a time (at least in this test). 859 EXPECT_THAT(New.Enqueued, AnyOf(S.Enqueued, S.Enqueued + 1)); 860 // Items are completed one at a time. 861 EXPECT_THAT(New.Completed, AnyOf(S.Completed, S.Completed + 1)); 862 // Items are started or completed one at a time. 863 EXPECT_THAT(New.Active, AnyOf(S.Active - 1, S.Active, S.Active + 1)); 864 // Idle point only advances in time. 865 EXPECT_GE(New.LastIdle, S.LastIdle); 866 // Idle point is a task that has been completed in the past. 867 EXPECT_LE(New.LastIdle, New.Completed); 868 // LastIdle is now only if we're really idle. 869 EXPECT_EQ(New.LastIdle == New.Enqueued, 870 New.Completed == New.Enqueued && New.Active == 0u); 871 S = New; 872 }); 873 874 // Two types of tasks: a ping task enqueues a pong task. 875 // This avoids all enqueues followed by all completions (boring!) 876 std::atomic<int> PingCount(0), PongCount(0); 877 BackgroundQueue::Task Pong([&] { ++PongCount; }); 878 BackgroundQueue::Task Ping([&] { 879 ++PingCount; 880 Q.push(Pong); 881 }); 882 883 for (int I = 0; I < 1000; ++I) 884 Q.push(Ping); 885 // Spin up some workers and stop while idle. 886 AsyncTaskRunner ThreadPool; 887 for (unsigned I = 0; I < 5; ++I) 888 ThreadPool.runAsync("worker", [&] { Q.work([&] { Q.stop(); }); }); 889 ThreadPool.wait(); 890 891 // Everything's done, check final stats. 892 // Assertions above ensure we got from 0 to 2000 in a reasonable way. 893 EXPECT_EQ(PingCount.load(), 1000); 894 EXPECT_EQ(PongCount.load(), 1000); 895 EXPECT_EQ(S.Active, 0u); 896 EXPECT_EQ(S.Enqueued, 2000u); 897 EXPECT_EQ(S.Completed, 2000u); 898 EXPECT_EQ(S.LastIdle, 2000u); 899 } 900 901 TEST(BackgroundIndex, Profile) { 902 MockFS FS; 903 MockCompilationDatabase CDB; 904 BackgroundIndex Idx(FS, CDB, [](llvm::StringRef) { return nullptr; }, 905 /*Opts=*/{}); 906 907 llvm::BumpPtrAllocator Alloc; 908 MemoryTree MT(&Alloc); 909 Idx.profile(MT); 910 ASSERT_THAT(MT.children(), 911 UnorderedElementsAre(Pair("slabs", _), Pair("index", _))); 912 } 913 914 } // namespace clangd 915 } // namespace clang 916