| //===-- ConfigYAMLTests.cpp -----------------------------------------------===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "Annotations.h" |
| #include "ConfigFragment.h" |
| #include "ConfigTesting.h" |
| #include "Protocol.h" |
| #include "llvm/ADT/None.h" |
| #include "llvm/ADT/StringRef.h" |
| #include "llvm/Support/SMLoc.h" |
| #include "llvm/Support/ScopedPrinter.h" |
| #include "llvm/Support/SourceMgr.h" |
| #include "llvm/Testing/Support/SupportHelpers.h" |
| #include "gmock/gmock.h" |
| #include "gtest/gtest.h" |
| |
| namespace clang { |
| namespace clangd { |
| namespace config { |
| template <typename T> void PrintTo(const Located<T> &V, std::ostream *OS) { |
| *OS << ::testing::PrintToString(*V); |
| } |
| |
| namespace { |
| using ::testing::AllOf; |
| using ::testing::ElementsAre; |
| using ::testing::IsEmpty; |
| |
| MATCHER_P(Val, Value, "") { |
| if (*arg == Value) |
| return true; |
| *result_listener << "value is " << *arg; |
| return false; |
| } |
| |
| MATCHER_P2(PairVal, Value1, Value2, "") { |
| if (*arg.first == Value1 && *arg.second == Value2) |
| return true; |
| *result_listener << "values are [" << *arg.first << ", " << *arg.second |
| << "]"; |
| return false; |
| } |
| |
| TEST(ParseYAML, SyntacticForms) { |
| CapturedDiags Diags; |
| const char *YAML = R"yaml( |
| If: |
| PathMatch: |
| - 'abc' |
| CompileFlags: { Add: [foo, bar] } |
| --- |
| CompileFlags: |
| Add: | |
| b |
| az |
| --- |
| Index: |
| Background: Skip |
| --- |
| Diagnostics: |
| ClangTidy: |
| CheckOptions: |
| IgnoreMacros: true |
| example-check.ExampleOption: 0 |
| UnusedIncludes: Strict |
| )yaml"; |
| auto Results = Fragment::parseYAML(YAML, "config.yaml", Diags.callback()); |
| EXPECT_THAT(Diags.Diagnostics, IsEmpty()); |
| EXPECT_THAT(Diags.Files, ElementsAre("config.yaml")); |
| ASSERT_EQ(Results.size(), 4u); |
| EXPECT_FALSE(Results[0].If.HasUnrecognizedCondition); |
| EXPECT_THAT(Results[0].If.PathMatch, ElementsAre(Val("abc"))); |
| EXPECT_THAT(Results[0].CompileFlags.Add, ElementsAre(Val("foo"), Val("bar"))); |
| |
| EXPECT_THAT(Results[1].CompileFlags.Add, ElementsAre(Val("b\naz\n"))); |
| |
| ASSERT_TRUE(Results[2].Index.Background); |
| EXPECT_EQ("Skip", *Results[2].Index.Background.getValue()); |
| EXPECT_THAT(Results[3].Diagnostics.ClangTidy.CheckOptions, |
| ElementsAre(PairVal("IgnoreMacros", "true"), |
| PairVal("example-check.ExampleOption", "0"))); |
| EXPECT_TRUE(Results[3].Diagnostics.UnusedIncludes); |
| EXPECT_EQ("Strict", *Results[3].Diagnostics.UnusedIncludes.getValue()); |
| } |
| |
| TEST(ParseYAML, Locations) { |
| CapturedDiags Diags; |
| Annotations YAML(R"yaml( |
| If: |
| PathMatch: [['???bad***regex(((']] |
| )yaml"); |
| auto Results = |
| Fragment::parseYAML(YAML.code(), "config.yaml", Diags.callback()); |
| EXPECT_THAT(Diags.Diagnostics, IsEmpty()); |
| ASSERT_EQ(Results.size(), 1u); |
| ASSERT_NE(Results.front().Source.Manager, nullptr); |
| EXPECT_EQ(toRange(Results.front().If.PathMatch.front().Range, |
| *Results.front().Source.Manager), |
| YAML.range()); |
| } |
| |
| TEST(ParseYAML, ConfigDiagnostics) { |
| CapturedDiags Diags; |
| Annotations YAML(R"yaml( |
| If: |
| $unknown[[UnknownCondition]]: "foo" |
| CompileFlags: |
| Add: 'first' |
| --- |
| CompileFlags: {$unexpected^ |
| )yaml"); |
| auto Results = |
| Fragment::parseYAML(YAML.code(), "config.yaml", Diags.callback()); |
| |
| ASSERT_THAT( |
| Diags.Diagnostics, |
| ElementsAre(AllOf(DiagMessage("Unknown If key 'UnknownCondition'"), |
| DiagKind(llvm::SourceMgr::DK_Warning), |
| DiagPos(YAML.range("unknown").start), |
| DiagRange(YAML.range("unknown"))), |
| AllOf(DiagMessage("Unexpected token. Expected Key, Flow " |
| "Entry, or Flow Mapping End."), |
| DiagKind(llvm::SourceMgr::DK_Error), |
| DiagPos(YAML.point("unexpected")), |
| DiagRange(llvm::None)))); |
| |
| ASSERT_EQ(Results.size(), 1u); // invalid fragment discarded. |
| EXPECT_THAT(Results.front().CompileFlags.Add, ElementsAre(Val("first"))); |
| EXPECT_TRUE(Results.front().If.HasUnrecognizedCondition); |
| } |
| |
| TEST(ParseYAML, Invalid) { |
| CapturedDiags Diags; |
| const char *YAML = R"yaml( |
| If: |
| |
| horrible |
| --- |
| - 1 |
| )yaml"; |
| auto Results = Fragment::parseYAML(YAML, "config.yaml", Diags.callback()); |
| EXPECT_THAT(Diags.Diagnostics, |
| ElementsAre(DiagMessage("If should be a dictionary"), |
| DiagMessage("Config should be a dictionary"))); |
| ASSERT_THAT(Results, IsEmpty()); |
| } |
| |
| TEST(ParseYAML, ExternalBlockNone) { |
| CapturedDiags Diags; |
| Annotations YAML(R"yaml( |
| Index: |
| External: None |
| )yaml"); |
| auto Results = |
| Fragment::parseYAML(YAML.code(), "config.yaml", Diags.callback()); |
| ASSERT_THAT(Diags.Diagnostics, IsEmpty()); |
| ASSERT_EQ(Results.size(), 1u); |
| ASSERT_TRUE(Results[0].Index.External); |
| EXPECT_FALSE(Results[0].Index.External.getValue()->File.hasValue()); |
| EXPECT_FALSE(Results[0].Index.External.getValue()->MountPoint.hasValue()); |
| EXPECT_FALSE(Results[0].Index.External.getValue()->Server.hasValue()); |
| EXPECT_THAT(*Results[0].Index.External.getValue()->IsNone, testing::Eq(true)); |
| } |
| |
| TEST(ParseYAML, ExternalBlock) { |
| CapturedDiags Diags; |
| Annotations YAML(R"yaml( |
| Index: |
| External: |
| File: "foo" |
| Server: ^"bar" |
| MountPoint: "baz" |
| )yaml"); |
| auto Results = |
| Fragment::parseYAML(YAML.code(), "config.yaml", Diags.callback()); |
| ASSERT_EQ(Results.size(), 1u); |
| ASSERT_TRUE(Results[0].Index.External); |
| EXPECT_THAT(*Results[0].Index.External.getValue()->File, Val("foo")); |
| EXPECT_THAT(*Results[0].Index.External.getValue()->MountPoint, Val("baz")); |
| ASSERT_THAT(Diags.Diagnostics, IsEmpty()); |
| EXPECT_THAT(*Results[0].Index.External.getValue()->Server, Val("bar")); |
| } |
| |
| TEST(ParseYAML, AllScopes) { |
| CapturedDiags Diags; |
| Annotations YAML(R"yaml( |
| Completion: |
| AllScopes: True |
| )yaml"); |
| auto Results = |
| Fragment::parseYAML(YAML.code(), "config.yaml", Diags.callback()); |
| ASSERT_THAT(Diags.Diagnostics, IsEmpty()); |
| ASSERT_EQ(Results.size(), 1u); |
| EXPECT_THAT(Results[0].Completion.AllScopes, llvm::ValueIs(Val(true))); |
| } |
| |
| TEST(ParseYAML, AllScopesWarn) { |
| CapturedDiags Diags; |
| Annotations YAML(R"yaml( |
| Completion: |
| AllScopes: $diagrange[[Truex]] |
| )yaml"); |
| auto Results = |
| Fragment::parseYAML(YAML.code(), "config.yaml", Diags.callback()); |
| EXPECT_THAT(Diags.Diagnostics, |
| ElementsAre(AllOf(DiagMessage("AllScopes should be a boolean"), |
| DiagKind(llvm::SourceMgr::DK_Warning), |
| DiagPos(YAML.range("diagrange").start), |
| DiagRange(YAML.range("diagrange"))))); |
| ASSERT_EQ(Results.size(), 1u); |
| EXPECT_THAT(Results[0].Completion.AllScopes, testing::Eq(llvm::None)); |
| } |
| } // namespace |
| } // namespace config |
| } // namespace clangd |
| } // namespace clang |