19 #include "clang/Basic/DiagnosticDriver.h" 20 #include "llvm/ADT/ArrayRef.h" 21 #include "llvm/ADT/STLExtras.h" 22 #include "llvm/ADT/ScopeExit.h" 23 #include "gmock/gmock.h" 24 #include "gtest/gtest.h" 32 using ::testing::AnyOf;
33 using ::testing::Each;
34 using ::testing::ElementsAre;
36 using ::testing::Field;
37 using ::testing::IsEmpty;
38 using ::testing::Pointee;
39 using ::testing::UnorderedElementsAre;
41 MATCHER_P2(TUState, State, ActionName,
"") {
42 return arg.Action.S == State && arg.Action.Name == ActionName;
45 class TUSchedulerTests :
public ::testing::Test {
49 Inputs.CompileCommand = *
CDB.getCompileCommand(File);
51 Inputs.Contents = std::move(Contents);
52 Inputs.Opts = ParseOptions();
56 void updateWithCallback(TUScheduler &S,
PathRef File,
58 llvm::unique_function<
void()> CB) {
59 WithContextValue
Ctx(llvm::make_scope_exit(std::move(CB)));
60 S.update(File, getInputs(File, Contents), WD);
63 static Key<llvm::unique_function<void(PathRef File, std::vector<Diag>)>>
68 static std::unique_ptr<ParsingCallbacks> captureDiags() {
69 class CaptureDiags :
public ParsingCallbacks {
71 void onMainAST(
PathRef File, ParsedAST &
AST, PublishFn Publish)
override {
72 reportDiagnostics(File, AST.getDiagnostics(), Publish);
75 void onFailedAST(
PathRef File, std::vector<Diag> Diags,
76 PublishFn Publish)
override {
77 reportDiagnostics(File, Diags, Publish);
81 void reportDiagnostics(
PathRef File, llvm::ArrayRef<Diag> Diags,
88 llvm::unique_function<void(PathRef, std::vector<Diag>)
> &> (*D)(
89 File, std::move(Diags));
93 return std::make_unique<CaptureDiags>();
99 void updateWithDiags(TUScheduler &S,
PathRef File, ParseInputs Inputs,
101 llvm::unique_function<
void(std::vector<Diag>)> CB) {
102 Path OrigFile = File.str();
104 [OrigFile, CB = std::move(CB)](
105 PathRef File, std::vector<Diag> Diags)
mutable {
106 assert(File == OrigFile);
107 CB(std::move(Diags));
109 S.update(File, std::move(Inputs), WD);
112 void updateWithDiags(TUScheduler &S,
PathRef File, llvm::StringRef Contents,
114 llvm::unique_function<
void(std::vector<Diag>)> CB) {
115 return updateWithDiags(S, File, getInputs(File, Contents), WD,
121 MockCompilationDatabase
CDB;
124 Key<llvm::unique_function<void(PathRef File, std::vector<Diag>)>>
127 TEST_F(TUSchedulerTests, MissingFiles) {
130 std::chrono::steady_clock::duration::zero(),
131 ASTRetentionPolicy());
139 EXPECT_EQ(S.getContents(Added),
"");
141 EXPECT_EQ(S.getContents(Added),
"x");
154 S.runWithAST(
"", Added,
155 [&](Expected<InputsAndAST> AST) { EXPECT_TRUE(
bool(AST)); });
157 [&](Expected<InputsAndPreamble> Preamble) {
158 EXPECT_TRUE(
bool(Preamble));
160 EXPECT_EQ(S.getContents(Added),
"x");
162 EXPECT_EQ(S.getContents(Added),
"");
165 S.runWithAST(
"", Added,
166 [&](Expected<InputsAndAST> AST) {
EXPECT_ERROR(AST); });
168 [&](Expected<InputsAndPreamble> Preamble) {
169 ASSERT_FALSE(
bool(Preamble));
170 llvm::consumeError(Preamble.takeError());
177 std::atomic<int> CallbackCount(0);
184 true, captureDiags(),
185 std::chrono::steady_clock::duration::zero(),
186 ASTRetentionPolicy());
189 [&](std::vector<Diag>) { Ready.wait(); });
191 [&](std::vector<Diag>) { ++CallbackCount; });
193 [&](std::vector<Diag>) {
195 <<
"auto should have been cancelled by auto";
198 [&](std::vector<Diag>) {
199 ADD_FAILURE() <<
"no diags should not be called back";
202 [&](std::vector<Diag>) { ++CallbackCount; });
207 EXPECT_EQ(2, CallbackCount);
210 TEST_F(TUSchedulerTests, Debounce) {
211 std::atomic<int> CallbackCount(0);
214 true, captureDiags(),
215 std::chrono::seconds(1),
216 ASTRetentionPolicy());
220 [&](std::vector<Diag>) {
222 <<
"auto should have been debounced and canceled";
224 std::this_thread::sleep_for(std::chrono::milliseconds(200));
226 [&](std::vector<Diag>) { ++CallbackCount; });
227 std::this_thread::sleep_for(std::chrono::seconds(2));
229 [&](std::vector<Diag>) { ++CallbackCount; });
233 EXPECT_EQ(2, CallbackCount);
236 static std::vector<std::string> includes(
const PreambleData *
Preamble) {
237 std::vector<std::string> Result;
239 for (
const auto &Inclusion : Preamble->Includes.MainFileIncludes)
240 Result.push_back(Inclusion.Written);
244 TEST_F(TUSchedulerTests, PreambleConsistency) {
245 std::atomic<int> CallbackCount(0);
247 Notification InconsistentReadDone;
251 std::chrono::steady_clock::duration::zero(),
252 ASTRetentionPolicy());
263 InconsistentReadDone.wait();
268 std::this_thread::sleep_for(std::chrono::milliseconds(100));
273 [&](Expected<InputsAndPreamble> Pre) {
274 ASSERT_TRUE(
bool(Pre));
276 EXPECT_THAT(includes(Pre->Preamble),
278 InconsistentReadDone.notify();
282 [&](Expected<InputsAndPreamble> Pre) {
283 ASSERT_TRUE(
bool(Pre));
284 EXPECT_THAT(includes(Pre->Preamble),
289 EXPECT_EQ(2, CallbackCount);
292 TEST_F(TUSchedulerTests, Cancellation) {
302 std::vector<std::string> DiagsSeen, ReadsSeen, ReadsCanceled;
304 Notification Proceed;
308 std::chrono::steady_clock::duration::zero(),
309 ASTRetentionPolicy());
312 auto Update = [&](std::string ID) ->
Canceler {
314 WithContext C(std::move(T.first));
317 [&, ID](std::vector<Diag> Diags) { DiagsSeen.push_back(ID); });
318 return std::move(T.second);
323 WithContext C(std::move(T.first));
324 S.runWithAST(ID,
Path, [&, ID](llvm::Expected<InputsAndAST> E) {
325 if (
auto Err = E.takeError()) {
326 if (Err.isA<CancelledError>()) {
327 ReadsCanceled.push_back(ID);
328 consumeError(std::move(Err));
330 ADD_FAILURE() <<
"Non-cancelled error for " << ID <<
": " 334 ReadsSeen.push_back(ID);
337 return std::move(T.second);
341 [&]() { Proceed.wait(); });
354 EXPECT_THAT(DiagsSeen, ElementsAre(
"U2",
"U3"))
355 <<
"U1 and all dependent reads were cancelled. " 356 "U2 has a dependent read R2A. " 357 "U3 was not cancelled.";
358 EXPECT_THAT(ReadsSeen, ElementsAre(
"R2B"))
359 <<
"All reads other than R2B were cancelled";
360 EXPECT_THAT(ReadsCanceled, ElementsAre(
"R1",
"R2A",
"R3"))
361 <<
"All reads other than R2B were cancelled";
364 TEST_F(TUSchedulerTests, ManyUpdates) {
365 const int FilesCount = 3;
366 const int UpdatesPerFile = 10;
369 int TotalASTReads = 0;
370 int TotalPreambleReads = 0;
371 int TotalUpdates = 0;
376 true, captureDiags(),
377 std::chrono::milliseconds(50),
378 ASTRetentionPolicy());
380 std::vector<std::string>
Files;
381 for (
int I = 0; I < FilesCount; ++I) {
382 std::string
Name =
"foo" + std::to_string(I) +
".cpp";
384 this->Files[Files.back()] =
"";
387 StringRef Contents1 = R
"cpp(int a;)cpp"; 388 StringRef Contents2 = R"cpp(int main() { return 1; })cpp"; 389 StringRef Contents3 = R"cpp(int a; int b; int sum() { return a + b; })cpp"; 391 StringRef AllContents[] = {Contents1, Contents2, Contents3}; 392 const int AllContentsSize = 3;
396 static Key<int> NonceKey;
399 for (
int FileI = 0; FileI < FilesCount; ++FileI) {
400 for (
int UpdateI = 0; UpdateI < UpdatesPerFile; ++UpdateI) {
401 auto Contents = AllContents[(FileI + UpdateI) % AllContentsSize];
403 auto File = Files[FileI];
404 auto Inputs = getInputs(File, Contents.str());
406 WithContextValue WithNonce(NonceKey, ++Nonce);
409 [File, Nonce, &Mut, &TotalUpdates](std::vector<Diag>) {
412 std::lock_guard<std::mutex> Lock(Mut);
418 WithContextValue WithNonce(NonceKey, ++Nonce);
421 [File, Inputs, Nonce, &Mut,
422 &TotalASTReads](Expected<InputsAndAST>
AST) {
425 ASSERT_TRUE((
bool)AST);
426 EXPECT_EQ(AST->Inputs.FS, Inputs.FS);
427 EXPECT_EQ(AST->Inputs.Contents, Inputs.Contents);
429 std::lock_guard<std::mutex> Lock(Mut);
436 WithContextValue WithNonce(NonceKey, ++Nonce);
439 [File, Inputs, Nonce, &Mut,
440 &TotalPreambleReads](Expected<InputsAndPreamble> Preamble) {
443 ASSERT_TRUE((
bool)Preamble);
444 EXPECT_EQ(Preamble->Contents, Inputs.Contents);
446 std::lock_guard<std::mutex> Lock(Mut);
447 ++TotalPreambleReads;
456 std::lock_guard<std::mutex> Lock(Mut);
457 EXPECT_EQ(TotalUpdates, FilesCount * UpdatesPerFile);
458 EXPECT_EQ(TotalASTReads, FilesCount * UpdatesPerFile);
459 EXPECT_EQ(TotalPreambleReads, FilesCount * UpdatesPerFile);
462 TEST_F(TUSchedulerTests, EvictedAST) {
463 std::atomic<int> BuiltASTCounter(0);
464 ASTRetentionPolicy Policy;
465 Policy.MaxRetainedASTs = 2;
469 std::chrono::steady_clock::duration::zero(),
472 llvm::StringLiteral SourceContents = R
"cpp( 476 llvm::StringLiteral OtherSourceContents = R"cpp( 488 [&BuiltASTCounter]() { ++BuiltASTCounter; });
490 ASSERT_EQ(BuiltASTCounter.load(), 1);
495 [&BuiltASTCounter]() { ++BuiltASTCounter; });
497 [&BuiltASTCounter]() { ++BuiltASTCounter; });
499 ASSERT_EQ(BuiltASTCounter.load(), 3);
502 ASSERT_THAT(S.getFilesWithCachedAST(), UnorderedElementsAre(Bar, Baz));
506 [&BuiltASTCounter]() { ++BuiltASTCounter; });
508 ASSERT_EQ(BuiltASTCounter.load(), 4);
512 EXPECT_THAT(S.getFilesWithCachedAST(),
513 UnorderedElementsAre(Foo, AnyOf(Bar, Baz)));
516 TEST_F(TUSchedulerTests, EmptyPreamble) {
520 std::chrono::steady_clock::duration::zero(),
521 ASTRetentionPolicy());
526 Files[Header] =
"void foo()";
528 auto WithPreamble = R
"cpp( 532 auto WithEmptyPreamble = R
"cpp(int main() {})cpp"; 536 [&](Expected<InputsAndPreamble> Preamble) {
539 cantFail(std::move(Preamble)).Preamble->Preamble.getBounds().Size,
551 [&](Expected<InputsAndPreamble> Preamble) {
554 cantFail(std::move(Preamble)).Preamble->Preamble.getBounds().Size,
559 TEST_F(TUSchedulerTests, RunWaitsForPreamble) {
565 std::chrono::steady_clock::duration::zero(),
566 ASTRetentionPolicy());
568 auto NonEmptyPreamble = R
"cpp( 574 constexpr int ReadsToSchedule = 10;
575 std::mutex PreamblesMut;
576 std::vector<const void *> Preambles(ReadsToSchedule,
nullptr);
578 for (
int I = 0; I < ReadsToSchedule; ++I) {
581 [I, &PreamblesMut, &Preambles](Expected<InputsAndPreamble> IP) {
582 std::lock_guard<std::mutex> Lock(PreamblesMut);
583 Preambles[I] = cantFail(std::move(IP)).Preamble;
588 std::lock_guard<std::mutex> Lock(PreamblesMut);
589 ASSERT_NE(Preambles[0],
nullptr);
590 ASSERT_THAT(Preambles, Each(Preambles[0]));
593 TEST_F(TUSchedulerTests, NoopOnEmptyChanges) {
596 true, captureDiags(),
597 std::chrono::steady_clock::duration::zero(),
598 ASTRetentionPolicy());
603 Files[Header] =
"int a;";
606 auto SourceContents = R
"cpp( 612 auto DoUpdate = [&](std::string
Contents) ->
bool {
613 std::atomic<bool> Updated(
false);
616 [&Updated](std::vector<Diag>) { Updated =
true; });
619 ADD_FAILURE() <<
"Updated has not finished in one second. Threading bug?";
624 ASSERT_TRUE(DoUpdate(SourceContents));
625 ASSERT_FALSE(DoUpdate(SourceContents));
629 ASSERT_TRUE(DoUpdate(SourceContents));
630 ASSERT_FALSE(DoUpdate(SourceContents));
633 auto OtherSourceContents = R
"cpp( 637 ASSERT_TRUE(DoUpdate(OtherSourceContents)); 638 ASSERT_FALSE(DoUpdate(OtherSourceContents)); 641 CDB.ExtraClangFlags.push_back(
"-DSOMETHING");
642 ASSERT_TRUE(DoUpdate(OtherSourceContents));
643 ASSERT_FALSE(DoUpdate(OtherSourceContents));
646 TEST_F(TUSchedulerTests, NoChangeDiags) {
649 true, captureDiags(),
650 std::chrono::steady_clock::duration::zero(),
651 ASTRetentionPolicy());
654 auto Contents =
"int a; int b;";
658 [](std::vector<Diag>) { ADD_FAILURE() <<
"Should not be called."; });
659 S.runWithAST(
"touchAST", FooCpp, [](Expected<InputsAndAST> IA) {
661 cantFail(std::move(IA));
667 std::atomic<bool> SeenDiags(
false);
669 [&](std::vector<Diag>) { SeenDiags =
true; });
671 ASSERT_TRUE(SeenDiags);
677 [&](std::vector<Diag>) { ADD_FAILURE() <<
"Should not be called."; });
681 TEST_F(TUSchedulerTests, Run) {
684 std::chrono::steady_clock::duration::zero(),
685 ASTRetentionPolicy());
686 std::atomic<int> Counter(0);
687 S.run(
"add 1", [&] { ++Counter; });
688 S.run(
"add 2", [&] { Counter += 2; });
690 EXPECT_EQ(Counter.load(), 3);
693 TEST_F(TUSchedulerTests, TUStatus) {
694 class CaptureTUStatus :
public DiagnosticsConsumer {
696 void onDiagnosticsReady(
PathRef File,
697 std::vector<Diag> Diagnostics)
override {}
699 void onFileUpdated(
PathRef File,
const TUStatus &Status)
override {
700 std::lock_guard<std::mutex> Lock(Mutex);
701 AllStatus.push_back(Status);
704 std::vector<TUStatus> allStatus() {
705 std::lock_guard<std::mutex> Lock(Mutex);
711 std::vector<TUStatus> AllStatus;
714 MockCompilationDatabase
CDB;
716 Annotations
Code(
"int m^ain () {}");
721 Server.locateSymbolAt(
testPath(
"foo.cpp"), Code.point(),
722 [](Expected<std::vector<LocatedSymbol>> Result) {
723 ASSERT_TRUE((
bool)Result);
726 ASSERT_TRUE(Server.blockUntilIdleForTest());
728 EXPECT_THAT(CaptureTUStatus.allStatus(),
740 TEST_F(TUSchedulerTests, CommandLineErrors) {
742 CDB.ExtraClangFlags = {
"-fsome-unknown-flag"};
748 true, captureDiags(),
749 std::chrono::steady_clock::duration::zero(),
750 ASTRetentionPolicy());
752 std::vector<Diag> Diagnostics;
753 updateWithDiags(S,
testPath(
"foo.cpp"),
"void test() {}",
755 Diagnostics = std::move(D);
768 TEST_F(TUSchedulerTests, CommandLineWarnings) {
770 CDB.ExtraClangFlags = {
"-Wsome-unknown-warning"};
776 true, captureDiags(),
777 std::chrono::steady_clock::duration::zero(),
778 ASTRetentionPolicy());
780 std::vector<Diag> Diagnostics;
781 updateWithDiags(S,
testPath(
"foo.cpp"),
"void test() {}",
783 Diagnostics = std::move(D);
788 EXPECT_THAT(Diagnostics, IsEmpty());
WantDiagnostics
Determines whether diagnostics should be generated for a file snapshot.
MockCompilationDatabase CDB
The preamble may be generated from an older version of the file.
Diagnostics must be generated for this snapshot.
std::function< void()> Canceler
A canceller requests cancellation of a task, when called.
static llvm::Optional< llvm::StringRef > getFileBeingProcessedInContext()
#define EXPECT_ERROR(expectedValue)
llvm::StringRef PathRef
A typedef to represent a ref to file path.
static llvm::StringRef toString(SpecialMemberFunctionsCheck::SpecialMemberFunctionKind K)
static Options optsForTest()
TEST_F(BackgroundIndexTest, NoCrashOnErrorFile)
const Type * get(const Key< Type > &Key) const
Get data stored for a typed Key.
llvm::IntrusiveRefCntPtr< llvm::vfs::FileSystem > buildTestFS(llvm::StringMap< std::string > const &Files, llvm::StringMap< time_t > const &Timestamps)
std::string testPath(PathRef File)
std::string Path
A typedef to represent a file path.
static const Context & current()
Returns the context for the current thread, creating it if needed.
static constexpr llvm::StringLiteral Name
std::pair< Context, Canceler > cancelableTask()
Defines a new task whose cancellation may be requested.
unsigned getDefaultAsyncThreadsCount()
Returns a number of a default async threads to use for TUScheduler.
llvm::StringMap< time_t > Timestamps
const PreambleData * Preamble
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
static Key< llvm::unique_function< void(PathRef File, std::vector< Diag >)> > DiagsCallbackKey
Deadline timeoutSeconds(llvm::Optional< double > Seconds)
Makes a deadline from a timeout in seconds. None means wait forever.
Diagnostics must not be generated for this snapshot.
llvm::StringMap< std::string > Files
The preamble is generated from the current version of the file.