clang-tools  14.0.0git
ConfigProviderTests.cpp
Go to the documentation of this file.
1 //===-- ConfigProviderTests.cpp -------------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "Config.h"
10 #include "ConfigProvider.h"
11 #include "ConfigTesting.h"
12 #include "TestFS.h"
13 #include "llvm/Support/Path.h"
14 #include "llvm/Support/SourceMgr.h"
15 #include "gmock/gmock.h"
16 #include "gtest/gtest.h"
17 #include <atomic>
18 #include <chrono>
19 
20 namespace clang {
21 namespace clangd {
22 namespace config {
23 namespace {
24 using ::testing::ElementsAre;
25 using ::testing::IsEmpty;
26 
27 // Provider that appends an arg to compile flags.
28 // The arg is prefix<N>, where N is the times getFragments() was called.
29 // It also yields a diagnostic each time it's called.
30 class FakeProvider : public Provider {
31  std::string Prefix;
32  mutable std::atomic<unsigned> Index = {0};
33 
34  std::vector<CompiledFragment>
35  getFragments(const Params &, DiagnosticCallback DC) const override {
36  DC(llvm::SMDiagnostic("", llvm::SourceMgr::DK_Error, Prefix));
38  [Arg(Prefix + std::to_string(++Index))](const Params &P, Config &C) {
39  C.CompileFlags.Edits.push_back(
40  [Arg](std::vector<std::string> &Argv) { Argv.push_back(Arg); });
41  return true;
42  };
43  return {F};
44  }
45 
46 public:
47  FakeProvider(llvm::StringRef Prefix) : Prefix(Prefix) {}
48 };
49 
50 std::vector<std::string> getAddedArgs(Config &C) {
51  std::vector<std::string> Argv;
52  for (auto &Edit : C.CompileFlags.Edits)
53  Edit(Argv);
54  return Argv;
55 }
56 
57 // The provider from combine() should invoke its providers in order, and not
58 // cache their results.
59 TEST(ProviderTest, Combine) {
60  CapturedDiags Diags;
61  FakeProvider Foo("foo");
62  FakeProvider Bar("bar");
63  auto Combined = Provider::combine({&Foo, &Bar});
64  Config Cfg = Combined->getConfig(Params(), Diags.callback());
65  EXPECT_THAT(Diags.Diagnostics,
66  ElementsAre(DiagMessage("foo"), DiagMessage("bar")));
67  EXPECT_THAT(getAddedArgs(Cfg), ElementsAre("foo1", "bar1"));
68  Diags.Diagnostics.clear();
69 
70  Cfg = Combined->getConfig(Params(), Diags.callback());
71  EXPECT_THAT(Diags.Diagnostics,
72  ElementsAre(DiagMessage("foo"), DiagMessage("bar")));
73  EXPECT_THAT(getAddedArgs(Cfg), ElementsAre("foo2", "bar2"));
74 }
75 
76 const char *AddFooWithErr = R"yaml(
77 CompileFlags:
78  Add: foo
79  Unknown: 42
80 )yaml";
81 
82 const char *AddFooWithTypoErr = R"yaml(
83 CompileFlags:
84  Add: foo
85  Removr: 42
86 )yaml";
87 
88 const char *AddBarBaz = R"yaml(
89 CompileFlags:
90  Add: bar
91 ---
92 CompileFlags:
93  Add: baz
94 )yaml";
95 
96 TEST(ProviderTest, FromYAMLFile) {
97  MockFS FS;
98  FS.Files["foo.yaml"] = AddFooWithErr;
99 
100  CapturedDiags Diags;
101  auto P = Provider::fromYAMLFile(testPath("foo.yaml"), /*Directory=*/"", FS);
102  auto Cfg = P->getConfig(Params(), Diags.callback());
103  EXPECT_THAT(Diags.Diagnostics,
104  ElementsAre(DiagMessage("Unknown CompileFlags key 'Unknown'")));
105  EXPECT_THAT(Diags.Files, ElementsAre(testPath("foo.yaml")));
106  EXPECT_THAT(getAddedArgs(Cfg), ElementsAre("foo"));
107  Diags.clear();
108 
109  Cfg = P->getConfig(Params(), Diags.callback());
110  EXPECT_THAT(Diags.Diagnostics, IsEmpty()) << "Cached, not re-parsed";
111  EXPECT_THAT(Diags.Files, IsEmpty());
112  EXPECT_THAT(getAddedArgs(Cfg), ElementsAre("foo"));
113 
114  FS.Files["foo.yaml"] = AddFooWithTypoErr;
115  Cfg = P->getConfig(Params(), Diags.callback());
116  EXPECT_THAT(
118  ElementsAre(DiagMessage(
119  "Unknown CompileFlags key 'Removr'; did you mean 'Remove'?")));
120  EXPECT_THAT(Diags.Files, ElementsAre(testPath("foo.yaml")));
121  EXPECT_THAT(getAddedArgs(Cfg), ElementsAre("foo"));
122  Diags.clear();
123 
124  FS.Files["foo.yaml"] = AddBarBaz;
125  Cfg = P->getConfig(Params(), Diags.callback());
126  EXPECT_THAT(Diags.Diagnostics, IsEmpty()) << "New config, no errors";
127  EXPECT_THAT(Diags.Files, ElementsAre(testPath("foo.yaml")));
128  EXPECT_THAT(getAddedArgs(Cfg), ElementsAre("bar", "baz"));
129  Diags.clear();
130 
131  FS.Files.erase("foo.yaml");
132  Cfg = P->getConfig(Params(), Diags.callback());
133  EXPECT_THAT(Diags.Diagnostics, IsEmpty()) << "Missing file is not an error";
134  EXPECT_THAT(Diags.Files, IsEmpty());
135  EXPECT_THAT(getAddedArgs(Cfg), IsEmpty());
136 }
137 
138 TEST(ProviderTest, FromAncestorRelativeYAMLFiles) {
139  MockFS FS;
140  FS.Files["a/b/c/foo.yaml"] = AddBarBaz;
141  FS.Files["a/foo.yaml"] = AddFooWithErr;
142 
143  std::string ABCPath =
144  testPath("a/b/c/d/test.cc", llvm::sys::path::Style::posix);
145  Params ABCParams;
146  ABCParams.Path = ABCPath;
147  std::string APath =
148  testPath("a/b/e/f/test.cc", llvm::sys::path::Style::posix);
149  Params AParams;
150  AParams.Path = APath;
151 
152  CapturedDiags Diags;
153  auto P = Provider::fromAncestorRelativeYAMLFiles("foo.yaml", FS);
154 
155  auto Cfg = P->getConfig(Params(), Diags.callback());
156  EXPECT_THAT(Diags.Diagnostics, IsEmpty());
157  EXPECT_THAT(Diags.Files, IsEmpty());
158  EXPECT_THAT(getAddedArgs(Cfg), IsEmpty());
159 
160  Cfg = P->getConfig(ABCParams, Diags.callback());
161  EXPECT_THAT(Diags.Diagnostics,
162  ElementsAre(DiagMessage("Unknown CompileFlags key 'Unknown'")));
163  // FIXME: fails on windows: paths have mixed slashes like C:\a/b\c.yaml
164  EXPECT_THAT(Diags.Files,
165  ElementsAre(testPath("a/foo.yaml"), testPath("a/b/c/foo.yaml")));
166  EXPECT_THAT(getAddedArgs(Cfg), ElementsAre("foo", "bar", "baz"));
167  Diags.clear();
168 
169  Cfg = P->getConfig(AParams, Diags.callback());
170  EXPECT_THAT(Diags.Diagnostics, IsEmpty()) << "Cached config";
171  EXPECT_THAT(Diags.Files, IsEmpty());
172  EXPECT_THAT(getAddedArgs(Cfg), ElementsAre("foo"));
173 
174  FS.Files.erase("a/foo.yaml");
175  Cfg = P->getConfig(ABCParams, Diags.callback());
176  EXPECT_THAT(Diags.Diagnostics, IsEmpty());
177  EXPECT_THAT(Diags.Files, IsEmpty());
178  EXPECT_THAT(getAddedArgs(Cfg), ElementsAre("bar", "baz"));
179 }
180 
181 // FIXME: delete this test, it's covered by FileCacheTests.
182 TEST(ProviderTest, Staleness) {
183  MockFS FS;
184 
185  auto StartTime = std::chrono::steady_clock::now();
186  Params StaleOK;
187  StaleOK.FreshTime = StartTime;
188  Params MustBeFresh;
189  MustBeFresh.FreshTime = StartTime + std::chrono::hours(1);
190  CapturedDiags Diags;
191  auto P = Provider::fromYAMLFile(testPath("foo.yaml"), /*Directory=*/"", FS);
192 
193  // Initial query always reads, regardless of policy.
194  FS.Files["foo.yaml"] = AddFooWithErr;
195  auto Cfg = P->getConfig(StaleOK, Diags.callback());
196  EXPECT_THAT(Diags.Diagnostics,
197  ElementsAre(DiagMessage("Unknown CompileFlags key 'Unknown'")));
198  EXPECT_THAT(getAddedArgs(Cfg), ElementsAre("foo"));
199  Diags.clear();
200 
201  // Stale value reused by policy.
202  FS.Files["foo.yaml"] = AddBarBaz;
203  Cfg = P->getConfig(StaleOK, Diags.callback());
204  EXPECT_THAT(Diags.Diagnostics, IsEmpty()) << "Cached, not re-parsed";
205  EXPECT_THAT(getAddedArgs(Cfg), ElementsAre("foo"));
206 
207  // Cache revalidated by policy.
208  Cfg = P->getConfig(MustBeFresh, Diags.callback());
209  EXPECT_THAT(Diags.Diagnostics, IsEmpty()) << "New config, no errors";
210  EXPECT_THAT(getAddedArgs(Cfg), ElementsAre("bar", "baz"));
211 
212  // Cache revalidated by (default) policy.
213  FS.Files.erase("foo.yaml");
214  Cfg = P->getConfig(Params(), Diags.callback());
215  EXPECT_THAT(Diags.Diagnostics, IsEmpty());
216  EXPECT_THAT(getAddedArgs(Cfg), IsEmpty());
217 }
218 
219 TEST(ProviderTest, SourceInfo) {
220  MockFS FS;
221 
222  FS.Files["baz/foo.yaml"] = R"yaml(
223 If:
224  PathMatch: .*
225  PathExclude: bar.h
226 CompileFlags:
227  Add: bar
228 )yaml";
229  const auto BarPath = testPath("baz/bar.h", llvm::sys::path::Style::posix);
230  CapturedDiags Diags;
231  Params Bar;
232  Bar.Path = BarPath;
233 
234  // This should be an absolute match/exclude hence baz/bar.h should not be
235  // excluded.
236  auto P =
237  Provider::fromYAMLFile(testPath("baz/foo.yaml"), /*Directory=*/"", FS);
238  auto Cfg = P->getConfig(Bar, Diags.callback());
239  ASSERT_THAT(Diags.Diagnostics, IsEmpty());
240  EXPECT_THAT(getAddedArgs(Cfg), ElementsAre("bar"));
241  Diags.clear();
242 
243  // This should be a relative match/exclude hence baz/bar.h should be excluded.
244  P = Provider::fromAncestorRelativeYAMLFiles("foo.yaml", FS);
245  Cfg = P->getConfig(Bar, Diags.callback());
246  ASSERT_THAT(Diags.Diagnostics, IsEmpty());
247  EXPECT_THAT(getAddedArgs(Cfg), IsEmpty());
248  Diags.clear();
249 }
250 } // namespace
251 } // namespace config
252 } // namespace clangd
253 } // namespace clang
clang::clangd::TEST
TEST(BackgroundQueueTest, Priority)
Definition: BackgroundIndexTests.cpp:751
clang::clangd::testPath
std::string testPath(PathRef File, llvm::sys::path::Style Style)
Definition: TestFS.cpp:82
clang::clangd::config::CapturedDiags::callback
std::function< void(const llvm::SMDiagnostic &)> callback()
Definition: ConfigTesting.h:25
ConfigProvider.h
Foo
Definition: sample.h:4
clang::clangd::config::CapturedDiags::clear
void clear()
Definition: ConfigTesting.h:62
TestFS.h
ConfigTesting.h
clang::clangd::config::Provider::fromAncestorRelativeYAMLFiles
static std::unique_ptr< Provider > fromAncestorRelativeYAMLFiles(llvm::StringRef RelPath, const ThreadsafeFS &, bool Trusted=false)
Definition: ConfigProvider.cpp:84
clang::clangd::config::CompiledFragment
std::function< bool(const Params &, Config &)> CompiledFragment
A chunk of configuration that has been fully analyzed and is ready to apply.
Definition: ConfigProvider.h:58
Bar
Definition: sample.cpp:5
Config
static cl::opt< std::string > Config("config", cl::desc(R"( Specifies a configuration in YAML/JSON format: -config="{Checks:' *', CheckOptions:[{key:x, value:y}]}" When the value is empty, clang-tidy will attempt to find a file named .clang-tidy for each source file in its parent directories. )"), cl::init(""), cl::cat(ClangTidyCategory))
clang::clangd::config::CapturedDiags::Files
std::vector< std::string > Files
Definition: ConfigTesting.h:60
Index
const SymbolIndex * Index
Definition: Dexp.cpp:99
Config.h
clang::clangd::config::Provider::fromYAMLFile
static std::unique_ptr< Provider > fromYAMLFile(llvm::StringRef AbsPath, llvm::StringRef Directory, const ThreadsafeFS &, bool Trusted=false)
Reads fragments from a single YAML file with a fixed path.
Definition: ConfigProvider.cpp:56
clang::clangd::config::Provider::combine
static std::unique_ptr< Provider > combine(std::vector< const Provider * >)
A provider that includes fragments from all the supplied providers.
Definition: ConfigProvider.cpp:147
C
const Criteria C
Definition: FunctionCognitiveComplexityCheck.cpp:93
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ApplyReplacements.h:27
Diags
CapturedDiags Diags
Definition: ConfigCompileTests.cpp:38
clang::clangd::config::CapturedDiags::Diagnostics
std::vector< Diag > Diagnostics
Definition: ConfigTesting.h:59
clang::clangd::config::DiagnosticCallback
llvm::function_ref< void(const llvm::SMDiagnostic &)> DiagnosticCallback
Used to report problems in parsing or interpreting a config.
Definition: ConfigProvider.h:51