18bd000a6SSam McCall //===-- ConfigProviderTests.cpp -------------------------------------------===//
28bd000a6SSam McCall //
38bd000a6SSam McCall // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
48bd000a6SSam McCall // See https://llvm.org/LICENSE.txt for license information.
58bd000a6SSam McCall // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
68bd000a6SSam McCall //
78bd000a6SSam McCall //===----------------------------------------------------------------------===//
88bd000a6SSam McCall
98bd000a6SSam McCall #include "Config.h"
108bd000a6SSam McCall #include "ConfigProvider.h"
118bd000a6SSam McCall #include "ConfigTesting.h"
128bd000a6SSam McCall #include "TestFS.h"
137f059a25SKadir Cetinkaya #include "llvm/Support/Path.h"
14ff616f74SSam McCall #include "llvm/Support/SourceMgr.h"
158bd000a6SSam McCall #include "gmock/gmock.h"
168bd000a6SSam McCall #include "gtest/gtest.h"
178bd000a6SSam McCall #include <atomic>
18ff616f74SSam McCall #include <chrono>
198bd000a6SSam McCall
208bd000a6SSam McCall namespace clang {
218bd000a6SSam McCall namespace clangd {
228bd000a6SSam McCall namespace config {
238bd000a6SSam McCall namespace {
248bd000a6SSam McCall using ::testing::ElementsAre;
258bd000a6SSam McCall using ::testing::IsEmpty;
268bd000a6SSam McCall
278bd000a6SSam McCall // Provider that appends an arg to compile flags.
288bd000a6SSam McCall // The arg is prefix<N>, where N is the times getFragments() was called.
298bd000a6SSam McCall // It also yields a diagnostic each time it's called.
308bd000a6SSam McCall class FakeProvider : public Provider {
318bd000a6SSam McCall std::string Prefix;
328bd000a6SSam McCall mutable std::atomic<unsigned> Index = {0};
338bd000a6SSam McCall
348bd000a6SSam McCall std::vector<CompiledFragment>
getFragments(const Params &,DiagnosticCallback DC) const358bd000a6SSam McCall getFragments(const Params &, DiagnosticCallback DC) const override {
368bd000a6SSam McCall DC(llvm::SMDiagnostic("", llvm::SourceMgr::DK_Error, Prefix));
378bd000a6SSam McCall CompiledFragment F =
388bd000a6SSam McCall [Arg(Prefix + std::to_string(++Index))](const Params &P, Config &C) {
398bd000a6SSam McCall C.CompileFlags.Edits.push_back(
408bd000a6SSam McCall [Arg](std::vector<std::string> &Argv) { Argv.push_back(Arg); });
418bd000a6SSam McCall return true;
428bd000a6SSam McCall };
438bd000a6SSam McCall return {F};
448bd000a6SSam McCall }
458bd000a6SSam McCall
468bd000a6SSam McCall public:
FakeProvider(llvm::StringRef Prefix)478bd000a6SSam McCall FakeProvider(llvm::StringRef Prefix) : Prefix(Prefix) {}
488bd000a6SSam McCall };
498bd000a6SSam McCall
getAddedArgs(Config & C)508bd000a6SSam McCall std::vector<std::string> getAddedArgs(Config &C) {
518bd000a6SSam McCall std::vector<std::string> Argv;
528bd000a6SSam McCall for (auto &Edit : C.CompileFlags.Edits)
538bd000a6SSam McCall Edit(Argv);
548bd000a6SSam McCall return Argv;
558bd000a6SSam McCall }
568bd000a6SSam McCall
578bd000a6SSam McCall // The provider from combine() should invoke its providers in order, and not
588bd000a6SSam McCall // cache their results.
TEST(ProviderTest,Combine)598bd000a6SSam McCall TEST(ProviderTest, Combine) {
608bd000a6SSam McCall CapturedDiags Diags;
61f88ce078SSam McCall FakeProvider Foo("foo");
62f88ce078SSam McCall FakeProvider Bar("bar");
63f88ce078SSam McCall auto Combined = Provider::combine({&Foo, &Bar});
648bd000a6SSam McCall Config Cfg = Combined->getConfig(Params(), Diags.callback());
658bd000a6SSam McCall EXPECT_THAT(Diags.Diagnostics,
66*8edfc2f8SChristian Kühnel ElementsAre(diagMessage("foo"), diagMessage("bar")));
678bd000a6SSam McCall EXPECT_THAT(getAddedArgs(Cfg), ElementsAre("foo1", "bar1"));
688bd000a6SSam McCall Diags.Diagnostics.clear();
698bd000a6SSam McCall
708bd000a6SSam McCall Cfg = Combined->getConfig(Params(), Diags.callback());
718bd000a6SSam McCall EXPECT_THAT(Diags.Diagnostics,
72*8edfc2f8SChristian Kühnel ElementsAre(diagMessage("foo"), diagMessage("bar")));
738bd000a6SSam McCall EXPECT_THAT(getAddedArgs(Cfg), ElementsAre("foo2", "bar2"));
748bd000a6SSam McCall }
758bd000a6SSam McCall
768bd000a6SSam McCall const char *AddFooWithErr = R"yaml(
778bd000a6SSam McCall CompileFlags:
788bd000a6SSam McCall Add: foo
798bd000a6SSam McCall Unknown: 42
808bd000a6SSam McCall )yaml";
818bd000a6SSam McCall
82cfa1010cSNathan James const char *AddFooWithTypoErr = R"yaml(
83cfa1010cSNathan James CompileFlags:
84cfa1010cSNathan James Add: foo
85cfa1010cSNathan James Removr: 42
86cfa1010cSNathan James )yaml";
87cfa1010cSNathan James
888bd000a6SSam McCall const char *AddBarBaz = R"yaml(
898bd000a6SSam McCall CompileFlags:
908bd000a6SSam McCall Add: bar
918bd000a6SSam McCall ---
928bd000a6SSam McCall CompileFlags:
938bd000a6SSam McCall Add: baz
948bd000a6SSam McCall )yaml";
958bd000a6SSam McCall
TEST(ProviderTest,FromYAMLFile)968bd000a6SSam McCall TEST(ProviderTest, FromYAMLFile) {
978bd000a6SSam McCall MockFS FS;
988bd000a6SSam McCall FS.Files["foo.yaml"] = AddFooWithErr;
998bd000a6SSam McCall
1008bd000a6SSam McCall CapturedDiags Diags;
1017f059a25SKadir Cetinkaya auto P = Provider::fromYAMLFile(testPath("foo.yaml"), /*Directory=*/"", FS);
1028bd000a6SSam McCall auto Cfg = P->getConfig(Params(), Diags.callback());
1038bd000a6SSam McCall EXPECT_THAT(Diags.Diagnostics,
104*8edfc2f8SChristian Kühnel ElementsAre(diagMessage("Unknown CompileFlags key 'Unknown'")));
105fed9af29SSam McCall EXPECT_THAT(Diags.Files, ElementsAre(testPath("foo.yaml")));
1068bd000a6SSam McCall EXPECT_THAT(getAddedArgs(Cfg), ElementsAre("foo"));
107fed9af29SSam McCall Diags.clear();
1088bd000a6SSam McCall
1098bd000a6SSam McCall Cfg = P->getConfig(Params(), Diags.callback());
1108bd000a6SSam McCall EXPECT_THAT(Diags.Diagnostics, IsEmpty()) << "Cached, not re-parsed";
111fed9af29SSam McCall EXPECT_THAT(Diags.Files, IsEmpty());
1128bd000a6SSam McCall EXPECT_THAT(getAddedArgs(Cfg), ElementsAre("foo"));
1138bd000a6SSam McCall
114cfa1010cSNathan James FS.Files["foo.yaml"] = AddFooWithTypoErr;
115cfa1010cSNathan James Cfg = P->getConfig(Params(), Diags.callback());
116cfa1010cSNathan James EXPECT_THAT(
117cfa1010cSNathan James Diags.Diagnostics,
118*8edfc2f8SChristian Kühnel ElementsAre(diagMessage(
119cfa1010cSNathan James "Unknown CompileFlags key 'Removr'; did you mean 'Remove'?")));
120cfa1010cSNathan James EXPECT_THAT(Diags.Files, ElementsAre(testPath("foo.yaml")));
121cfa1010cSNathan James EXPECT_THAT(getAddedArgs(Cfg), ElementsAre("foo"));
122cfa1010cSNathan James Diags.clear();
123cfa1010cSNathan James
1248bd000a6SSam McCall FS.Files["foo.yaml"] = AddBarBaz;
1258bd000a6SSam McCall Cfg = P->getConfig(Params(), Diags.callback());
1268bd000a6SSam McCall EXPECT_THAT(Diags.Diagnostics, IsEmpty()) << "New config, no errors";
127fed9af29SSam McCall EXPECT_THAT(Diags.Files, ElementsAre(testPath("foo.yaml")));
1288bd000a6SSam McCall EXPECT_THAT(getAddedArgs(Cfg), ElementsAre("bar", "baz"));
129fed9af29SSam McCall Diags.clear();
1308bd000a6SSam McCall
1318bd000a6SSam McCall FS.Files.erase("foo.yaml");
1328bd000a6SSam McCall Cfg = P->getConfig(Params(), Diags.callback());
1338bd000a6SSam McCall EXPECT_THAT(Diags.Diagnostics, IsEmpty()) << "Missing file is not an error";
134fed9af29SSam McCall EXPECT_THAT(Diags.Files, IsEmpty());
1358bd000a6SSam McCall EXPECT_THAT(getAddedArgs(Cfg), IsEmpty());
1368bd000a6SSam McCall }
1378bd000a6SSam McCall
TEST(ProviderTest,FromAncestorRelativeYAMLFiles)1388bd000a6SSam McCall TEST(ProviderTest, FromAncestorRelativeYAMLFiles) {
1398bd000a6SSam McCall MockFS FS;
1408bd000a6SSam McCall FS.Files["a/b/c/foo.yaml"] = AddBarBaz;
1418bd000a6SSam McCall FS.Files["a/foo.yaml"] = AddFooWithErr;
1428bd000a6SSam McCall
1438bd000a6SSam McCall std::string ABCPath =
1448bd000a6SSam McCall testPath("a/b/c/d/test.cc", llvm::sys::path::Style::posix);
1458bd000a6SSam McCall Params ABCParams;
1468bd000a6SSam McCall ABCParams.Path = ABCPath;
1478bd000a6SSam McCall std::string APath =
1488bd000a6SSam McCall testPath("a/b/e/f/test.cc", llvm::sys::path::Style::posix);
1498bd000a6SSam McCall Params AParams;
1508bd000a6SSam McCall AParams.Path = APath;
1518bd000a6SSam McCall
1528bd000a6SSam McCall CapturedDiags Diags;
1538bd000a6SSam McCall auto P = Provider::fromAncestorRelativeYAMLFiles("foo.yaml", FS);
1548bd000a6SSam McCall
1558bd000a6SSam McCall auto Cfg = P->getConfig(Params(), Diags.callback());
1568bd000a6SSam McCall EXPECT_THAT(Diags.Diagnostics, IsEmpty());
157fed9af29SSam McCall EXPECT_THAT(Diags.Files, IsEmpty());
1588bd000a6SSam McCall EXPECT_THAT(getAddedArgs(Cfg), IsEmpty());
1598bd000a6SSam McCall
1608bd000a6SSam McCall Cfg = P->getConfig(ABCParams, Diags.callback());
1618bd000a6SSam McCall EXPECT_THAT(Diags.Diagnostics,
162*8edfc2f8SChristian Kühnel ElementsAre(diagMessage("Unknown CompileFlags key 'Unknown'")));
163f1357264SSam McCall // FIXME: fails on windows: paths have mixed slashes like C:\a/b\c.yaml
164fed9af29SSam McCall EXPECT_THAT(Diags.Files,
165fed9af29SSam McCall ElementsAre(testPath("a/foo.yaml"), testPath("a/b/c/foo.yaml")));
1668bd000a6SSam McCall EXPECT_THAT(getAddedArgs(Cfg), ElementsAre("foo", "bar", "baz"));
167fed9af29SSam McCall Diags.clear();
1688bd000a6SSam McCall
1698bd000a6SSam McCall Cfg = P->getConfig(AParams, Diags.callback());
1708bd000a6SSam McCall EXPECT_THAT(Diags.Diagnostics, IsEmpty()) << "Cached config";
171fed9af29SSam McCall EXPECT_THAT(Diags.Files, IsEmpty());
1728bd000a6SSam McCall EXPECT_THAT(getAddedArgs(Cfg), ElementsAre("foo"));
1738bd000a6SSam McCall
1748bd000a6SSam McCall FS.Files.erase("a/foo.yaml");
1758bd000a6SSam McCall Cfg = P->getConfig(ABCParams, Diags.callback());
1768bd000a6SSam McCall EXPECT_THAT(Diags.Diagnostics, IsEmpty());
177fed9af29SSam McCall EXPECT_THAT(Diags.Files, IsEmpty());
1788bd000a6SSam McCall EXPECT_THAT(getAddedArgs(Cfg), ElementsAre("bar", "baz"));
1798bd000a6SSam McCall }
1808bd000a6SSam McCall
TEST(ProviderTest,SourceInfo)1817f059a25SKadir Cetinkaya TEST(ProviderTest, SourceInfo) {
1827f059a25SKadir Cetinkaya MockFS FS;
1837f059a25SKadir Cetinkaya
1847f059a25SKadir Cetinkaya FS.Files["baz/foo.yaml"] = R"yaml(
1857f059a25SKadir Cetinkaya If:
1867f059a25SKadir Cetinkaya PathMatch: .*
1877f059a25SKadir Cetinkaya PathExclude: bar.h
1887f059a25SKadir Cetinkaya CompileFlags:
1897f059a25SKadir Cetinkaya Add: bar
1907f059a25SKadir Cetinkaya )yaml";
1917f059a25SKadir Cetinkaya const auto BarPath = testPath("baz/bar.h", llvm::sys::path::Style::posix);
1927f059a25SKadir Cetinkaya CapturedDiags Diags;
1937f059a25SKadir Cetinkaya Params Bar;
1947f059a25SKadir Cetinkaya Bar.Path = BarPath;
1957f059a25SKadir Cetinkaya
1967f059a25SKadir Cetinkaya // This should be an absolute match/exclude hence baz/bar.h should not be
1977f059a25SKadir Cetinkaya // excluded.
1987f059a25SKadir Cetinkaya auto P =
1997f059a25SKadir Cetinkaya Provider::fromYAMLFile(testPath("baz/foo.yaml"), /*Directory=*/"", FS);
2007f059a25SKadir Cetinkaya auto Cfg = P->getConfig(Bar, Diags.callback());
2017f059a25SKadir Cetinkaya ASSERT_THAT(Diags.Diagnostics, IsEmpty());
2027f059a25SKadir Cetinkaya EXPECT_THAT(getAddedArgs(Cfg), ElementsAre("bar"));
203fed9af29SSam McCall Diags.clear();
2047f059a25SKadir Cetinkaya
2057f059a25SKadir Cetinkaya // This should be a relative match/exclude hence baz/bar.h should be excluded.
2067f059a25SKadir Cetinkaya P = Provider::fromAncestorRelativeYAMLFiles("foo.yaml", FS);
2077f059a25SKadir Cetinkaya Cfg = P->getConfig(Bar, Diags.callback());
2087f059a25SKadir Cetinkaya ASSERT_THAT(Diags.Diagnostics, IsEmpty());
2097f059a25SKadir Cetinkaya EXPECT_THAT(getAddedArgs(Cfg), IsEmpty());
210fed9af29SSam McCall Diags.clear();
2117f059a25SKadir Cetinkaya }
2128bd000a6SSam McCall } // namespace
2138bd000a6SSam McCall } // namespace config
2148bd000a6SSam McCall } // namespace clangd
2158bd000a6SSam McCall } // namespace clang
216