xref: /llvm-project/clang-tools-extra/clangd/unittests/ConfigProviderTests.cpp (revision 5c14940394919cbc66cab06103342610af69bc58)
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