14 #include "llvm/ADT/ScopeExit.h" 15 #include "gmock/gmock.h" 16 #include "gtest/gtest.h" 24 using ::testing::AnyOf;
25 using ::testing::Each;
26 using ::testing::ElementsAre;
27 using ::testing::Pointee;
28 using ::testing::UnorderedElementsAre;
30 MATCHER_P2(TUState, State, ActionName,
"") {
31 return arg.Action.S == State && arg.Action.Name == ActionName;
34 class TUSchedulerTests :
public ::testing::Test {
38 Inputs.CompileCommand = *
CDB.getCompileCommand(File);
40 Inputs.Contents = std::move(Contents);
41 Inputs.Opts = ParseOptions();
45 void updateWithCallback(TUScheduler &S,
PathRef File,
47 llvm::unique_function<
void()> CB) {
48 WithContextValue
Ctx(llvm::make_scope_exit(std::move(CB)));
49 S.update(File, getInputs(File, Contents), WD);
52 static Key<llvm::unique_function<void(PathRef File, std::vector<Diag>)>>
57 static std::unique_ptr<ParsingCallbacks> captureDiags() {
58 class CaptureDiags :
public ParsingCallbacks {
59 void onDiagnostics(
PathRef File, std::vector<Diag> Diags)
override {
63 const_cast<llvm::unique_function<void(PathRef, std::vector<Diag>)
> &> (
67 return llvm::make_unique<CaptureDiags>();
73 void updateWithDiags(TUScheduler &S,
PathRef File, ParseInputs Inputs,
75 llvm::unique_function<
void(std::vector<Diag>)> CB) {
76 Path OrigFile = File.str();
80 [OrigFile](decltype(CB) CB,
PathRef File, std::vector<Diag> Diags) {
81 assert(File == OrigFile);
85 S.update(File, std::move(Inputs), WD);
88 void updateWithDiags(TUScheduler &S,
PathRef File, llvm::StringRef Contents,
90 llvm::unique_function<
void(std::vector<Diag>)> CB) {
91 return updateWithDiags(S, File, getInputs(File, Contents), WD,
95 llvm::StringMap<std::string>
Files;
97 MockCompilationDatabase
CDB;
100 Key<llvm::unique_function<void(PathRef File, std::vector<Diag>)>>
103 TEST_F(TUSchedulerTests, MissingFiles) {
106 std::chrono::steady_clock::duration::zero(),
107 ASTRetentionPolicy());
115 EXPECT_EQ(S.getContents(Added),
"");
117 EXPECT_EQ(S.getContents(Added),
"x");
130 S.runWithAST(
"", Added,
131 [&](Expected<InputsAndAST> AST) { EXPECT_TRUE(
bool(AST)); });
133 [&](Expected<InputsAndPreamble> Preamble) {
134 EXPECT_TRUE(
bool(Preamble));
136 EXPECT_EQ(S.getContents(Added),
"x");
138 EXPECT_EQ(S.getContents(Added),
"");
141 S.runWithAST(
"", Added,
142 [&](Expected<InputsAndAST> AST) {
EXPECT_ERROR(AST); });
144 [&](Expected<InputsAndPreamble> Preamble) {
145 ASSERT_FALSE(
bool(Preamble));
146 llvm::consumeError(Preamble.takeError());
153 std::atomic<int> CallbackCount(0);
160 true, captureDiags(),
161 std::chrono::steady_clock::duration::zero(),
162 ASTRetentionPolicy());
165 [&](std::vector<Diag>) { Ready.wait(); });
167 [&](std::vector<Diag>) { ++CallbackCount; });
169 [&](std::vector<Diag>) {
171 <<
"auto should have been cancelled by auto";
174 [&](std::vector<Diag>) {
175 ADD_FAILURE() <<
"no diags should not be called back";
178 [&](std::vector<Diag>) { ++CallbackCount; });
183 EXPECT_EQ(2, CallbackCount);
186 TEST_F(TUSchedulerTests, Debounce) {
187 std::atomic<int> CallbackCount(0);
190 true, captureDiags(),
191 std::chrono::seconds(1),
192 ASTRetentionPolicy());
196 [&](std::vector<Diag>) {
198 <<
"auto should have been debounced and canceled";
200 std::this_thread::sleep_for(std::chrono::milliseconds(200));
202 [&](std::vector<Diag>) { ++CallbackCount; });
203 std::this_thread::sleep_for(std::chrono::seconds(2));
205 [&](std::vector<Diag>) { ++CallbackCount; });
209 EXPECT_EQ(2, CallbackCount);
212 static std::vector<std::string> includes(
const PreambleData *
Preamble) {
213 std::vector<std::string>
Result;
215 for (
const auto &Inclusion : Preamble->Includes.MainFileIncludes)
216 Result.push_back(Inclusion.Written);
220 TEST_F(TUSchedulerTests, PreambleConsistency) {
221 std::atomic<int> CallbackCount(0);
223 Notification InconsistentReadDone;
227 std::chrono::steady_clock::duration::zero(),
228 ASTRetentionPolicy());
239 InconsistentReadDone.wait();
244 std::this_thread::sleep_for(std::chrono::milliseconds(100));
249 [&](Expected<InputsAndPreamble> Pre) {
250 ASSERT_TRUE(
bool(Pre));
252 EXPECT_THAT(includes(Pre->Preamble),
254 InconsistentReadDone.notify();
258 [&](Expected<InputsAndPreamble> Pre) {
259 ASSERT_TRUE(
bool(Pre));
260 EXPECT_THAT(includes(Pre->Preamble),
265 EXPECT_EQ(2, CallbackCount);
268 TEST_F(TUSchedulerTests, Cancellation) {
278 std::vector<std::string> DiagsSeen, ReadsSeen, ReadsCanceled;
280 Notification Proceed;
284 std::chrono::steady_clock::duration::zero(),
285 ASTRetentionPolicy());
288 auto Update = [&](std::string ID) ->
Canceler {
290 WithContext C(std::move(T.first));
293 [&, ID](std::vector<Diag> Diags) { DiagsSeen.push_back(ID); });
294 return std::move(T.second);
299 WithContext C(std::move(T.first));
300 S.runWithAST(ID,
Path, [&, ID](llvm::Expected<InputsAndAST> E) {
301 if (
auto Err = E.takeError()) {
302 if (Err.isA<CancelledError>()) {
303 ReadsCanceled.push_back(ID);
304 consumeError(std::move(Err));
306 ADD_FAILURE() <<
"Non-cancelled error for " << ID <<
": " 310 ReadsSeen.push_back(ID);
313 return std::move(T.second);
317 [&]() { Proceed.wait(); });
330 EXPECT_THAT(DiagsSeen, ElementsAre(
"U2",
"U3"))
331 <<
"U1 and all dependent reads were cancelled. " 332 "U2 has a dependent read R2A. " 333 "U3 was not cancelled.";
334 EXPECT_THAT(ReadsSeen, ElementsAre(
"R2B"))
335 <<
"All reads other than R2B were cancelled";
336 EXPECT_THAT(ReadsCanceled, ElementsAre(
"R1",
"R2A",
"R3"))
337 <<
"All reads other than R2B were cancelled";
340 TEST_F(TUSchedulerTests, ManyUpdates) {
341 const int FilesCount = 3;
342 const int UpdatesPerFile = 10;
345 int TotalASTReads = 0;
346 int TotalPreambleReads = 0;
347 int TotalUpdates = 0;
352 true, captureDiags(),
353 std::chrono::milliseconds(50),
354 ASTRetentionPolicy());
356 std::vector<std::string>
Files;
357 for (
int I = 0; I < FilesCount; ++I) {
358 std::string
Name =
"foo" + std::to_string(I) +
".cpp";
360 this->Files[Files.back()] =
"";
363 StringRef Contents1 = R
"cpp(int a;)cpp"; 364 StringRef Contents2 = R"cpp(int main() { return 1; })cpp"; 365 StringRef Contents3 = R"cpp(int a; int b; int sum() { return a + b; })cpp"; 367 StringRef AllContents[] = {Contents1, Contents2, Contents3}; 368 const int AllContentsSize = 3;
372 static Key<int> NonceKey;
375 for (
int FileI = 0; FileI < FilesCount; ++FileI) {
376 for (
int UpdateI = 0; UpdateI < UpdatesPerFile; ++UpdateI) {
377 auto Contents = AllContents[(FileI + UpdateI) % AllContentsSize];
379 auto File = Files[FileI];
380 auto Inputs = getInputs(File, Contents.str());
382 WithContextValue WithNonce(NonceKey, ++Nonce);
385 [File, Nonce, &Mut, &TotalUpdates](std::vector<Diag>) {
388 std::lock_guard<std::mutex> Lock(Mut);
394 WithContextValue WithNonce(NonceKey, ++Nonce);
397 [File, Inputs, Nonce, &Mut,
398 &TotalASTReads](Expected<InputsAndAST>
AST) {
401 ASSERT_TRUE((
bool)AST);
402 EXPECT_EQ(AST->Inputs.FS, Inputs.FS);
403 EXPECT_EQ(AST->Inputs.Contents, Inputs.Contents);
405 std::lock_guard<std::mutex> Lock(Mut);
412 WithContextValue WithNonce(NonceKey, ++Nonce);
415 [File, Inputs, Nonce, &Mut,
416 &TotalPreambleReads](Expected<InputsAndPreamble> Preamble) {
419 ASSERT_TRUE((
bool)Preamble);
420 EXPECT_EQ(Preamble->Contents, Inputs.Contents);
422 std::lock_guard<std::mutex> Lock(Mut);
423 ++TotalPreambleReads;
432 std::lock_guard<std::mutex> Lock(Mut);
433 EXPECT_EQ(TotalUpdates, FilesCount * UpdatesPerFile);
434 EXPECT_EQ(TotalASTReads, FilesCount * UpdatesPerFile);
435 EXPECT_EQ(TotalPreambleReads, FilesCount * UpdatesPerFile);
438 TEST_F(TUSchedulerTests, EvictedAST) {
439 std::atomic<int> BuiltASTCounter(0);
440 ASTRetentionPolicy Policy;
441 Policy.MaxRetainedASTs = 2;
445 std::chrono::steady_clock::duration::zero(),
448 llvm::StringLiteral SourceContents = R
"cpp( 452 llvm::StringLiteral OtherSourceContents = R"cpp( 464 [&BuiltASTCounter]() { ++BuiltASTCounter; });
466 ASSERT_EQ(BuiltASTCounter.load(), 1);
471 [&BuiltASTCounter]() { ++BuiltASTCounter; });
473 [&BuiltASTCounter]() { ++BuiltASTCounter; });
475 ASSERT_EQ(BuiltASTCounter.load(), 3);
478 ASSERT_THAT(S.getFilesWithCachedAST(), UnorderedElementsAre(Bar, Baz));
482 [&BuiltASTCounter]() { ++BuiltASTCounter; });
484 ASSERT_EQ(BuiltASTCounter.load(), 4);
488 EXPECT_THAT(S.getFilesWithCachedAST(),
489 UnorderedElementsAre(Foo, AnyOf(Bar, Baz)));
492 TEST_F(TUSchedulerTests, EmptyPreamble) {
496 std::chrono::steady_clock::duration::zero(),
497 ASTRetentionPolicy());
502 Files[Header] =
"void foo()";
504 auto WithPreamble = R
"cpp( 508 auto WithEmptyPreamble = R
"cpp(int main() {})cpp"; 512 [&](Expected<InputsAndPreamble> Preamble) {
515 cantFail(std::move(Preamble)).Preamble->Preamble.getBounds().Size,
527 [&](Expected<InputsAndPreamble> Preamble) {
530 cantFail(std::move(Preamble)).Preamble->Preamble.getBounds().Size,
535 TEST_F(TUSchedulerTests, RunWaitsForPreamble) {
541 std::chrono::steady_clock::duration::zero(),
542 ASTRetentionPolicy());
544 auto NonEmptyPreamble = R
"cpp( 550 constexpr int ReadsToSchedule = 10;
551 std::mutex PreamblesMut;
552 std::vector<const void *> Preambles(ReadsToSchedule,
nullptr);
554 for (
int I = 0; I < ReadsToSchedule; ++I) {
557 [I, &PreamblesMut, &Preambles](Expected<InputsAndPreamble> IP) {
558 std::lock_guard<std::mutex> Lock(PreamblesMut);
559 Preambles[I] = cantFail(std::move(IP)).Preamble;
564 std::lock_guard<std::mutex> Lock(PreamblesMut);
565 ASSERT_NE(Preambles[0],
nullptr);
566 ASSERT_THAT(Preambles, Each(Preambles[0]));
569 TEST_F(TUSchedulerTests, NoopOnEmptyChanges) {
572 true, captureDiags(),
573 std::chrono::steady_clock::duration::zero(),
574 ASTRetentionPolicy());
579 Files[Header] =
"int a;";
582 auto SourceContents = R
"cpp( 588 auto DoUpdate = [&](std::string
Contents) ->
bool {
589 std::atomic<bool> Updated(
false);
592 [&Updated](std::vector<Diag>) { Updated =
true; });
595 ADD_FAILURE() <<
"Updated has not finished in one second. Threading bug?";
600 ASSERT_TRUE(DoUpdate(SourceContents));
601 ASSERT_FALSE(DoUpdate(SourceContents));
605 ASSERT_TRUE(DoUpdate(SourceContents));
606 ASSERT_FALSE(DoUpdate(SourceContents));
609 auto OtherSourceContents = R
"cpp( 613 ASSERT_TRUE(DoUpdate(OtherSourceContents)); 614 ASSERT_FALSE(DoUpdate(OtherSourceContents)); 617 CDB.ExtraClangFlags.push_back(
"-DSOMETHING");
618 ASSERT_TRUE(DoUpdate(OtherSourceContents));
619 ASSERT_FALSE(DoUpdate(OtherSourceContents));
622 TEST_F(TUSchedulerTests, NoChangeDiags) {
625 true, captureDiags(),
626 std::chrono::steady_clock::duration::zero(),
627 ASTRetentionPolicy());
630 auto Contents =
"int a; int b;";
634 [](std::vector<Diag>) { ADD_FAILURE() <<
"Should not be called."; });
635 S.runWithAST(
"touchAST", FooCpp, [](Expected<InputsAndAST> IA) {
637 cantFail(std::move(IA));
643 std::atomic<bool> SeenDiags(
false);
645 [&](std::vector<Diag>) { SeenDiags =
true; });
647 ASSERT_TRUE(SeenDiags);
653 [&](std::vector<Diag>) { ADD_FAILURE() <<
"Should not be called."; });
657 TEST_F(TUSchedulerTests, Run) {
660 std::chrono::steady_clock::duration::zero(),
661 ASTRetentionPolicy());
662 std::atomic<int> Counter(0);
663 S.run(
"add 1", [&] { ++Counter; });
664 S.run(
"add 2", [&] { Counter += 2; });
666 EXPECT_EQ(Counter.load(), 3);
669 TEST_F(TUSchedulerTests, TUStatus) {
670 class CaptureTUStatus :
public DiagnosticsConsumer {
672 void onDiagnosticsReady(
PathRef File,
673 std::vector<Diag> Diagnostics)
override {}
675 void onFileUpdated(
PathRef File,
const TUStatus &Status)
override {
676 std::lock_guard<std::mutex> Lock(Mutex);
677 AllStatus.push_back(Status);
680 std::vector<TUStatus> allStatus() {
681 std::lock_guard<std::mutex> Lock(Mutex);
687 std::vector<TUStatus> AllStatus;
690 MockCompilationDatabase
CDB;
692 Annotations Code(
"int m^ain () {}");
697 Server.locateSymbolAt(
testPath(
"foo.cpp"), Code.point(),
698 [](Expected<std::vector<LocatedSymbol>>
Result) {
699 ASSERT_TRUE((
bool)
Result);
702 ASSERT_TRUE(Server.blockUntilIdleForTest());
704 EXPECT_THAT(CaptureTUStatus.allStatus(),
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)
ForwardBinder< Func, Args... > Bind(Func F, Args &&... As)
Creates an object that stores a callable (F) and first arguments to the callable (As) and allows to c...
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.
llvm::Optional< llvm::Expected< tooling::AtomicChanges > > Result
Diagnostics must not be generated for this snapshot.
llvm::StringMap< std::string > Files
The preamble is generated from the current version of the file.