blob: 2cf6ef562a2da5676c99bf47a07951433920e0d3 [file] [log] [blame]
//===-- TUSchedulerTests.cpp ------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "TUScheduler.h"
#include "TestFS.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include <algorithm>
#include <utility>
namespace clang {
namespace clangd {
using ::testing::Pair;
using ::testing::Pointee;
void ignoreUpdate(Context, llvm::Optional<std::vector<DiagWithFixIts>>) {}
void ignoreError(llvm::Error Err) {
handleAllErrors(std::move(Err), [](const llvm::ErrorInfoBase &) {});
}
class TUSchedulerTests : public ::testing::Test {
protected:
ParseInputs getInputs(PathRef File, std::string Contents) {
return ParseInputs{*CDB.getCompileCommand(File), buildTestFS(Files),
std::move(Contents)};
}
void changeFile(PathRef File, std::string Contents) {
Files[File] = Contents;
}
private:
llvm::StringMap<std::string> Files;
MockCompilationDatabase CDB;
};
TEST_F(TUSchedulerTests, MissingFiles) {
TUScheduler S(getDefaultAsyncThreadsCount(),
/*StorePreamblesInMemory=*/true,
/*ASTParsedCallback=*/nullptr);
auto Added = getVirtualTestFilePath("added.cpp");
changeFile(Added, "");
auto Missing = getVirtualTestFilePath("missing.cpp");
changeFile(Missing, "");
S.update(Context::empty(), Added, getInputs(Added, ""), ignoreUpdate);
// Assert each operation for missing file is an error (even if it's available
// in VFS).
S.runWithAST(Missing, [&](llvm::Expected<InputsAndAST> AST) {
ASSERT_FALSE(bool(AST));
ignoreError(AST.takeError());
});
S.runWithPreamble(Missing, [&](llvm::Expected<InputsAndPreamble> Preamble) {
ASSERT_FALSE(bool(Preamble));
ignoreError(Preamble.takeError());
});
S.remove(Missing, [&](llvm::Error Err) {
EXPECT_TRUE(bool(Err));
ignoreError(std::move(Err));
});
// Assert there aren't any errors for added file.
S.runWithAST(
Added, [&](llvm::Expected<InputsAndAST> AST) { EXPECT_TRUE(bool(AST)); });
S.runWithPreamble(Added, [&](llvm::Expected<InputsAndPreamble> Preamble) {
EXPECT_TRUE(bool(Preamble));
});
S.remove(Added, [&](llvm::Error Err) { EXPECT_FALSE(bool(Err)); });
// Assert that all operations fail after removing the file.
S.runWithAST(Added, [&](llvm::Expected<InputsAndAST> AST) {
ASSERT_FALSE(bool(AST));
ignoreError(AST.takeError());
});
S.runWithPreamble(Added, [&](llvm::Expected<InputsAndPreamble> Preamble) {
ASSERT_FALSE(bool(Preamble));
ignoreError(Preamble.takeError());
});
S.remove(Added, [&](llvm::Error Err) {
EXPECT_TRUE(bool(Err));
ignoreError(std::move(Err));
});
}
TEST_F(TUSchedulerTests, ManyUpdates) {
const int FilesCount = 3;
const int UpdatesPerFile = 10;
std::mutex Mut;
int TotalASTReads = 0;
int TotalPreambleReads = 0;
int TotalUpdates = 0;
// Run TUScheduler and collect some stats.
{
TUScheduler S(getDefaultAsyncThreadsCount(),
/*StorePreamblesInMemory=*/true,
/*ASTParsedCallback=*/nullptr);
std::vector<std::string> Files;
for (int I = 0; I < FilesCount; ++I) {
Files.push_back(
getVirtualTestFilePath("foo" + std::to_string(I) + ".cpp").str());
changeFile(Files.back(), "");
}
llvm::StringRef Contents1 = R"cpp(int a;)cpp";
llvm::StringRef Contents2 = R"cpp(int main() { return 1; })cpp";
llvm::StringRef Contents3 =
R"cpp(int a; int b; int sum() { return a + b; })cpp";
llvm::StringRef AllContents[] = {Contents1, Contents2, Contents3};
const int AllContentsSize = 3;
for (int FileI = 0; FileI < FilesCount; ++FileI) {
for (int UpdateI = 0; UpdateI < UpdatesPerFile; ++UpdateI) {
auto Contents = AllContents[(FileI + UpdateI) % AllContentsSize];
auto File = Files[FileI];
auto Inputs = getInputs(File, Contents.str());
static Key<std::pair<int, int>> FileAndUpdateKey;
auto Ctx = Context::empty().derive(FileAndUpdateKey,
std::make_pair(FileI, UpdateI));
S.update(std::move(Ctx), File, Inputs,
[FileI, UpdateI, &Mut, &TotalUpdates](
Context Ctx,
llvm::Optional<std::vector<DiagWithFixIts>> Diags) {
EXPECT_THAT(Ctx.get(FileAndUpdateKey),
Pointee(Pair(FileI, UpdateI)));
std::lock_guard<std::mutex> Lock(Mut);
++TotalUpdates;
});
S.runWithAST(File, [Inputs, &Mut,
&TotalASTReads](llvm::Expected<InputsAndAST> AST) {
ASSERT_TRUE((bool)AST);
EXPECT_EQ(AST->Inputs.FS, Inputs.FS);
EXPECT_EQ(AST->Inputs.Contents, Inputs.Contents);
std::lock_guard<std::mutex> Lock(Mut);
++TotalASTReads;
});
S.runWithPreamble(
File, [Inputs, &Mut, &TotalPreambleReads](
llvm::Expected<InputsAndPreamble> Preamble) {
ASSERT_TRUE((bool)Preamble);
EXPECT_EQ(Preamble->Inputs.FS, Inputs.FS);
EXPECT_EQ(Preamble->Inputs.Contents, Inputs.Contents);
std::lock_guard<std::mutex> Lock(Mut);
++TotalPreambleReads;
});
}
}
} // TUScheduler destructor waits for all operations to finish.
std::lock_guard<std::mutex> Lock(Mut);
EXPECT_EQ(TotalUpdates, FilesCount * UpdatesPerFile);
EXPECT_EQ(TotalASTReads, FilesCount * UpdatesPerFile);
EXPECT_EQ(TotalPreambleReads, FilesCount * UpdatesPerFile);
}
} // namespace clangd
} // namespace clang