25 #include "clang/Basic/DiagnosticDriver.h"
26 #include "llvm/ADT/ArrayRef.h"
27 #include "llvm/ADT/FunctionExtras.h"
28 #include "llvm/ADT/ScopeExit.h"
29 #include "llvm/ADT/StringExtras.h"
30 #include "llvm/ADT/StringMap.h"
31 #include "llvm/ADT/StringRef.h"
32 #include "gmock/gmock.h"
33 #include "gtest/gtest.h"
46 using ::testing::AllOf;
47 using ::testing::AnyOf;
48 using ::testing::Contains;
49 using ::testing::Each;
50 using ::testing::ElementsAre;
53 using ::testing::IsEmpty;
55 using ::testing::Pair;
56 using ::testing::Pointee;
57 using ::testing::SizeIs;
58 using ::testing::UnorderedElementsAre;
60 MATCHER_P2(TUState, PreambleActivity, ASTActivity,
"") {
61 if (arg.PreambleActivity != PreambleActivity) {
62 *result_listener <<
"preamblestate is "
63 <<
static_cast<uint8_t
>(arg.PreambleActivity);
66 if (arg.ASTActivity.K != ASTActivity) {
67 *result_listener <<
"aststate is " << arg.ASTActivity.K;
74 static Key<std::string> BoundPath;
78 llvm::StringRef boundPath() {
80 return V ? *V : llvm::StringRef(
"");
83 TUScheduler::Options optsForTest() {
85 Opts.ContextProvider = bindPath;
89 class TUSchedulerTests :
public ::testing::Test {
91 ParseInputs getInputs(
PathRef File, std::string Contents) {
93 Inputs.CompileCommand = *
CDB.getCompileCommand(File);
95 Inputs.Contents = std::move(Contents);
96 Inputs.Opts = ParseOptions();
100 void updateWithCallback(TUScheduler &S,
PathRef File,
102 llvm::unique_function<
void()> CB) {
103 updateWithCallback(S, File, getInputs(File, std::string(Contents)), WD,
107 void updateWithCallback(TUScheduler &S,
PathRef File, ParseInputs
Inputs,
109 llvm::unique_function<
void()> CB) {
110 WithContextValue
Ctx(llvm::make_scope_exit(std::move(CB)));
111 S.update(File,
Inputs, WD);
114 static Key<llvm::unique_function<void(
PathRef File, std::vector<Diag>)>>
119 static std::unique_ptr<ParsingCallbacks> captureDiags() {
120 class CaptureDiags :
public ParsingCallbacks {
122 void onMainAST(
PathRef File, ParsedAST &AST, PublishFn Publish)
override {
123 reportDiagnostics(File, *AST.getDiagnostics(), Publish);
126 void onFailedAST(
PathRef File, llvm::StringRef Version,
127 std::vector<Diag>
Diags, PublishFn Publish)
override {
128 reportDiagnostics(File,
Diags, Publish);
132 void reportDiagnostics(
PathRef File, llvm::ArrayRef<Diag>
Diags,
139 llvm::unique_function<
void(
PathRef, std::vector<Diag>)
> &> (*D)(
140 File, std::move(
Diags));
144 return std::make_unique<CaptureDiags>();
150 void updateWithDiags(TUScheduler &S,
PathRef File, ParseInputs
Inputs,
152 llvm::unique_function<
void(std::vector<Diag>)> CB) {
153 Path OrigFile = File.str();
155 [OrigFile, CB = std::move(CB)](
157 assert(File == OrigFile);
158 CB(std::move(
Diags));
160 S.update(File, std::move(
Inputs), WD);
163 void updateWithDiags(TUScheduler &S,
PathRef File, llvm::StringRef Contents,
165 llvm::unique_function<
void(std::vector<Diag>)> CB) {
166 return updateWithDiags(S, File, getInputs(File, std::string(Contents)), WD,
171 MockCompilationDatabase
CDB;
174 Key<llvm::unique_function<void(
PathRef File, std::vector<Diag>)>>
177 TEST_F(TUSchedulerTests, MissingFiles) {
178 TUScheduler S(
CDB, optsForTest());
191 [&](Expected<InputsAndAST> AST) {
EXPECT_ERROR(AST); });
194 [&](Expected<InputsAndPreamble> Preamble) {
EXPECT_ERROR(Preamble); });
199 S.runWithAST(
"", Added,
200 [&](Expected<InputsAndAST> AST) { EXPECT_TRUE(
bool(AST)); });
202 [&](Expected<InputsAndPreamble> Preamble) {
203 EXPECT_TRUE(
bool(Preamble));
208 S.runWithAST(
"", Added,
209 [&](Expected<InputsAndAST> AST) {
EXPECT_ERROR(AST); });
211 [&](Expected<InputsAndPreamble> Preamble) {
212 ASSERT_FALSE(
bool(Preamble));
213 llvm::consumeError(
Preamble.takeError());
220 std::atomic<int> CallbackCount(0);
225 TUScheduler S(
CDB, optsForTest(), captureDiags());
228 [&](std::vector<Diag>) { Ready.wait(); });
230 [&](std::vector<Diag>) { ++CallbackCount; });
232 [&](std::vector<Diag>) {
234 <<
"auto should have been cancelled by auto";
237 [&](std::vector<Diag>) {
238 ADD_FAILURE() <<
"no diags should not be called back";
241 [&](std::vector<Diag>) { ++CallbackCount; });
246 EXPECT_EQ(2, CallbackCount);
249 TEST_F(TUSchedulerTests, Debounce) {
250 auto Opts = optsForTest();
252 TUScheduler S(
CDB, Opts, captureDiags());
256 [&](std::vector<Diag>) {
258 <<
"auto should have been debounced and canceled";
261 std::this_thread::sleep_for(std::chrono::milliseconds(50));
266 [&](std::vector<Diag>) { N.notify(); });
271 [&](std::vector<Diag>) {
273 <<
"auto should have been discarded (dead write)";
277 TEST_F(TUSchedulerTests, Cancellation) {
287 std::vector<StringRef> DiagsSeen, ReadsSeen, ReadsCanceled;
289 Notification Proceed;
290 TUScheduler S(
CDB, optsForTest(), captureDiags());
295 WithContext
C(std::move(T.first));
298 [&,
ID](std::vector<Diag>
Diags) { DiagsSeen.push_back(
ID); });
299 return std::move(T.second);
304 WithContext
C(std::move(T.first));
305 S.runWithAST(
ID,
Path, [&,
ID](llvm::Expected<InputsAndAST>
E) {
306 if (
auto Err =
E.takeError()) {
307 if (Err.isA<CancelledError>()) {
308 ReadsCanceled.push_back(ID);
309 consumeError(std::move(Err));
311 ADD_FAILURE() <<
"Non-cancelled error for " << ID <<
": "
312 << llvm::toString(std::move(Err));
315 ReadsSeen.push_back(
ID);
318 return std::move(T.second);
322 [&]() { Proceed.wait(); });
335 EXPECT_THAT(DiagsSeen, ElementsAre(
"U2",
"U3"))
336 <<
"U1 and all dependent reads were cancelled. "
337 "U2 has a dependent read R2A. "
338 "U3 was not cancelled.";
339 EXPECT_THAT(ReadsSeen, ElementsAre(
"R2B"))
340 <<
"All reads other than R2B were cancelled";
341 EXPECT_THAT(ReadsCanceled, ElementsAre(
"R1",
"R2A",
"R3"))
342 <<
"All reads other than R2B were cancelled";
345 TEST_F(TUSchedulerTests, InvalidationNoCrash) {
347 TUScheduler S(
CDB, optsForTest(), captureDiags());
349 Notification StartedRunning;
350 Notification ScheduledChange;
356 "invalidatable-but-running",
Path,
357 [&](llvm::Expected<InputsAndAST> AST) {
358 StartedRunning.notify();
359 ScheduledChange.wait();
360 ASSERT_TRUE(
bool(AST));
363 StartedRunning.wait();
365 ScheduledChange.notify();
369 TEST_F(TUSchedulerTests, Invalidation) {
371 TUScheduler S(
CDB, optsForTest(), captureDiags());
372 std::atomic<int> Builds(0), Actions(0);
380 "invalidatable",
Path,
381 [&](llvm::Expected<InputsAndAST> AST) {
383 EXPECT_FALSE(
bool(AST));
384 llvm::Error
E = AST.takeError();
385 EXPECT_TRUE(
E.isA<CancelledError>());
386 handleAllErrors(std::move(
E), [&](
const CancelledError &
E) {
392 "not-invalidatable",
Path,
393 [&](llvm::Expected<InputsAndAST> AST) {
395 EXPECT_TRUE(
bool(AST));
400 ADD_FAILURE() <<
"Shouldn't build, all dependents invalidated";
403 "invalidatable",
Path,
404 [&](llvm::Expected<InputsAndAST> AST) {
406 EXPECT_FALSE(
bool(AST));
407 llvm::Error
E = AST.takeError();
408 EXPECT_TRUE(
E.isA<CancelledError>());
409 consumeError(std::move(
E));
413 [&](std::vector<Diag>) { ++Builds; });
415 "invalidatable",
Path,
416 [&](llvm::Expected<InputsAndAST> AST) {
418 EXPECT_TRUE(
bool(AST)) <<
"Shouldn't be invalidated, no update follows";
424 EXPECT_EQ(2, Builds.load()) <<
"Middle build should be skipped";
425 EXPECT_EQ(4, Actions.load()) <<
"All actions should run (some with error)";
433 TEST_F(TUSchedulerTests, InvalidationUnchanged) {
435 TUScheduler S(
CDB, optsForTest(), captureDiags());
436 std::atomic<int> Actions(0);
443 "invalidatable",
Path,
444 [&](llvm::Expected<InputsAndAST> AST) {
446 EXPECT_TRUE(
bool(AST))
447 <<
"Should not invalidate based on an update with same content: "
452 ADD_FAILURE() <<
"Shouldn't build, identical to previous";
457 EXPECT_EQ(1, Actions.load()) <<
"All actions should run";
460 TEST_F(TUSchedulerTests, ManyUpdates) {
461 const int FilesCount = 3;
462 const int UpdatesPerFile = 10;
465 int TotalASTReads = 0;
466 int TotalPreambleReads = 0;
467 int TotalUpdates = 0;
468 llvm::StringMap<int> LatestDiagVersion;
472 auto Opts = optsForTest();
474 TUScheduler S(
CDB, Opts, captureDiags());
476 std::vector<std::string>
Files;
477 for (
int I = 0; I < FilesCount; ++I) {
478 std::string
Name =
"foo" + std::to_string(I) +
".cpp";
480 this->
FS.
Files[Files.back()] =
"";
483 StringRef Contents1 = R
"cpp(int a;)cpp";
484 StringRef Contents2 = R"cpp(int main() { return 1; })cpp";
485 StringRef Contents3 = R"cpp(int a; int b; int sum() { return a + b; })cpp";
487 StringRef AllContents[] = {Contents1, Contents2, Contents3};
488 const int AllContentsSize = 3;
492 static Key<int> NonceKey;
495 for (
int FileI = 0; FileI < FilesCount; ++FileI) {
496 for (
int UpdateI = 0; UpdateI < UpdatesPerFile; ++UpdateI) {
497 auto Contents = AllContents[(FileI + UpdateI) % AllContentsSize];
499 auto File =
Files[FileI];
500 auto Inputs = getInputs(File, Contents.str());
502 WithContextValue WithNonce(NonceKey, ++Nonce);
507 &LatestDiagVersion](std::vector<Diag>) {
508 EXPECT_THAT(Context::current().get(NonceKey), Pointee(Nonce));
509 EXPECT_EQ(File, boundPath());
511 std::lock_guard<std::mutex> Lock(Mut);
513 EXPECT_EQ(File, *TUScheduler::getFileBeingProcessedInContext());
515 auto It = LatestDiagVersion.try_emplace(File, -1);
516 const int PrevVersion = It.first->second;
518 ASSERT_TRUE(llvm::to_integer(Version, CurVersion, 10));
519 EXPECT_LT(PrevVersion, CurVersion);
520 It.first->getValue() = CurVersion;
524 WithContextValue WithNonce(NonceKey, ++Nonce);
527 [File,
Inputs, Nonce, &Mut,
528 &TotalASTReads](Expected<InputsAndAST> AST) {
530 EXPECT_EQ(File, boundPath());
532 ASSERT_TRUE((
bool)AST);
537 std::lock_guard<std::mutex> Lock(Mut);
544 WithContextValue WithNonce(NonceKey, ++Nonce);
547 [File,
Inputs, Nonce, &Mut,
548 &TotalPreambleReads](Expected<InputsAndPreamble> Preamble) {
550 EXPECT_EQ(File, boundPath());
552 ASSERT_TRUE((
bool)Preamble);
555 std::lock_guard<std::mutex> Lock(Mut);
556 ++TotalPreambleReads;
565 std::lock_guard<std::mutex> Lock(Mut);
568 EXPECT_GE(TotalUpdates, FilesCount);
569 EXPECT_LE(TotalUpdates, FilesCount * UpdatesPerFile);
571 for (
const auto &
Entry : LatestDiagVersion)
572 EXPECT_EQ(
Entry.second, UpdatesPerFile - 1);
573 EXPECT_EQ(TotalASTReads, FilesCount * UpdatesPerFile);
574 EXPECT_EQ(TotalPreambleReads, FilesCount * UpdatesPerFile);
577 TEST_F(TUSchedulerTests, EvictedAST) {
578 std::atomic<int> BuiltASTCounter(0);
579 auto Opts = optsForTest();
580 Opts.AsyncThreadsCount = 1;
581 Opts.RetentionPolicy.MaxRetainedASTs = 2;
583 TUScheduler S(
CDB, Opts);
585 llvm::StringLiteral SourceContents = R
"cpp(
589 llvm::StringLiteral OtherSourceContents = R"cpp(
598 EXPECT_THAT(
Tracer.takeMetric(
"ast_access_diag",
"hit"), SizeIs(0));
599 EXPECT_THAT(
Tracer.takeMetric(
"ast_access_diag",
"miss"), SizeIs(0));
603 [&BuiltASTCounter]() { ++BuiltASTCounter; });
605 ASSERT_EQ(BuiltASTCounter.load(), 1);
606 EXPECT_THAT(
Tracer.takeMetric(
"ast_access_diag",
"hit"), SizeIs(0));
607 EXPECT_THAT(
Tracer.takeMetric(
"ast_access_diag",
"miss"), SizeIs(1));
612 [&BuiltASTCounter]() { ++BuiltASTCounter; });
614 [&BuiltASTCounter]() { ++BuiltASTCounter; });
616 ASSERT_EQ(BuiltASTCounter.load(), 3);
617 EXPECT_THAT(
Tracer.takeMetric(
"ast_access_diag",
"hit"), SizeIs(0));
618 EXPECT_THAT(
Tracer.takeMetric(
"ast_access_diag",
"miss"), SizeIs(2));
621 ASSERT_THAT(S.getFilesWithCachedAST(), UnorderedElementsAre(
Bar, Baz));
625 [&BuiltASTCounter]() { ++BuiltASTCounter; });
627 ASSERT_EQ(BuiltASTCounter.load(), 4);
628 EXPECT_THAT(
Tracer.takeMetric(
"ast_access_diag",
"hit"), SizeIs(0));
629 EXPECT_THAT(
Tracer.takeMetric(
"ast_access_diag",
"miss"), SizeIs(1));
633 EXPECT_THAT(S.getFilesWithCachedAST(),
634 UnorderedElementsAre(
Foo, AnyOf(
Bar, Baz)));
640 TEST_F(TUSchedulerTests, NoopChangesDontThrashCache) {
641 auto Opts = optsForTest();
642 Opts.RetentionPolicy.MaxRetainedASTs = 1;
643 TUScheduler S(
CDB, Opts);
646 auto FooInputs = getInputs(
Foo,
"int x=1;");
648 auto BarInputs = getInputs(
Bar,
"int x=2;");
655 ASSERT_THAT(S.getFilesWithCachedAST(), ElementsAre(
Bar));
662 ASSERT_THAT(S.getFilesWithCachedAST(), ElementsAre(
Bar));
664 ASSERT_EQ(S.fileStats().lookup(
Foo).ASTBuilds, 1u);
665 ASSERT_EQ(S.fileStats().lookup(
Bar).ASTBuilds, 1u);
668 TEST_F(TUSchedulerTests, EmptyPreamble) {
669 TUScheduler S(
CDB, optsForTest());
674 FS.
Files[Header] =
"void foo()";
676 auto *WithPreamble = R
"cpp(
680 auto *WithEmptyPreamble = R
"cpp(int main() {})cpp";
684 [&](Expected<InputsAndPreamble> Preamble) {
699 [&](Expected<InputsAndPreamble> Preamble) {
707 TEST_F(TUSchedulerTests, ASTSignalsSmokeTests) {
708 TUScheduler S(
CDB, optsForTest());
712 FS.
Files[Header] =
"namespace tar { int foo(); }";
713 const char *Contents = R
"cpp(
725 Notification TaskRun;
728 [&](Expected<InputsAndPreamble> IP) {
730 std::vector<std::pair<StringRef, int>> NS;
731 for (
const auto &P : IP->Signals->RelatedNamespaces)
732 NS.emplace_back(P.getKey(), P.getValue());
734 UnorderedElementsAre(Pair(
"ns::", 1), Pair(
"tar::", 1)));
736 std::vector<std::pair<SymbolID, int>> Sym;
737 for (
const auto &P : IP->Signals->ReferencedSymbols)
738 Sym.emplace_back(P.getFirst(), P.getSecond());
739 EXPECT_THAT(Sym, UnorderedElementsAre(Pair(
ns(
"tar").
ID, 1),
740 Pair(
ns(
"ns").
ID, 1),
741 Pair(
func(
"tar::foo").
ID, 1),
742 Pair(
func(
"ns::func").
ID, 1)));
748 TEST_F(TUSchedulerTests, RunWaitsForPreamble) {
751 TUScheduler S(
CDB, optsForTest());
753 auto *NonEmptyPreamble = R
"cpp(
759 constexpr int ReadsToSchedule = 10;
760 std::mutex PreamblesMut;
761 std::vector<const void *> Preambles(ReadsToSchedule,
nullptr);
763 for (
int I = 0; I < ReadsToSchedule; ++I) {
766 [I, &PreamblesMut, &Preambles](Expected<InputsAndPreamble> IP) {
767 std::lock_guard<std::mutex> Lock(PreamblesMut);
768 Preambles[I] = cantFail(std::move(IP)).Preamble;
773 std::lock_guard<std::mutex> Lock(PreamblesMut);
774 ASSERT_NE(Preambles[0],
nullptr);
775 ASSERT_THAT(Preambles, Each(Preambles[0]));
778 TEST_F(TUSchedulerTests, NoopOnEmptyChanges) {
779 TUScheduler S(
CDB, optsForTest(), captureDiags());
787 std::string SourceContents = R
"cpp(
793 auto DoUpdate = [&](std::string Contents) ->
bool {
794 std::atomic<bool> Updated(
false);
797 [&Updated](std::vector<Diag>) { Updated =
true; });
800 ADD_FAILURE() <<
"Updated has not finished in one second. Threading bug?";
805 ASSERT_TRUE(DoUpdate(SourceContents));
806 ASSERT_EQ(S.fileStats().lookup(Source).ASTBuilds, 1u);
807 ASSERT_EQ(S.fileStats().lookup(Source).PreambleBuilds, 1u);
808 ASSERT_FALSE(DoUpdate(SourceContents));
809 ASSERT_EQ(S.fileStats().lookup(Source).ASTBuilds, 1u);
810 ASSERT_EQ(S.fileStats().lookup(Source).PreambleBuilds, 1u);
814 ASSERT_TRUE(DoUpdate(SourceContents));
815 ASSERT_FALSE(DoUpdate(SourceContents));
816 ASSERT_EQ(S.fileStats().lookup(Source).ASTBuilds, 2u);
817 ASSERT_EQ(S.fileStats().lookup(Source).PreambleBuilds, 2u);
820 SourceContents +=
"\nint c = b;";
821 ASSERT_TRUE(DoUpdate(SourceContents));
822 ASSERT_FALSE(DoUpdate(SourceContents));
823 ASSERT_EQ(S.fileStats().lookup(Source).ASTBuilds, 3u);
824 ASSERT_EQ(S.fileStats().lookup(Source).PreambleBuilds, 2u);
828 ASSERT_TRUE(DoUpdate(SourceContents));
829 ASSERT_FALSE(DoUpdate(SourceContents));
830 ASSERT_EQ(S.fileStats().lookup(Source).ASTBuilds, 4u);
831 ASSERT_EQ(S.fileStats().lookup(Source).PreambleBuilds, 3u);
837 TEST_F(TUSchedulerTests, MissingHeader) {
841 FS.
Files.try_emplace(
"a/__unused__");
842 FS.
Files.try_emplace(
"b/__unused__");
843 TUScheduler S(
CDB, optsForTest(), captureDiags());
849 auto *SourceContents = R
"cpp(
854 ParseInputs Inputs = getInputs(Source, SourceContents);
855 std::atomic<size_t> DiagCount(0);
861 [&DiagCount](std::vector<Diag>
Diags) {
866 "use of undeclared identifier 'b'")));
875 [&DiagCount](std::vector<Diag>
Diags) {
877 EXPECT_THAT(
Diags, IsEmpty());
888 [&DiagCount](std::vector<Diag>
Diags) {
891 <<
"Didn't expect new diagnostics when adding a/foo.h";
898 [&DiagCount](std::vector<Diag>
Diags) {
904 EXPECT_EQ(DiagCount, 3U);
907 TEST_F(TUSchedulerTests, NoChangeDiags) {
909 TUScheduler S(
CDB, optsForTest(), captureDiags());
912 const auto *Contents =
"int a; int b;";
914 EXPECT_THAT(
Tracer.takeMetric(
"ast_access_read",
"hit"), SizeIs(0));
915 EXPECT_THAT(
Tracer.takeMetric(
"ast_access_read",
"miss"), SizeIs(0));
916 EXPECT_THAT(
Tracer.takeMetric(
"ast_access_diag",
"hit"), SizeIs(0));
917 EXPECT_THAT(
Tracer.takeMetric(
"ast_access_diag",
"miss"), SizeIs(0));
920 [](std::vector<Diag>) { ADD_FAILURE() <<
"Should not be called."; });
921 S.runWithAST(
"touchAST", FooCpp, [](Expected<InputsAndAST> IA) {
923 cantFail(std::move(IA));
926 EXPECT_THAT(
Tracer.takeMetric(
"ast_access_read",
"hit"), SizeIs(0));
927 EXPECT_THAT(
Tracer.takeMetric(
"ast_access_read",
"miss"), SizeIs(1));
931 std::atomic<bool> SeenDiags(
false);
933 [&](std::vector<Diag>) { SeenDiags =
true; });
935 ASSERT_TRUE(SeenDiags);
936 EXPECT_THAT(
Tracer.takeMetric(
"ast_access_diag",
"hit"), SizeIs(1));
937 EXPECT_THAT(
Tracer.takeMetric(
"ast_access_diag",
"miss"), SizeIs(0));
943 [&](std::vector<Diag>) { ADD_FAILURE() <<
"Should not be called."; });
947 TEST_F(TUSchedulerTests, Run) {
948 for (
bool Sync : {
false,
true}) {
949 auto Opts = optsForTest();
951 Opts.AsyncThreadsCount = 0;
952 TUScheduler S(
CDB, Opts);
954 S.run(
"add 1",
"", [&] { ++
Counter; });
955 S.run(
"add 2",
"", [&] {
Counter += 2; });
959 Notification TaskRun;
961 WithContextValue CtxWithKey(TestKey, 10);
962 const char *
Path =
"somepath";
963 S.run(
"props context",
Path, [&] {
965 EXPECT_EQ(
Path, boundPath());
972 TEST_F(TUSchedulerTests, TUStatus) {
973 class CaptureTUStatus :
public ClangdServer::Callbacks {
975 void onFileUpdated(
PathRef File,
const TUStatus &Status)
override {
976 auto ASTAction = Status.ASTActivity.K;
978 std::lock_guard<std::mutex> Lock(Mutex);
985 if (ASTActions.empty() || ASTActions.back() != ASTAction)
986 ASTActions.push_back(ASTAction);
987 if (PreambleActions.empty() || PreambleActions.back() !=
PreambleAction)
991 std::vector<PreambleAction> preambleStatuses() {
992 std::lock_guard<std::mutex> Lock(Mutex);
993 return PreambleActions;
996 std::vector<ASTAction::Kind> astStatuses() {
997 std::lock_guard<std::mutex> Lock(Mutex);
1003 std::vector<ASTAction::Kind> ASTActions;
1004 std::vector<PreambleAction> PreambleActions;
1007 MockCompilationDatabase
CDB;
1009 Annotations
Code(
"int m^ain () {}");
1013 Server.addDocument(
testPath(
"foo.cpp"),
Code.code(),
"1",
1015 ASSERT_TRUE(Server.blockUntilIdleForTest());
1016 Server.locateSymbolAt(
testPath(
"foo.cpp"),
Code.point(),
1017 [](
Expected<std::vector<LocatedSymbol>> Result) {
1018 ASSERT_TRUE((bool)Result);
1020 ASSERT_TRUE(Server.blockUntilIdleForTest());
1022 EXPECT_THAT(CaptureTUStatus.preambleStatuses(),
1033 EXPECT_THAT(CaptureTUStatus.astStatuses(),
1049 TEST_F(TUSchedulerTests, CommandLineErrors) {
1056 TUScheduler S(
CDB, optsForTest(), captureDiags());
1058 updateWithDiags(S,
testPath(
"foo.cpp"),
"void test() {}",
1073 TEST_F(TUSchedulerTests, CommandLineWarnings) {
1080 TUScheduler S(
CDB, optsForTest(), captureDiags());
1082 updateWithDiags(S,
testPath(
"foo.cpp"),
"void test() {}",
1092 TEST(DebouncePolicy, Compute) {
1093 namespace c = std::chrono;
1094 DebouncePolicy::clock::duration History[] = {
1100 DebouncePolicy Policy;
1101 Policy.Min = c::seconds(3);
1102 Policy.Max = c::seconds(25);
1104 auto Compute = [&](llvm::ArrayRef<DebouncePolicy::clock::duration> History) {
1105 return c::duration_cast<c::duration<float, c::seconds::period>>(
1106 Policy.compute(History))
1109 EXPECT_NEAR(10, Compute(History), 0.01) <<
"(upper) median = 10";
1110 Policy.RebuildRatio = 1.5;
1111 EXPECT_NEAR(15, Compute(History), 0.01) <<
"median = 10, ratio = 1.5";
1112 Policy.RebuildRatio = 3;
1113 EXPECT_NEAR(25, Compute(History), 0.01) <<
"constrained by max";
1114 Policy.RebuildRatio = 0;
1115 EXPECT_NEAR(3, Compute(History), 0.01) <<
"constrained by min";
1116 EXPECT_NEAR(25, Compute({}), 0.01) <<
"no history -> max";
1119 TEST_F(TUSchedulerTests, AsyncPreambleThread) {
1122 class BlockPreambleThread :
public ParsingCallbacks {
1124 BlockPreambleThread(llvm::StringRef BlockVersion, Notification &N)
1125 : BlockVersion(BlockVersion), N(N) {}
1126 void onPreambleAST(
PathRef Path, llvm::StringRef Version, ASTContext &
Ctx,
1127 Preprocessor &,
const CanonicalIncludes &)
override {
1128 if (Version == BlockVersion)
1133 llvm::StringRef BlockVersion;
1137 static constexpr llvm::StringLiteral InputsV0 =
"v0";
1138 static constexpr llvm::StringLiteral InputsV1 =
"v1";
1140 TUScheduler S(
CDB, optsForTest(),
1141 std::make_unique<BlockPreambleThread>(InputsV1, Ready));
1144 auto PI = getInputs(File,
"");
1145 PI.Version = InputsV0.str();
1150 PI.Version = InputsV1.str();
1154 Notification RunASTAction;
1157 S.runWithAST(
"test", File, [&](Expected<InputsAndAST> AST) {
1158 ASSERT_TRUE(
bool(AST));
1162 EXPECT_THAT(AST->Inputs.Version, InputsV1.str());
1163 RunASTAction.notify();
1165 RunASTAction.wait();
1169 TEST_F(TUSchedulerTests, OnlyPublishWhenPreambleIsBuilt) {
1170 struct PreamblePublishCounter :
public ParsingCallbacks {
1171 PreamblePublishCounter(
int &PreamblePublishCount)
1172 : PreamblePublishCount(PreamblePublishCount) {}
1173 void onPreamblePublished(
PathRef File)
override { ++PreamblePublishCount; }
1174 int &PreamblePublishCount;
1177 int PreamblePublishCount = 0;
1178 TUScheduler S(
CDB, optsForTest(),
1179 std::make_unique<PreamblePublishCounter>(PreamblePublishCount));
1184 EXPECT_EQ(PreamblePublishCount, 1);
1188 EXPECT_EQ(PreamblePublishCount, 1);
1192 EXPECT_EQ(PreamblePublishCount, 2);
1197 TEST_F(TUSchedulerTests, IncluderCache) {
1198 static std::string Main =
testPath(
"main.cpp"), Main2 =
testPath(
"main2.cpp"),
1201 Unreliable =
testPath(
"unreliable.h"),
1203 NotIncluded =
testPath(
"not_included.h");
1204 struct NoHeadersCDB :
public GlobalCompilationDatabase {
1205 llvm::Optional<tooling::CompileCommand>
1206 getCompileCommand(
PathRef File)
const override {
1207 if (File == NoCmd || File == NotIncluded || FailAll)
1209 auto Basic = getFallbackCommand(File);
1210 Basic.Heuristic.clear();
1211 if (File == Unreliable) {
1212 Basic.Heuristic =
"not reliable";
1213 }
else if (File == Main) {
1214 Basic.CommandLine.push_back(
"-DMAIN");
1215 }
else if (File == Main2) {
1216 Basic.CommandLine.push_back(
"-DMAIN2");
1217 }
else if (File == Main3) {
1218 Basic.CommandLine.push_back(
"-DMAIN3");
1223 std::atomic<bool> FailAll{
false};
1225 TUScheduler S(
CDB, optsForTest());
1226 auto GetFlags = [&](
PathRef Header) {
1229 tooling::CompileCommand Cmd;
1231 [&](llvm::Expected<InputsAndPreamble>
Inputs) {
1233 Cmd = std::move(
Inputs->Command);
1236 return Cmd.CommandLine;
1239 for (
const auto &
Path : {NoCmd, Unreliable, OK, NotIncluded})
1243 EXPECT_THAT(GetFlags(Main), Contains(
"-DMAIN")) <<
"sanity check";
1244 EXPECT_THAT(GetFlags(NoCmd), Not(Contains(
"-DMAIN"))) <<
"no includes yet";
1247 const char *AllIncludes = R
"cpp(
1250 #include "unreliable.h"
1254 EXPECT_THAT(GetFlags(NoCmd), Contains("-DMAIN"))
1255 <<
"Included from main file, has no own command";
1256 EXPECT_THAT(GetFlags(Unreliable), Contains(
"-DMAIN"))
1257 <<
"Included from main file, own command is heuristic";
1258 EXPECT_THAT(GetFlags(OK), Not(Contains(
"-DMAIN")))
1259 <<
"Included from main file, but own command is used";
1260 EXPECT_THAT(GetFlags(NotIncluded), Not(Contains(
"-DMAIN")))
1261 <<
"Not included from main file";
1264 std::string SomeIncludes = R
"cpp(
1266 #include "not_included.h"
1270 EXPECT_THAT(GetFlags(NoCmd),
1271 AllOf(Contains("-DMAIN"), Not(Contains(
"-DMAIN2"))))
1272 <<
"mainfile association is stable";
1273 EXPECT_THAT(GetFlags(NotIncluded),
1274 AllOf(Contains(
"-DMAIN2"), Not(Contains(
"-DMAIN"))))
1275 <<
"new headers are associated with new mainfile";
1281 EXPECT_THAT(GetFlags(NoCmd),
1282 AllOf(Contains(
"-DMAIN"), Not(Contains(
"-DMAIN2"))))
1283 <<
"mainfile association not updated yet!";
1288 EXPECT_THAT(GetFlags(NoCmd), Contains(
"-DMAIN3"))
1289 <<
"association invalidated and then claimed by main3";
1290 EXPECT_THAT(GetFlags(Unreliable), Contains(
"-DMAIN"))
1291 <<
"association invalidated but not reclaimed";
1292 EXPECT_THAT(GetFlags(NotIncluded), Contains(
"-DMAIN2"))
1293 <<
"association still valid";
1297 EXPECT_THAT(GetFlags(NoCmd), Not(Contains(
"-DMAIN3")))
1298 <<
"association should've been invalidated.";
1304 CDB.FailAll =
false;
1307 EXPECT_THAT(GetFlags(NoCmd), Contains(
"-DMAIN3"))
1308 <<
"association invalidated and then claimed by main3";
1311 TEST_F(TUSchedulerTests, PreservesLastActiveFile) {
1312 for (
bool Sync : {
false,
true}) {
1313 auto Opts = optsForTest();
1315 Opts.AsyncThreadsCount = 0;
1316 TUScheduler S(
CDB, Opts);
1318 auto CheckNoFileActionsSeesLastActiveFile =
1319 [&](llvm::StringRef LastActiveFile) {
1324 S.run(
"run-UsesLastActiveFile",
"", [&] {
1326 EXPECT_EQ(LastActiveFile, boundPath());
1328 S.runQuick(
"runQuick-UsesLastActiveFile",
"", [&] {
1330 EXPECT_EQ(LastActiveFile, boundPath());
1337 CheckNoFileActionsSeesLastActiveFile(
"");
1343 CheckNoFileActionsSeesLastActiveFile(
Path);
1347 CheckNoFileActionsSeesLastActiveFile(
Path);
1351 S.runWithAST(
Path,
Path, [](llvm::Expected<InputsAndAST> Inp) {
1352 EXPECT_TRUE(
bool(Inp));
1354 CheckNoFileActionsSeesLastActiveFile(
Path);
1360 [](llvm::Expected<InputsAndPreamble> Inp) { EXPECT_TRUE(
bool(Inp)); });
1361 CheckNoFileActionsSeesLastActiveFile(
Path);
1365 CheckNoFileActionsSeesLastActiveFile(
Path);
1368 auto LastActive =
Path;
1371 CheckNoFileActionsSeesLastActiveFile(LastActive);