1 //===-- ConfigYAMLTests.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 "Annotations.h"
10 #include "ConfigFragment.h"
11 #include "ConfigTesting.h"
12 #include "Protocol.h"
13 #include "llvm/ADT/None.h"
14 #include "llvm/ADT/StringRef.h"
15 #include "llvm/Support/SMLoc.h"
16 #include "llvm/Support/ScopedPrinter.h"
17 #include "llvm/Support/SourceMgr.h"
18 #include "llvm/Testing/Support/SupportHelpers.h"
19 #include "gmock/gmock.h"
20 #include "gtest/gtest.h"
21
22 namespace clang {
23 namespace clangd {
24 namespace config {
PrintTo(const Located<T> & V,std::ostream * OS)25 template <typename T> void PrintTo(const Located<T> &V, std::ostream *OS) {
26 *OS << ::testing::PrintToString(*V);
27 }
28
29 namespace {
30 using ::testing::AllOf;
31 using ::testing::ElementsAre;
32 using ::testing::IsEmpty;
33
34 MATCHER_P(Val, Value, "") {
35 if (*arg == Value)
36 return true;
37 *result_listener << "value is " << *arg;
38 return false;
39 }
40
41 MATCHER_P2(PairVal, Value1, Value2, "") {
42 if (*arg.first == Value1 && *arg.second == Value2)
43 return true;
44 *result_listener << "values are [" << *arg.first << ", " << *arg.second
45 << "]";
46 return false;
47 }
48
TEST(ParseYAML,SyntacticForms)49 TEST(ParseYAML, SyntacticForms) {
50 CapturedDiags Diags;
51 const char *YAML = R"yaml(
52 If:
53 PathMatch:
54 - 'abc'
55 CompileFlags: { Add: [foo, bar] }
56 ---
57 CompileFlags:
58 Add: |
59 b
60 az
61 ---
62 Index:
63 Background: Skip
64 ---
65 Diagnostics:
66 ClangTidy:
67 CheckOptions:
68 IgnoreMacros: true
69 example-check.ExampleOption: 0
70 )yaml";
71 auto Results = Fragment::parseYAML(YAML, "config.yaml", Diags.callback());
72 EXPECT_THAT(Diags.Diagnostics, IsEmpty());
73 EXPECT_THAT(Diags.Files, ElementsAre("config.yaml"));
74 ASSERT_EQ(Results.size(), 4u);
75 EXPECT_FALSE(Results[0].If.HasUnrecognizedCondition);
76 EXPECT_THAT(Results[0].If.PathMatch, ElementsAre(Val("abc")));
77 EXPECT_THAT(Results[0].CompileFlags.Add, ElementsAre(Val("foo"), Val("bar")));
78
79 EXPECT_THAT(Results[1].CompileFlags.Add, ElementsAre(Val("b\naz\n")));
80
81 ASSERT_TRUE(Results[2].Index.Background);
82 EXPECT_EQ("Skip", *Results[2].Index.Background.getValue());
83 EXPECT_THAT(Results[3].Diagnostics.ClangTidy.CheckOptions,
84 ElementsAre(PairVal("IgnoreMacros", "true"),
85 PairVal("example-check.ExampleOption", "0")));
86 }
87
TEST(ParseYAML,Locations)88 TEST(ParseYAML, Locations) {
89 CapturedDiags Diags;
90 Annotations YAML(R"yaml(
91 If:
92 PathMatch: [['???bad***regex(((']]
93 )yaml");
94 auto Results =
95 Fragment::parseYAML(YAML.code(), "config.yaml", Diags.callback());
96 EXPECT_THAT(Diags.Diagnostics, IsEmpty());
97 ASSERT_EQ(Results.size(), 1u);
98 ASSERT_NE(Results.front().Source.Manager, nullptr);
99 EXPECT_EQ(toRange(Results.front().If.PathMatch.front().Range,
100 *Results.front().Source.Manager),
101 YAML.range());
102 }
103
TEST(ParseYAML,ConfigDiagnostics)104 TEST(ParseYAML, ConfigDiagnostics) {
105 CapturedDiags Diags;
106 Annotations YAML(R"yaml(
107 If:
108 $unknown[[UnknownCondition]]: "foo"
109 CompileFlags:
110 Add: 'first'
111 ---
112 CompileFlags: {$unexpected^
113 )yaml");
114 auto Results =
115 Fragment::parseYAML(YAML.code(), "config.yaml", Diags.callback());
116
117 ASSERT_THAT(
118 Diags.Diagnostics,
119 ElementsAre(AllOf(DiagMessage("Unknown If key 'UnknownCondition'"),
120 DiagKind(llvm::SourceMgr::DK_Warning),
121 DiagPos(YAML.range("unknown").start),
122 DiagRange(YAML.range("unknown"))),
123 AllOf(DiagMessage("Unexpected token. Expected Key, Flow "
124 "Entry, or Flow Mapping End."),
125 DiagKind(llvm::SourceMgr::DK_Error),
126 DiagPos(YAML.point("unexpected")),
127 DiagRange(llvm::None))));
128
129 ASSERT_EQ(Results.size(), 1u); // invalid fragment discarded.
130 EXPECT_THAT(Results.front().CompileFlags.Add, ElementsAre(Val("first")));
131 EXPECT_TRUE(Results.front().If.HasUnrecognizedCondition);
132 }
133
TEST(ParseYAML,Invalid)134 TEST(ParseYAML, Invalid) {
135 CapturedDiags Diags;
136 const char *YAML = R"yaml(
137 If:
138
139 horrible
140 ---
141 - 1
142 )yaml";
143 auto Results = Fragment::parseYAML(YAML, "config.yaml", Diags.callback());
144 EXPECT_THAT(Diags.Diagnostics,
145 ElementsAre(DiagMessage("If should be a dictionary"),
146 DiagMessage("Config should be a dictionary")));
147 ASSERT_THAT(Results, IsEmpty());
148 }
149
TEST(ParseYAML,ExternalBlockNone)150 TEST(ParseYAML, ExternalBlockNone) {
151 CapturedDiags Diags;
152 Annotations YAML(R"yaml(
153 Index:
154 External: None
155 )yaml");
156 auto Results =
157 Fragment::parseYAML(YAML.code(), "config.yaml", Diags.callback());
158 ASSERT_THAT(Diags.Diagnostics, IsEmpty());
159 ASSERT_EQ(Results.size(), 1u);
160 ASSERT_TRUE(Results[0].Index.External);
161 EXPECT_FALSE(Results[0].Index.External.getValue()->File.hasValue());
162 EXPECT_FALSE(Results[0].Index.External.getValue()->MountPoint.hasValue());
163 EXPECT_FALSE(Results[0].Index.External.getValue()->Server.hasValue());
164 EXPECT_THAT(*Results[0].Index.External.getValue()->IsNone, testing::Eq(true));
165 }
166
TEST(ParseYAML,ExternalBlock)167 TEST(ParseYAML, ExternalBlock) {
168 CapturedDiags Diags;
169 Annotations YAML(R"yaml(
170 Index:
171 External:
172 File: "foo"
173 Server: ^"bar"
174 MountPoint: "baz"
175 )yaml");
176 auto Results =
177 Fragment::parseYAML(YAML.code(), "config.yaml", Diags.callback());
178 ASSERT_EQ(Results.size(), 1u);
179 ASSERT_TRUE(Results[0].Index.External);
180 EXPECT_THAT(*Results[0].Index.External.getValue()->File, Val("foo"));
181 EXPECT_THAT(*Results[0].Index.External.getValue()->MountPoint, Val("baz"));
182 ASSERT_THAT(Diags.Diagnostics, IsEmpty());
183 EXPECT_THAT(*Results[0].Index.External.getValue()->Server, Val("bar"));
184 }
185
TEST(ParseYAML,AllScopes)186 TEST(ParseYAML, AllScopes) {
187 CapturedDiags Diags;
188 Annotations YAML(R"yaml(
189 Completion:
190 AllScopes: True
191 )yaml");
192 auto Results =
193 Fragment::parseYAML(YAML.code(), "config.yaml", Diags.callback());
194 ASSERT_THAT(Diags.Diagnostics, IsEmpty());
195 ASSERT_EQ(Results.size(), 1u);
196 EXPECT_THAT(Results[0].Completion.AllScopes, llvm::ValueIs(Val(true)));
197 }
198
TEST(ParseYAML,AllScopesWarn)199 TEST(ParseYAML, AllScopesWarn) {
200 CapturedDiags Diags;
201 Annotations YAML(R"yaml(
202 Completion:
203 AllScopes: $diagrange[[Truex]]
204 )yaml");
205 auto Results =
206 Fragment::parseYAML(YAML.code(), "config.yaml", Diags.callback());
207 EXPECT_THAT(Diags.Diagnostics,
208 ElementsAre(AllOf(DiagMessage("AllScopes should be a boolean"),
209 DiagKind(llvm::SourceMgr::DK_Warning),
210 DiagPos(YAML.range("diagrange").start),
211 DiagRange(YAML.range("diagrange")))));
212 ASSERT_EQ(Results.size(), 1u);
213 EXPECT_THAT(Results[0].Completion.AllScopes, testing::Eq(llvm::None));
214 }
215 } // namespace
216 } // namespace config
217 } // namespace clangd
218 } // namespace clang
219