1 //===-- ConfigProviderTests.cpp -------------------------------------------===//
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
7 //===----------------------------------------------------------------------===//
10 #include "ConfigProvider.h"
11 #include "ConfigTesting.h"
13 #include "llvm/Support/Path.h"
14 #include "llvm/Support/SourceMgr.h"
15 #include "gmock/gmock.h"
16 #include "gtest/gtest.h"
24 using ::testing::ElementsAre
;
25 using ::testing::IsEmpty
;
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
{
32 mutable std::atomic
<unsigned> Index
= {0};
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
); });
47 FakeProvider(llvm::StringRef Prefix
) : Prefix(Prefix
) {}
50 std::vector
<std::string
> getAddedArgs(Config
&C
) {
51 std::vector
<std::string
> Argv
;
52 for (auto &Edit
: C
.CompileFlags
.Edits
)
57 // The provider from combine() should invoke its providers in order, and not
58 // cache their results.
59 TEST(ProviderTest
, Combine
) {
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();
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"));
76 const char *AddFooWithErr
= R
"yaml(
82 const char *AddFooWithTypoErr
= R
"yaml(
88 const char *AddBarBaz
= R
"yaml(
96 TEST(ProviderTest
, FromYAMLFile
) {
98 FS
.Files
["foo.yaml"] = AddFooWithErr
;
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"));
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"));
114 FS
.Files
["foo.yaml"] = AddFooWithTypoErr
;
115 Cfg
= P
->getConfig(Params(), Diags
.callback());
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"));
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"));
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());
138 TEST(ProviderTest
, FromAncestorRelativeYAMLFiles
) {
140 FS
.Files
["a/b/c/foo.yaml"] = AddBarBaz
;
141 FS
.Files
["a/foo.yaml"] = AddFooWithErr
;
143 std::string ABCPath
=
144 testPath("a/b/c/d/test.cc", llvm::sys::path::Style::posix
);
146 ABCParams
.Path
= ABCPath
;
148 testPath("a/b/e/f/test.cc", llvm::sys::path::Style::posix
);
150 AParams
.Path
= APath
;
153 auto P
= Provider::fromAncestorRelativeYAMLFiles("foo.yaml", FS
);
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());
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"));
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"));
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"));
181 TEST(ProviderTest
, SourceInfo
) {
184 FS
.Files
["baz/foo.yaml"] = R
"yaml(
191 const auto BarPath
= testPath("baz/bar.h", llvm::sys::path::Style::posix
);
196 // This should be an absolute match/exclude hence baz/bar.h should not be
199 Provider::fromYAMLFile(testPath("baz/foo.yaml"), /*Directory=*/"", FS
);
200 auto Cfg
= P
->getConfig(Bar
, Diags
.callback());
201 ASSERT_THAT(Diags
.Diagnostics
, IsEmpty());
202 EXPECT_THAT(getAddedArgs(Cfg
), ElementsAre("bar"));
205 // This should be a relative match/exclude hence baz/bar.h should be excluded.
206 P
= Provider::fromAncestorRelativeYAMLFiles("foo.yaml", FS
);
207 Cfg
= P
->getConfig(Bar
, Diags
.callback());
208 ASSERT_THAT(Diags
.Diagnostics
, IsEmpty());
209 EXPECT_THAT(getAddedArgs(Cfg
), IsEmpty());
213 } // namespace config
214 } // namespace clangd