llvm-project
215 строк · 6.8 Кб
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
20namespace clang {
21namespace clangd {
22namespace config {
23namespace {
24using ::testing::ElementsAre;
25using ::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.
30class FakeProvider : public Provider {
31std::string Prefix;
32mutable std::atomic<unsigned> Index = {0};
33
34std::vector<CompiledFragment>
35getFragments(const Params &, DiagnosticCallback DC) const override {
36DC(llvm::SMDiagnostic("", llvm::SourceMgr::DK_Error, Prefix));
37CompiledFragment F =
38[Arg(Prefix + std::to_string(++Index))](const Params &P, Config &C) {
39C.CompileFlags.Edits.push_back(
40[Arg](std::vector<std::string> &Argv) { Argv.push_back(Arg); });
41return true;
42};
43return {F};
44}
45
46public:
47FakeProvider(llvm::StringRef Prefix) : Prefix(Prefix) {}
48};
49
50std::vector<std::string> getAddedArgs(Config &C) {
51std::vector<std::string> Argv;
52for (auto &Edit : C.CompileFlags.Edits)
53Edit(Argv);
54return Argv;
55}
56
57// The provider from combine() should invoke its providers in order, and not
58// cache their results.
59TEST(ProviderTest, Combine) {
60CapturedDiags Diags;
61FakeProvider Foo("foo");
62FakeProvider Bar("bar");
63auto Combined = Provider::combine({&Foo, &Bar});
64Config Cfg = Combined->getConfig(Params(), Diags.callback());
65EXPECT_THAT(Diags.Diagnostics,
66ElementsAre(diagMessage("foo"), diagMessage("bar")));
67EXPECT_THAT(getAddedArgs(Cfg), ElementsAre("foo1", "bar1"));
68Diags.Diagnostics.clear();
69
70Cfg = Combined->getConfig(Params(), Diags.callback());
71EXPECT_THAT(Diags.Diagnostics,
72ElementsAre(diagMessage("foo"), diagMessage("bar")));
73EXPECT_THAT(getAddedArgs(Cfg), ElementsAre("foo2", "bar2"));
74}
75
76const char *AddFooWithErr = R"yaml(
77CompileFlags:
78Add: foo
79Unknown: 42
80)yaml";
81
82const char *AddFooWithTypoErr = R"yaml(
83CompileFlags:
84Add: foo
85Removr: 42
86)yaml";
87
88const char *AddBarBaz = R"yaml(
89CompileFlags:
90Add: bar
91---
92CompileFlags:
93Add: baz
94)yaml";
95
96TEST(ProviderTest, FromYAMLFile) {
97MockFS FS;
98FS.Files["foo.yaml"] = AddFooWithErr;
99
100CapturedDiags Diags;
101auto P = Provider::fromYAMLFile(testPath("foo.yaml"), /*Directory=*/"", FS);
102auto Cfg = P->getConfig(Params(), Diags.callback());
103EXPECT_THAT(Diags.Diagnostics,
104ElementsAre(diagMessage("Unknown CompileFlags key 'Unknown'")));
105EXPECT_THAT(Diags.Files, ElementsAre(testPath("foo.yaml")));
106EXPECT_THAT(getAddedArgs(Cfg), ElementsAre("foo"));
107Diags.clear();
108
109Cfg = P->getConfig(Params(), Diags.callback());
110EXPECT_THAT(Diags.Diagnostics, IsEmpty()) << "Cached, not re-parsed";
111EXPECT_THAT(Diags.Files, IsEmpty());
112EXPECT_THAT(getAddedArgs(Cfg), ElementsAre("foo"));
113
114FS.Files["foo.yaml"] = AddFooWithTypoErr;
115Cfg = P->getConfig(Params(), Diags.callback());
116EXPECT_THAT(
117Diags.Diagnostics,
118ElementsAre(diagMessage(
119"Unknown CompileFlags key 'Removr'; did you mean 'Remove'?")));
120EXPECT_THAT(Diags.Files, ElementsAre(testPath("foo.yaml")));
121EXPECT_THAT(getAddedArgs(Cfg), ElementsAre("foo"));
122Diags.clear();
123
124FS.Files["foo.yaml"] = AddBarBaz;
125Cfg = P->getConfig(Params(), Diags.callback());
126EXPECT_THAT(Diags.Diagnostics, IsEmpty()) << "New config, no errors";
127EXPECT_THAT(Diags.Files, ElementsAre(testPath("foo.yaml")));
128EXPECT_THAT(getAddedArgs(Cfg), ElementsAre("bar", "baz"));
129Diags.clear();
130
131FS.Files.erase("foo.yaml");
132Cfg = P->getConfig(Params(), Diags.callback());
133EXPECT_THAT(Diags.Diagnostics, IsEmpty()) << "Missing file is not an error";
134EXPECT_THAT(Diags.Files, IsEmpty());
135EXPECT_THAT(getAddedArgs(Cfg), IsEmpty());
136}
137
138TEST(ProviderTest, FromAncestorRelativeYAMLFiles) {
139MockFS FS;
140FS.Files["a/b/c/foo.yaml"] = AddBarBaz;
141FS.Files["a/foo.yaml"] = AddFooWithErr;
142
143std::string ABCPath =
144testPath("a/b/c/d/test.cc", llvm::sys::path::Style::posix);
145Params ABCParams;
146ABCParams.Path = ABCPath;
147std::string APath =
148testPath("a/b/e/f/test.cc", llvm::sys::path::Style::posix);
149Params AParams;
150AParams.Path = APath;
151
152CapturedDiags Diags;
153auto P = Provider::fromAncestorRelativeYAMLFiles("foo.yaml", FS);
154
155auto Cfg = P->getConfig(Params(), Diags.callback());
156EXPECT_THAT(Diags.Diagnostics, IsEmpty());
157EXPECT_THAT(Diags.Files, IsEmpty());
158EXPECT_THAT(getAddedArgs(Cfg), IsEmpty());
159
160Cfg = P->getConfig(ABCParams, Diags.callback());
161EXPECT_THAT(Diags.Diagnostics,
162ElementsAre(diagMessage("Unknown CompileFlags key 'Unknown'")));
163// FIXME: fails on windows: paths have mixed slashes like C:\a/b\c.yaml
164EXPECT_THAT(Diags.Files,
165ElementsAre(testPath("a/foo.yaml"), testPath("a/b/c/foo.yaml")));
166EXPECT_THAT(getAddedArgs(Cfg), ElementsAre("foo", "bar", "baz"));
167Diags.clear();
168
169Cfg = P->getConfig(AParams, Diags.callback());
170EXPECT_THAT(Diags.Diagnostics, IsEmpty()) << "Cached config";
171EXPECT_THAT(Diags.Files, IsEmpty());
172EXPECT_THAT(getAddedArgs(Cfg), ElementsAre("foo"));
173
174FS.Files.erase("a/foo.yaml");
175Cfg = P->getConfig(ABCParams, Diags.callback());
176EXPECT_THAT(Diags.Diagnostics, IsEmpty());
177EXPECT_THAT(Diags.Files, IsEmpty());
178EXPECT_THAT(getAddedArgs(Cfg), ElementsAre("bar", "baz"));
179}
180
181TEST(ProviderTest, SourceInfo) {
182MockFS FS;
183
184FS.Files["baz/foo.yaml"] = R"yaml(
185If:
186PathMatch: .*
187PathExclude: bar.h
188CompileFlags:
189Add: bar
190)yaml";
191const auto BarPath = testPath("baz/bar.h", llvm::sys::path::Style::posix);
192CapturedDiags Diags;
193Params Bar;
194Bar.Path = BarPath;
195
196// This should be an absolute match/exclude hence baz/bar.h should not be
197// excluded.
198auto P =
199Provider::fromYAMLFile(testPath("baz/foo.yaml"), /*Directory=*/"", FS);
200auto Cfg = P->getConfig(Bar, Diags.callback());
201ASSERT_THAT(Diags.Diagnostics, IsEmpty());
202EXPECT_THAT(getAddedArgs(Cfg), ElementsAre("bar"));
203Diags.clear();
204
205// This should be a relative match/exclude hence baz/bar.h should be excluded.
206P = Provider::fromAncestorRelativeYAMLFiles("foo.yaml", FS);
207Cfg = P->getConfig(Bar, Diags.callback());
208ASSERT_THAT(Diags.Diagnostics, IsEmpty());
209EXPECT_THAT(getAddedArgs(Cfg), IsEmpty());
210Diags.clear();
211}
212} // namespace
213} // namespace config
214} // namespace clangd
215} // namespace clang
216