//===-- 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 void PrintTo(const Located &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)); } TEST(ParseYAML, ShowAKA) { CapturedDiags Diags; Annotations YAML(R"yaml( Hover: ShowAKA: 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].Hover.ShowAKA, llvm::ValueIs(Val(true))); } } // namespace } // namespace config } // namespace clangd } // namespace clang