331 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8 9#include "Annotations.h"10#include "ConfigFragment.h"11#include "ConfigTesting.h"12#include "Protocol.h"13#include "llvm/ADT/StringRef.h"14#include "llvm/Support/SMLoc.h"15#include "llvm/Support/SourceMgr.h"16#include "llvm/Testing/Support/SupportHelpers.h"17#include "gmock/gmock.h"18#include "gtest/gtest.h"19 20namespace clang {21namespace clangd {22namespace config {23 24// PrintTo is a magic identifier of GTest25// NOLINTNEXTLINE (readability-identifier-naming)26template <typename T> void PrintTo(const Located<T> &V, std::ostream *OS) {27 *OS << ::testing::PrintToString(*V);28}29 30namespace {31using ::testing::AllOf;32using ::testing::ElementsAre;33using ::testing::IsEmpty;34 35MATCHER_P(val, Value, "") {36 if (*arg == Value)37 return true;38 *result_listener << "value is " << *arg;39 return false;40}41 42MATCHER_P2(PairVal, Value1, Value2, "") {43 if (*arg.first == Value1 && *arg.second == Value2)44 return true;45 *result_listener << "values are [" << *arg.first << ", " << *arg.second46 << "]";47 return false;48}49 50TEST(ParseYAML, SyntacticForms) {51 CapturedDiags Diags;52 const char *YAML = R"yaml(53If:54 PathMatch:55 - 'abc'56CompileFlags: { Add: [foo, bar] }57---58CompileFlags:59 Add: |60 b61 az62---63Index:64 Background: Skip65---66Diagnostics:67 ClangTidy:68 CheckOptions:69 example-check.ExampleOption: 070 UnusedIncludes: Strict71 )yaml";72 auto Results = Fragment::parseYAML(YAML, "config.yaml", Diags.callback());73 EXPECT_THAT(Diags.Diagnostics, IsEmpty());74 EXPECT_THAT(Diags.Files, ElementsAre("config.yaml"));75 ASSERT_EQ(Results.size(), 4u);76 EXPECT_FALSE(Results[0].If.HasUnrecognizedCondition);77 EXPECT_THAT(Results[0].If.PathMatch, ElementsAre(val("abc")));78 EXPECT_THAT(Results[0].CompileFlags.Add, ElementsAre(val("foo"), val("bar")));79 80 EXPECT_THAT(Results[1].CompileFlags.Add, ElementsAre(val("b\naz\n")));81 82 ASSERT_TRUE(Results[2].Index.Background);83 EXPECT_EQ("Skip", **Results[2].Index.Background);84 EXPECT_THAT(Results[3].Diagnostics.ClangTidy.CheckOptions,85 ElementsAre(PairVal("example-check.ExampleOption", "0")));86 EXPECT_TRUE(Results[3].Diagnostics.UnusedIncludes);87 EXPECT_EQ("Strict", **Results[3].Diagnostics.UnusedIncludes);88}89 90TEST(ParseYAML, Locations) {91 CapturedDiags Diags;92 Annotations YAML(R"yaml(93If:94 PathMatch: [['???bad***regex(((']]95 )yaml");96 auto Results =97 Fragment::parseYAML(YAML.code(), "config.yaml", Diags.callback());98 EXPECT_THAT(Diags.Diagnostics, IsEmpty());99 ASSERT_EQ(Results.size(), 1u);100 ASSERT_NE(Results.front().Source.Manager, nullptr);101 EXPECT_EQ(toRange(Results.front().If.PathMatch.front().Range,102 *Results.front().Source.Manager),103 YAML.range());104}105 106TEST(ParseYAML, ConfigDiagnostics) {107 CapturedDiags Diags;108 Annotations YAML(R"yaml(109If:110 $unknown[[UnknownCondition]]: "foo"111CompileFlags:112 Add: 'first'113---114CompileFlags: {$unexpected^115)yaml");116 auto Results =117 Fragment::parseYAML(YAML.code(), "config.yaml", Diags.callback());118 119 ASSERT_THAT(120 Diags.Diagnostics,121 ElementsAre(AllOf(diagMessage("Unknown If key 'UnknownCondition'"),122 diagKind(llvm::SourceMgr::DK_Warning),123 diagPos(YAML.range("unknown").start),124 diagRange(YAML.range("unknown"))),125 AllOf(diagMessage("Unexpected token. Expected Key, Flow "126 "Entry, or Flow Mapping End."),127 diagKind(llvm::SourceMgr::DK_Error),128 diagPos(YAML.point("unexpected")),129 diagRange(std::nullopt))));130 131 ASSERT_EQ(Results.size(), 1u); // invalid fragment discarded.132 EXPECT_THAT(Results.front().CompileFlags.Add, ElementsAre(val("first")));133 EXPECT_TRUE(Results.front().If.HasUnrecognizedCondition);134}135 136TEST(ParseYAML, Invalid) {137 CapturedDiags Diags;138 const char *YAML = R"yaml(139If:140 141horrible142---143- 1144 )yaml";145 auto Results = Fragment::parseYAML(YAML, "config.yaml", Diags.callback());146 EXPECT_THAT(Diags.Diagnostics,147 ElementsAre(diagMessage("If should be a dictionary"),148 diagMessage("Config should be a dictionary")));149 ASSERT_THAT(Results, IsEmpty());150}151 152TEST(ParseYAML, ExternalBlockNone) {153 CapturedDiags Diags;154 Annotations YAML(R"yaml(155Index:156 External: None157 )yaml");158 auto Results =159 Fragment::parseYAML(YAML.code(), "config.yaml", Diags.callback());160 ASSERT_THAT(Diags.Diagnostics, IsEmpty());161 ASSERT_EQ(Results.size(), 1u);162 ASSERT_TRUE(Results[0].Index.External);163 EXPECT_FALSE((*Results[0].Index.External)->File.has_value());164 EXPECT_FALSE((*Results[0].Index.External)->MountPoint.has_value());165 EXPECT_FALSE((*Results[0].Index.External)->Server.has_value());166 EXPECT_THAT(*(*Results[0].Index.External)->IsNone, testing::Eq(true));167}168 169TEST(ParseYAML, ExternalBlock) {170 CapturedDiags Diags;171 Annotations YAML(R"yaml(172Index:173 External:174 File: "foo"175 Server: ^"bar"176 MountPoint: "baz"177 )yaml");178 auto Results =179 Fragment::parseYAML(YAML.code(), "config.yaml", Diags.callback());180 ASSERT_EQ(Results.size(), 1u);181 ASSERT_TRUE(Results[0].Index.External);182 EXPECT_THAT(*(*Results[0].Index.External)->File, val("foo"));183 EXPECT_THAT(*(*Results[0].Index.External)->MountPoint, val("baz"));184 ASSERT_THAT(Diags.Diagnostics, IsEmpty());185 EXPECT_THAT(*(*Results[0].Index.External)->Server, val("bar"));186}187 188TEST(ParseYAML, AllScopes) {189 CapturedDiags Diags;190 Annotations YAML(R"yaml(191Completion:192 AllScopes: True193 )yaml");194 auto Results =195 Fragment::parseYAML(YAML.code(), "config.yaml", Diags.callback());196 ASSERT_THAT(Diags.Diagnostics, IsEmpty());197 ASSERT_EQ(Results.size(), 1u);198 EXPECT_THAT(Results[0].Completion.AllScopes, llvm::ValueIs(val(true)));199}200 201TEST(ParseYAML, AllScopesWarn) {202 CapturedDiags Diags;203 Annotations YAML(R"yaml(204Completion:205 AllScopes: $diagrange[[Truex]]206 )yaml");207 auto Results =208 Fragment::parseYAML(YAML.code(), "config.yaml", Diags.callback());209 EXPECT_THAT(Diags.Diagnostics,210 ElementsAre(AllOf(diagMessage("AllScopes should be a boolean"),211 diagKind(llvm::SourceMgr::DK_Warning),212 diagPos(YAML.range("diagrange").start),213 diagRange(YAML.range("diagrange")))));214 ASSERT_EQ(Results.size(), 1u);215 EXPECT_THAT(Results[0].Completion.AllScopes, testing::Eq(std::nullopt));216}217 218TEST(ParseYAML, CodePatterns) {219 CapturedDiags Diags;220 Annotations YAML(R"yaml(221 Completion:222 CodePatterns: None223 )yaml");224 auto Results =225 Fragment::parseYAML(YAML.code(), "config.yaml", Diags.callback());226 ASSERT_THAT(Diags.Diagnostics, IsEmpty());227 ASSERT_EQ(Results.size(), 1u);228 EXPECT_THAT(Results[0].Completion.CodePatterns, llvm::ValueIs(val("None")));229}230 231TEST(ParseYAML, Hover) {232 CapturedDiags Diags;233 Annotations YAML(R"yaml(234Hover:235 ShowAKA: True236 MacroContentsLimit: 4096237 )yaml");238 auto Results =239 Fragment::parseYAML(YAML.code(), "config.yaml", Diags.callback());240 ASSERT_THAT(Diags.Diagnostics, IsEmpty());241 ASSERT_EQ(Results.size(), 1u);242 EXPECT_THAT(Results[0].Hover.ShowAKA, llvm::ValueIs(val(true)));243 EXPECT_THAT(Results[0].Hover.MacroContentsLimit, llvm::ValueIs(val(4096U)));244}245 246TEST(ParseYAML, InlayHints) {247 CapturedDiags Diags;248 Annotations YAML(R"yaml(249InlayHints:250 Enabled: No251 ParameterNames: Yes252 )yaml");253 auto Results =254 Fragment::parseYAML(YAML.code(), "config.yaml", Diags.callback());255 ASSERT_THAT(Diags.Diagnostics, IsEmpty());256 ASSERT_EQ(Results.size(), 1u);257 EXPECT_THAT(Results[0].InlayHints.Enabled, llvm::ValueIs(val(false)));258 EXPECT_THAT(Results[0].InlayHints.ParameterNames, llvm::ValueIs(val(true)));259 EXPECT_EQ(Results[0].InlayHints.DeducedTypes, std::nullopt);260}261 262TEST(ParseYAML, SemanticTokens) {263 CapturedDiags Diags;264 Annotations YAML(R"yaml(265SemanticTokens:266 DisabledKinds: [ Operator, InactiveCode]267 DisabledModifiers: Readonly268 )yaml");269 auto Results =270 Fragment::parseYAML(YAML.code(), "config.yaml", Diags.callback());271 ASSERT_THAT(Diags.Diagnostics, IsEmpty());272 ASSERT_EQ(Results.size(), 1u);273 EXPECT_THAT(Results[0].SemanticTokens.DisabledKinds,274 ElementsAre(val("Operator"), val("InactiveCode")));275 EXPECT_THAT(Results[0].SemanticTokens.DisabledModifiers,276 ElementsAre(val("Readonly")));277}278 279TEST(ParseYAML, IncludesIgnoreHeader) {280 CapturedDiags Diags;281 Annotations YAML(R"yaml(282Diagnostics:283 Includes:284 IgnoreHeader: [foo, bar]285 )yaml");286 auto Results =287 Fragment::parseYAML(YAML.code(), "config.yaml", Diags.callback());288 ASSERT_THAT(Diags.Diagnostics, IsEmpty());289 ASSERT_EQ(Results.size(), 1u);290 EXPECT_THAT(Results[0].Diagnostics.Includes.IgnoreHeader,291 ElementsAre(val("foo"), val("bar")));292}293 294TEST(ParseYAML, IncludesAnalyzeAngledIncludes) {295 CapturedDiags Diags;296 Annotations YAML(R"yaml(297Diagnostics:298 Includes:299 AnalyzeAngledIncludes: true300 )yaml");301 auto Results =302 Fragment::parseYAML(YAML.code(), "config.yaml", Diags.callback());303 ASSERT_THAT(Diags.Diagnostics, IsEmpty());304 ASSERT_EQ(Results.size(), 1u);305 EXPECT_THAT(Results[0].Diagnostics.Includes.AnalyzeAngledIncludes,306 llvm::ValueIs(val(true)));307}308 309TEST(ParseYAML, Style) {310 CapturedDiags Diags;311 Annotations YAML(R"yaml(312Style:313 FullyQualifiedNamespaces: [foo, bar]314 AngledHeaders: ["foo", "bar"]315 QuotedHeaders: ["baz", "baar"])yaml");316 auto Results =317 Fragment::parseYAML(YAML.code(), "config.yaml", Diags.callback());318 ASSERT_THAT(Diags.Diagnostics, IsEmpty());319 ASSERT_EQ(Results.size(), 1u);320 EXPECT_THAT(Results[0].Style.FullyQualifiedNamespaces,321 ElementsAre(val("foo"), val("bar")));322 EXPECT_THAT(Results[0].Style.AngledHeaders,323 ElementsAre(val("foo"), val("bar")));324 EXPECT_THAT(Results[0].Style.QuotedHeaders,325 ElementsAre(val("baz"), val("baar")));326}327} // namespace328} // namespace config329} // namespace clangd330} // namespace clang331