forked from OSchip/llvm-project
254 lines
8.1 KiB
C++
254 lines
8.1 KiB
C++
//===-- ConfigProviderTests.cpp -------------------------------------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "Config.h"
|
|
#include "ConfigProvider.h"
|
|
#include "ConfigTesting.h"
|
|
#include "TestFS.h"
|
|
#include "llvm/Support/Path.h"
|
|
#include "llvm/Support/SourceMgr.h"
|
|
#include "gmock/gmock.h"
|
|
#include "gtest/gtest.h"
|
|
#include <atomic>
|
|
#include <chrono>
|
|
|
|
namespace clang {
|
|
namespace clangd {
|
|
namespace config {
|
|
namespace {
|
|
using ::testing::ElementsAre;
|
|
using ::testing::IsEmpty;
|
|
|
|
// Provider that appends an arg to compile flags.
|
|
// The arg is prefix<N>, where N is the times getFragments() was called.
|
|
// It also yields a diagnostic each time it's called.
|
|
class FakeProvider : public Provider {
|
|
std::string Prefix;
|
|
mutable std::atomic<unsigned> Index = {0};
|
|
|
|
std::vector<CompiledFragment>
|
|
getFragments(const Params &, DiagnosticCallback DC) const override {
|
|
DC(llvm::SMDiagnostic("", llvm::SourceMgr::DK_Error, Prefix));
|
|
CompiledFragment F =
|
|
[Arg(Prefix + std::to_string(++Index))](const Params &P, Config &C) {
|
|
C.CompileFlags.Edits.push_back(
|
|
[Arg](std::vector<std::string> &Argv) { Argv.push_back(Arg); });
|
|
return true;
|
|
};
|
|
return {F};
|
|
}
|
|
|
|
public:
|
|
FakeProvider(llvm::StringRef Prefix) : Prefix(Prefix) {}
|
|
};
|
|
|
|
std::vector<std::string> getAddedArgs(Config &C) {
|
|
std::vector<std::string> Argv;
|
|
for (auto &Edit : C.CompileFlags.Edits)
|
|
Edit(Argv);
|
|
return Argv;
|
|
}
|
|
|
|
// The provider from combine() should invoke its providers in order, and not
|
|
// cache their results.
|
|
TEST(ProviderTest, Combine) {
|
|
CapturedDiags Diags;
|
|
FakeProvider Foo("foo");
|
|
FakeProvider Bar("bar");
|
|
auto Combined = Provider::combine({&Foo, &Bar});
|
|
Config Cfg = Combined->getConfig(Params(), Diags.callback());
|
|
EXPECT_THAT(Diags.Diagnostics,
|
|
ElementsAre(DiagMessage("foo"), DiagMessage("bar")));
|
|
EXPECT_THAT(getAddedArgs(Cfg), ElementsAre("foo1", "bar1"));
|
|
Diags.Diagnostics.clear();
|
|
|
|
Cfg = Combined->getConfig(Params(), Diags.callback());
|
|
EXPECT_THAT(Diags.Diagnostics,
|
|
ElementsAre(DiagMessage("foo"), DiagMessage("bar")));
|
|
EXPECT_THAT(getAddedArgs(Cfg), ElementsAre("foo2", "bar2"));
|
|
}
|
|
|
|
const char *AddFooWithErr = R"yaml(
|
|
CompileFlags:
|
|
Add: foo
|
|
Unknown: 42
|
|
)yaml";
|
|
|
|
const char *AddFooWithTypoErr = R"yaml(
|
|
CompileFlags:
|
|
Add: foo
|
|
Removr: 42
|
|
)yaml";
|
|
|
|
const char *AddBarBaz = R"yaml(
|
|
CompileFlags:
|
|
Add: bar
|
|
---
|
|
CompileFlags:
|
|
Add: baz
|
|
)yaml";
|
|
|
|
TEST(ProviderTest, FromYAMLFile) {
|
|
MockFS FS;
|
|
FS.Files["foo.yaml"] = AddFooWithErr;
|
|
|
|
CapturedDiags Diags;
|
|
auto P = Provider::fromYAMLFile(testPath("foo.yaml"), /*Directory=*/"", FS);
|
|
auto Cfg = P->getConfig(Params(), Diags.callback());
|
|
EXPECT_THAT(Diags.Diagnostics,
|
|
ElementsAre(DiagMessage("Unknown CompileFlags key 'Unknown'")));
|
|
EXPECT_THAT(Diags.Files, ElementsAre(testPath("foo.yaml")));
|
|
EXPECT_THAT(getAddedArgs(Cfg), ElementsAre("foo"));
|
|
Diags.clear();
|
|
|
|
Cfg = P->getConfig(Params(), Diags.callback());
|
|
EXPECT_THAT(Diags.Diagnostics, IsEmpty()) << "Cached, not re-parsed";
|
|
EXPECT_THAT(Diags.Files, IsEmpty());
|
|
EXPECT_THAT(getAddedArgs(Cfg), ElementsAre("foo"));
|
|
|
|
FS.Files["foo.yaml"] = AddFooWithTypoErr;
|
|
Cfg = P->getConfig(Params(), Diags.callback());
|
|
EXPECT_THAT(
|
|
Diags.Diagnostics,
|
|
ElementsAre(DiagMessage(
|
|
"Unknown CompileFlags key 'Removr'; did you mean 'Remove'?")));
|
|
EXPECT_THAT(Diags.Files, ElementsAre(testPath("foo.yaml")));
|
|
EXPECT_THAT(getAddedArgs(Cfg), ElementsAre("foo"));
|
|
Diags.clear();
|
|
|
|
FS.Files["foo.yaml"] = AddBarBaz;
|
|
Cfg = P->getConfig(Params(), Diags.callback());
|
|
EXPECT_THAT(Diags.Diagnostics, IsEmpty()) << "New config, no errors";
|
|
EXPECT_THAT(Diags.Files, ElementsAre(testPath("foo.yaml")));
|
|
EXPECT_THAT(getAddedArgs(Cfg), ElementsAre("bar", "baz"));
|
|
Diags.clear();
|
|
|
|
FS.Files.erase("foo.yaml");
|
|
Cfg = P->getConfig(Params(), Diags.callback());
|
|
EXPECT_THAT(Diags.Diagnostics, IsEmpty()) << "Missing file is not an error";
|
|
EXPECT_THAT(Diags.Files, IsEmpty());
|
|
EXPECT_THAT(getAddedArgs(Cfg), IsEmpty());
|
|
}
|
|
|
|
TEST(ProviderTest, FromAncestorRelativeYAMLFiles) {
|
|
MockFS FS;
|
|
FS.Files["a/b/c/foo.yaml"] = AddBarBaz;
|
|
FS.Files["a/foo.yaml"] = AddFooWithErr;
|
|
|
|
std::string ABCPath =
|
|
testPath("a/b/c/d/test.cc", llvm::sys::path::Style::posix);
|
|
Params ABCParams;
|
|
ABCParams.Path = ABCPath;
|
|
std::string APath =
|
|
testPath("a/b/e/f/test.cc", llvm::sys::path::Style::posix);
|
|
Params AParams;
|
|
AParams.Path = APath;
|
|
|
|
CapturedDiags Diags;
|
|
auto P = Provider::fromAncestorRelativeYAMLFiles("foo.yaml", FS);
|
|
|
|
auto Cfg = P->getConfig(Params(), Diags.callback());
|
|
EXPECT_THAT(Diags.Diagnostics, IsEmpty());
|
|
EXPECT_THAT(Diags.Files, IsEmpty());
|
|
EXPECT_THAT(getAddedArgs(Cfg), IsEmpty());
|
|
|
|
Cfg = P->getConfig(ABCParams, Diags.callback());
|
|
EXPECT_THAT(Diags.Diagnostics,
|
|
ElementsAre(DiagMessage("Unknown CompileFlags key 'Unknown'")));
|
|
// FIXME: fails on windows: paths have mixed slashes like C:\a/b\c.yaml
|
|
EXPECT_THAT(Diags.Files,
|
|
ElementsAre(testPath("a/foo.yaml"), testPath("a/b/c/foo.yaml")));
|
|
EXPECT_THAT(getAddedArgs(Cfg), ElementsAre("foo", "bar", "baz"));
|
|
Diags.clear();
|
|
|
|
Cfg = P->getConfig(AParams, Diags.callback());
|
|
EXPECT_THAT(Diags.Diagnostics, IsEmpty()) << "Cached config";
|
|
EXPECT_THAT(Diags.Files, IsEmpty());
|
|
EXPECT_THAT(getAddedArgs(Cfg), ElementsAre("foo"));
|
|
|
|
FS.Files.erase("a/foo.yaml");
|
|
Cfg = P->getConfig(ABCParams, Diags.callback());
|
|
EXPECT_THAT(Diags.Diagnostics, IsEmpty());
|
|
EXPECT_THAT(Diags.Files, IsEmpty());
|
|
EXPECT_THAT(getAddedArgs(Cfg), ElementsAre("bar", "baz"));
|
|
}
|
|
|
|
// FIXME: delete this test, it's covered by FileCacheTests.
|
|
TEST(ProviderTest, Staleness) {
|
|
MockFS FS;
|
|
|
|
auto StartTime = std::chrono::steady_clock::now();
|
|
Params StaleOK;
|
|
StaleOK.FreshTime = StartTime;
|
|
Params MustBeFresh;
|
|
MustBeFresh.FreshTime = StartTime + std::chrono::hours(1);
|
|
CapturedDiags Diags;
|
|
auto P = Provider::fromYAMLFile(testPath("foo.yaml"), /*Directory=*/"", FS);
|
|
|
|
// Initial query always reads, regardless of policy.
|
|
FS.Files["foo.yaml"] = AddFooWithErr;
|
|
auto Cfg = P->getConfig(StaleOK, Diags.callback());
|
|
EXPECT_THAT(Diags.Diagnostics,
|
|
ElementsAre(DiagMessage("Unknown CompileFlags key 'Unknown'")));
|
|
EXPECT_THAT(getAddedArgs(Cfg), ElementsAre("foo"));
|
|
Diags.clear();
|
|
|
|
// Stale value reused by policy.
|
|
FS.Files["foo.yaml"] = AddBarBaz;
|
|
Cfg = P->getConfig(StaleOK, Diags.callback());
|
|
EXPECT_THAT(Diags.Diagnostics, IsEmpty()) << "Cached, not re-parsed";
|
|
EXPECT_THAT(getAddedArgs(Cfg), ElementsAre("foo"));
|
|
|
|
// Cache revalidated by policy.
|
|
Cfg = P->getConfig(MustBeFresh, Diags.callback());
|
|
EXPECT_THAT(Diags.Diagnostics, IsEmpty()) << "New config, no errors";
|
|
EXPECT_THAT(getAddedArgs(Cfg), ElementsAre("bar", "baz"));
|
|
|
|
// Cache revalidated by (default) policy.
|
|
FS.Files.erase("foo.yaml");
|
|
Cfg = P->getConfig(Params(), Diags.callback());
|
|
EXPECT_THAT(Diags.Diagnostics, IsEmpty());
|
|
EXPECT_THAT(getAddedArgs(Cfg), IsEmpty());
|
|
}
|
|
|
|
TEST(ProviderTest, SourceInfo) {
|
|
MockFS FS;
|
|
|
|
FS.Files["baz/foo.yaml"] = R"yaml(
|
|
If:
|
|
PathMatch: .*
|
|
PathExclude: bar.h
|
|
CompileFlags:
|
|
Add: bar
|
|
)yaml";
|
|
const auto BarPath = testPath("baz/bar.h", llvm::sys::path::Style::posix);
|
|
CapturedDiags Diags;
|
|
Params Bar;
|
|
Bar.Path = BarPath;
|
|
|
|
// This should be an absolute match/exclude hence baz/bar.h should not be
|
|
// excluded.
|
|
auto P =
|
|
Provider::fromYAMLFile(testPath("baz/foo.yaml"), /*Directory=*/"", FS);
|
|
auto Cfg = P->getConfig(Bar, Diags.callback());
|
|
ASSERT_THAT(Diags.Diagnostics, IsEmpty());
|
|
EXPECT_THAT(getAddedArgs(Cfg), ElementsAre("bar"));
|
|
Diags.clear();
|
|
|
|
// This should be a relative match/exclude hence baz/bar.h should be excluded.
|
|
P = Provider::fromAncestorRelativeYAMLFiles("foo.yaml", FS);
|
|
Cfg = P->getConfig(Bar, Diags.callback());
|
|
ASSERT_THAT(Diags.Diagnostics, IsEmpty());
|
|
EXPECT_THAT(getAddedArgs(Cfg), IsEmpty());
|
|
Diags.clear();
|
|
}
|
|
} // namespace
|
|
} // namespace config
|
|
} // namespace clangd
|
|
} // namespace clang
|