clang-tools  11.0.0
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/SourceMgr.h"
14 #include "gmock/gmock.h"
15 #include "gtest/gtest.h"
16 #include <atomic>
17 #include <chrono>
18 
19 namespace clang {
20 namespace clangd {
21 namespace config {
22 namespace {
23 using ::testing::ElementsAre;
24 using ::testing::IsEmpty;
25 
26 // Provider that appends an arg to compile flags.
27 // The arg is prefix<N>, where N is the times getFragments() was called.
28 // It also yields a diagnostic each time it's called.
29 class FakeProvider : public Provider {
30  std::string Prefix;
31  mutable std::atomic<unsigned> Index = {0};
32 
33  std::vector<CompiledFragment>
34  getFragments(const Params &, DiagnosticCallback DC) const override {
35  DC(llvm::SMDiagnostic("", llvm::SourceMgr::DK_Error, Prefix));
37  [Arg(Prefix + std::to_string(++Index))](const Params &P, Config &C) {
38  C.CompileFlags.Edits.push_back(
39  [Arg](std::vector<std::string> &Argv) { Argv.push_back(Arg); });
40  return true;
41  };
42  return {F};
43  }
44 
45 public:
46  FakeProvider(llvm::StringRef Prefix) : Prefix(Prefix) {}
47 };
48 
49 std::vector<std::string> getAddedArgs(Config &C) {
50  std::vector<std::string> Argv;
51  for (auto &Edit : C.CompileFlags.Edits)
52  Edit(Argv);
53  return Argv;
54 }
55 
56 // The provider from combine() should invoke its providers in order, and not
57 // cache their results.
58 TEST(ProviderTest, Combine) {
59  CapturedDiags Diags;
60  FakeProvider Foo("foo");
61  FakeProvider Bar("bar");
62  auto Combined = Provider::combine({&Foo, &Bar});
63  Config Cfg = Combined->getConfig(Params(), Diags.callback());
64  EXPECT_THAT(Diags.Diagnostics,
65  ElementsAre(DiagMessage("foo"), DiagMessage("bar")));
66  EXPECT_THAT(getAddedArgs(Cfg), ElementsAre("foo1", "bar1"));
67  Diags.Diagnostics.clear();
68 
69  Cfg = Combined->getConfig(Params(), Diags.callback());
70  EXPECT_THAT(Diags.Diagnostics,
71  ElementsAre(DiagMessage("foo"), DiagMessage("bar")));
72  EXPECT_THAT(getAddedArgs(Cfg), ElementsAre("foo2", "bar2"));
73 }
74 
75 const char *AddFooWithErr = R"yaml(
76 CompileFlags:
77  Add: foo
78  Unknown: 42
79 )yaml";
80 
81 const char *AddBarBaz = R"yaml(
82 CompileFlags:
83  Add: bar
84 ---
85 CompileFlags:
86  Add: baz
87 )yaml";
88 
89 TEST(ProviderTest, FromYAMLFile) {
90  MockFS FS;
91  FS.Files["foo.yaml"] = AddFooWithErr;
92 
93  CapturedDiags Diags;
94  auto P = Provider::fromYAMLFile(testPath("foo.yaml"), FS);
95  auto Cfg = P->getConfig(Params(), Diags.callback());
96  EXPECT_THAT(Diags.Diagnostics,
97  ElementsAre(DiagMessage("Unknown CompileFlags key Unknown")));
98  EXPECT_THAT(getAddedArgs(Cfg), ElementsAre("foo"));
99  Diags.Diagnostics.clear();
100 
101  Cfg = P->getConfig(Params(), Diags.callback());
102  EXPECT_THAT(Diags.Diagnostics, IsEmpty()) << "Cached, not re-parsed";
103  EXPECT_THAT(getAddedArgs(Cfg), ElementsAre("foo"));
104 
105  FS.Files["foo.yaml"] = AddBarBaz;
106  Cfg = P->getConfig(Params(), Diags.callback());
107  EXPECT_THAT(Diags.Diagnostics, IsEmpty()) << "New config, no errors";
108  EXPECT_THAT(getAddedArgs(Cfg), ElementsAre("bar", "baz"));
109 
110  FS.Files.erase("foo.yaml");
111  Cfg = P->getConfig(Params(), Diags.callback());
112  EXPECT_THAT(Diags.Diagnostics, IsEmpty()) << "Missing file is not an error";
113  EXPECT_THAT(getAddedArgs(Cfg), IsEmpty());
114 }
115 
116 TEST(ProviderTest, FromAncestorRelativeYAMLFiles) {
117  MockFS FS;
118  FS.Files["a/b/c/foo.yaml"] = AddBarBaz;
119  FS.Files["a/foo.yaml"] = AddFooWithErr;
120 
121  std::string ABCPath =
122  testPath("a/b/c/d/test.cc", llvm::sys::path::Style::posix);
123  Params ABCParams;
124  ABCParams.Path = ABCPath;
125  std::string APath =
126  testPath("a/b/e/f/test.cc", llvm::sys::path::Style::posix);
127  Params AParams;
128  AParams.Path = APath;
129 
130  CapturedDiags Diags;
131  auto P = Provider::fromAncestorRelativeYAMLFiles("foo.yaml", FS);
132 
133  auto Cfg = P->getConfig(Params(), Diags.callback());
134  EXPECT_THAT(Diags.Diagnostics, IsEmpty());
135  EXPECT_THAT(getAddedArgs(Cfg), IsEmpty());
136 
137  Cfg = P->getConfig(ABCParams, Diags.callback());
138  EXPECT_THAT(Diags.Diagnostics,
139  ElementsAre(DiagMessage("Unknown CompileFlags key Unknown")));
140  EXPECT_THAT(getAddedArgs(Cfg), ElementsAre("foo", "bar", "baz"));
141  Diags.Diagnostics.clear();
142 
143  Cfg = P->getConfig(AParams, Diags.callback());
144  EXPECT_THAT(Diags.Diagnostics, IsEmpty()) << "Cached config";
145  EXPECT_THAT(getAddedArgs(Cfg), ElementsAre("foo"));
146 
147  FS.Files.erase("a/foo.yaml");
148  Cfg = P->getConfig(ABCParams, Diags.callback());
149  EXPECT_THAT(Diags.Diagnostics, IsEmpty());
150  EXPECT_THAT(getAddedArgs(Cfg), ElementsAre("bar", "baz"));
151 }
152 
153 TEST(ProviderTest, Staleness) {
154  MockFS FS;
155 
156  auto StartTime = std::chrono::steady_clock::now();
157  Params StaleOK;
158  StaleOK.FreshTime = StartTime;
159  Params MustBeFresh;
160  MustBeFresh.FreshTime = StartTime + std::chrono::hours(1);
161  CapturedDiags Diags;
162  auto P = Provider::fromYAMLFile(testPath("foo.yaml"), FS);
163 
164  // Initial query always reads, regardless of policy.
165  FS.Files["foo.yaml"] = AddFooWithErr;
166  auto Cfg = P->getConfig(StaleOK, Diags.callback());
167  EXPECT_THAT(Diags.Diagnostics,
168  ElementsAre(DiagMessage("Unknown CompileFlags key Unknown")));
169  EXPECT_THAT(getAddedArgs(Cfg), ElementsAre("foo"));
170  Diags.Diagnostics.clear();
171 
172  // Stale value reused by policy.
173  FS.Files["foo.yaml"] = AddBarBaz;
174  Cfg = P->getConfig(StaleOK, Diags.callback());
175  EXPECT_THAT(Diags.Diagnostics, IsEmpty()) << "Cached, not re-parsed";
176  EXPECT_THAT(getAddedArgs(Cfg), ElementsAre("foo"));
177 
178  // Cache revalidated by policy.
179  Cfg = P->getConfig(MustBeFresh, Diags.callback());
180  EXPECT_THAT(Diags.Diagnostics, IsEmpty()) << "New config, no errors";
181  EXPECT_THAT(getAddedArgs(Cfg), ElementsAre("bar", "baz"));
182 
183  // Cache revalidated by (default) policy.
184  FS.Files.erase("foo.yaml");
185  Cfg = P->getConfig(Params(), Diags.callback());
186  EXPECT_THAT(Diags.Diagnostics, IsEmpty());
187  EXPECT_THAT(getAddedArgs(Cfg), IsEmpty());
188 }
189 
190 } // namespace
191 } // namespace config
192 } // namespace clangd
193 } // namespace clang
clang::clangd::TEST
TEST(BackgroundQueueTest, Priority)
Definition: BackgroundIndexTests.cpp:704
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
FS
MockFS FS
Definition: ClangdLSPServerTests.cpp:66
TestFS.h
clang::clangd::config::Provider::fromAncestorRelativeYAMLFiles
static std::unique_ptr< Provider > fromAncestorRelativeYAMLFiles(llvm::StringRef RelPath, const ThreadsafeFS &)
Definition: ConfigProvider.cpp:131
ConfigTesting.h
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:54
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))
Index
const SymbolIndex * Index
Definition: Dexp.cpp:95
Config.h
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:196
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ApplyReplacements.h:27
Diags
CapturedDiags Diags
Definition: ConfigCompileTests.cpp:26
clang::clangd::config::CapturedDiags::Diagnostics
std::vector< Diag > Diagnostics
Definition: ConfigTesting.h:53
clang::clangd::config::Provider::fromYAMLFile
static std::unique_ptr< Provider > fromYAMLFile(llvm::StringRef AbsPathPath, const ThreadsafeFS &)
Definition: ConfigProvider.cpp:107
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:47