/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/Features/CSharpTest/SolutionExplorer/CSharpSolutionExplorerSymbolTreeItemProviderTests.cs
645 строк
18 KB
David Barbet
Add optional namespace support to symbol tree item provider
16 янв 2026, 05:45
16 янв 2026, 05:45
f3eb8d3
Код
Авторство
О чём код?
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using System.Threading.Tasks; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.CodeAnalysis.Test.Utilities.SolutionExplorer; using Xunit; namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.SolutionExplorer; [UseExportProvider, Trait(Traits.Feature, Traits.Features.SolutionExplorer)] public sealed class CSharpSolutionExplorerSymbolTreeItemProviderTests : AbstractSolutionExplorerSymbolTreeItemProviderTests { protected override TestWorkspace CreateWorkspace(string code) { return TestWorkspace.CreateCSharp( code, CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.Preview)); } private Task TestCompilationUnit( string code, string expected) { return TestNode<CompilationUnitSyntax>(code, expected); } private Task TestCompilationUnitWithNamespaces( string code, string expected) { return TestNode<CompilationUnitSyntax>(code, expected, returnNamespaces: true); } private Task TestNamespaceDeclaration( string code, string expected) { return TestNode<BaseNamespaceDeclarationSyntax>(code, expected, returnNamespaces: true); } [Fact] public Task TestEmptyFile() => TestCompilationUnit("", ""); [Fact] public Task TestTopLevelClass() => TestCompilationUnit(""" class [|C|] { } """, """ Name="C" Glyph=ClassInternal HasItems=False """); [Fact] public Task TestTwoTopLevelTypes() => TestCompilationUnit(""" class [|C|] { } class [|D|] { } """, """ Name="C" Glyph=ClassInternal HasItems=False Name="D" Glyph=ClassInternal HasItems=False """); [Fact] public Task TestDelegatesAndEnums() => TestCompilationUnit(""" delegate string [|D|](int x); enum [|E|] { } """, """ Name="D(int) : string" Glyph=DelegateInternal HasItems=False Name="E" Glyph=EnumInternal HasItems=False """); [Fact] public Task TestTypesInBlockNamespace() => TestCompilationUnit(""" namespace N { class [|C|] { } class [|D|] { } } """, """ Name="C" Glyph=ClassInternal HasItems=False Name="D" Glyph=ClassInternal HasItems=False """); [Fact] public Task TestTypesInFileScopedNamespace() => TestCompilationUnit(""" namespace N; class [|C|] { } class [|D|] { } """, """ Name="C" Glyph=ClassInternal HasItems=False Name="D" Glyph=ClassInternal HasItems=False """); [Fact] public Task TestTypesAcrossNamespaces() => TestCompilationUnit(""" class [|C|] { } namespace N { class [|D|] { } } """, """ Name="C" Glyph=ClassInternal HasItems=False Name="D" Glyph=ClassInternal HasItems=False """); [Theory, CombinatorialData] public Task TestTypePermutations( [CombinatorialValues("Public", "Private", "Protected", "Internal")] string accessibility, [CombinatorialValues("Record", "Class", "Interface", "Struct")] string type) => TestCompilationUnit($$""" {{accessibility.ToLowerInvariant()}} {{type.ToLowerInvariant()}} [|C|] { } """, $$""" Name="C" Glyph={{type switch { "Record" => "Class", "Struct" => "Structure", _ => type }}}{{accessibility}} HasItems=False """); [Theory, CombinatorialData] public Task TestTypeHasItems( [CombinatorialValues("Record", "Class", "Interface", "Struct")] string type) => TestCompilationUnit($$""" {{type.ToLowerInvariant()}} [|C|] { int i; } """, $$""" Name="C" Glyph={{type switch { "Record" => "Class", "Struct" => "Structure", _ => type }}}Internal HasItems=True """); [Fact] public Task TestEnumHasItems() => TestCompilationUnit(""" enum [|E|] { A, B, C } """, """ Name="E" Glyph=EnumInternal HasItems=True """); [Theory] [InlineData("int", "int")] [InlineData("int[]", "int[]")] [InlineData("int[][]", "int[][]")] [InlineData("int[,][,,]", "int[,][,,]")] [InlineData("int*", "int*")] [InlineData("int?", "int?")] [InlineData("(int, string)", "(int, string)")] [InlineData("(int a, string b)", "(int a, string b)")] [InlineData("delegate*unmanaged[a]<int, string>", "delegate*<int, string>")] [InlineData("A.B", "B")] [InlineData("A::B", "B")] [InlineData("A::B.C", "C")] [InlineData("A", "A")] [InlineData("A.B<C::D, E::F.G<int>>", "B<D, G<int>>")] public Task TestTypes( string parameterType, string resultType) => TestCompilationUnit($$""" delegate void [|D|]({{parameterType}} x); """, $$""" Name="D({{resultType}}) : void" Glyph=DelegateInternal HasItems=False """); [Fact] public Task TestGenericClass() => TestCompilationUnit(""" class [|C|]<T> { } """, """ Name="C<T>" Glyph=ClassInternal HasItems=False """); [Fact] public Task TestGenericDelegate() => TestCompilationUnit(""" delegate void [|D|]<T>(); """, """ Name="D<T>() : void" Glyph=DelegateInternal HasItems=False """); [Fact] public Task TestEnumMembers() => TestNode<EnumDeclarationSyntax>(""" enum E { [|A|], [|B|], [|C|] } """, """ Name="A" Glyph=EnumMemberPublic HasItems=False Name="B" Glyph=EnumMemberPublic HasItems=False Name="C" Glyph=EnumMemberPublic HasItems=False """); [Fact] public Task TestClassMembers() => TestNode<ClassDeclarationSyntax>(""" class C { private int [|a|], [|b|]; public P [|Prop|] => default; internal [|C|]() { } ~[|C|]() { } protected R [|this|][string s] => default; private event Action [|A|] { } public event Action [|B|], [|C|]; void [|M|]<T>(int a) { } public void IInterface.[|O|]() { } public static C operator [|+|](C c1, int a) => default; internal static implicit operator [|int|](C c1) => default; } """, """ Name="a : int" Glyph=FieldPrivate HasItems=False Name="b : int" Glyph=FieldPrivate HasItems=False Name="Prop : P" Glyph=PropertyPublic HasItems=False Name="C()" Glyph=MethodInternal HasItems=False Name="~C()" Glyph=MethodPrivate HasItems=False Name="this[string] : R" Glyph=PropertyProtected HasItems=False Name="A : Action" Glyph=EventPrivate HasItems=False Name="B : Action" Glyph=EventPublic HasItems=False Name="C : Action" Glyph=EventPublic HasItems=False Name="M<T>(int) : void" Glyph=MethodPrivate HasItems=False Name="O() : void" Glyph=MethodPublic HasItems=False Name="operator +(C, int) : C" Glyph=OperatorPublic HasItems=False Name="implicit operator int(C)" Glyph=OperatorInternal HasItems=False """); [Fact] public Task TestExtension1() => TestNode<ClassDeclarationSyntax>(""" static class C { [|extension|]<T>(int i) { } public static void [|M|](this int i) {} } """, """ Name="extension<T>(int)" Glyph=ClassPublic HasItems=False Name="M(int) : void" Glyph=ExtensionMethodPublic HasItems=False """); [Fact] public Task TestExtension2() => TestNode<ExtensionBlockDeclarationSyntax>(""" static class C { extension<T>(int i) { public void [|M|]() { } } } """, """ Name="M() : void" Glyph=ExtensionMethodPublic HasItems=False """); [Fact] public Task TestMemberSetsHasItemsForLocalFunction() => TestNode<ClassDeclarationSyntax>(""" class C { private void [|M|]() { void MethodLocal() { } } } """, """ Name="M() : void" Glyph=MethodPrivate HasItems=True """); [Fact] public Task TestLocalFunctionSetsHasItemsForNestedLocalFunction() => TestNode<MethodDeclarationSyntax>(""" class C { private void M() { void [|MethodLocal|]() { void NestedLocal() { } } } } """, """ Name="MethodLocal() : void" Glyph=MethodPrivate HasItems=True """); [Fact] public Task TestLocalFunctionReturnsNestedLocalFunction() => TestNode<LocalFunctionStatementSyntax>(""" class C { void M() { void MethodLocal() { void [|NestedLocal|]() { } } } } """, """ Name="NestedLocal() : void" Glyph=MethodPrivate HasItems=False """); [Fact] public Task TestPropertyReturnsLocalFunctions() => TestNode<PropertyDeclarationSyntax>(""" class C { public P Prop { get { void [|GetLocal|]() { } return default; } set { void [|SetLocal|]() { } } } } """, """ Name="GetLocal() : void" Glyph=MethodPrivate HasItems=False Name="SetLocal() : void" Glyph=MethodPrivate HasItems=False """); [Fact] public Task TestConstructorReturnsLocalFunction() => TestNode<ConstructorDeclarationSyntax>(""" class C { internal C() { void [|CtorLocal|]() { } } } """, """ Name="CtorLocal() : void" Glyph=MethodPrivate HasItems=False """); [Fact] public Task TestDestructorReturnsLocalFunction() => TestNode<DestructorDeclarationSyntax>(""" class C { ~C() { void [|DtorLocal|]() { } } } """, """ Name="DtorLocal() : void" Glyph=MethodPrivate HasItems=False """); [Fact] public Task TestMethodReturnsLocalFunction() => TestNode<MethodDeclarationSyntax>(""" class C { void M<T>(int a) { void [|MethodLocal|]() { } } } """, """ Name="MethodLocal() : void" Glyph=MethodPrivate HasItems=False """); [Fact] public Task TestOperatorReturnsLocalFunction() => TestNode<OperatorDeclarationSyntax>(""" class C { public static C operator +(C c1, int a) { void [|OperatorLocal|]() { } return default; } } """, """ Name="OperatorLocal() : void" Glyph=MethodPrivate HasItems=False """); [Fact] public Task TestConversionOperatorReturnsLocalFunction() => TestNode<ConversionOperatorDeclarationSyntax>(""" class C { internal static implicit operator int(C c1) { void [|ConversionLocal|]() { } return default; } } """, """ Name="ConversionLocal() : void" Glyph=MethodPrivate HasItems=False """); [Fact] public Task TestLocalFunctionWithParameters() => TestNode<MethodDeclarationSyntax>(""" class C { void M<T>(int a) { int [|MethodLocal|](string input) { } } } """, """ Name="MethodLocal(string) : int" Glyph=MethodPrivate HasItems=False """); #region Namespace Tests (returnNamespaces: true) [Fact] public Task TestBlockNamespace() => TestCompilationUnitWithNamespaces(""" namespace [|N|] { class C { } } """, """ Name="N" Glyph=Namespace HasItems=True """); [Fact] public Task TestBlockNamespaceEmpty() => TestCompilationUnitWithNamespaces(""" namespace [|N|] { } """, """ Name="N" Glyph=Namespace HasItems=False """); [Fact] public Task TestFileScopedNamespace() => TestCompilationUnitWithNamespaces(""" namespace [|N|]; class C { } """, """ Name="N" Glyph=Namespace HasItems=True """); [Fact] public Task TestFileScopedNamespaceEmpty() => TestCompilationUnitWithNamespaces(""" namespace [|N|]; """, """ Name="N" Glyph=Namespace HasItems=False """); [Fact] public Task TestMultipleBlockNamespaces() => TestCompilationUnitWithNamespaces(""" namespace [|N1|] { class C { } } namespace [|N2|] { class D { } } """, """ Name="N1" Glyph=Namespace HasItems=True Name="N2" Glyph=Namespace HasItems=True """); [Fact] public Task TestQualifiedNamespace() => TestCompilationUnitWithNamespaces(""" namespace [|A|].B.C { class D { } } """, """ Name="A.B.C" Glyph=Namespace HasItems=True """); [Fact] public Task TestNamespaceNextToTopLevelType() => TestCompilationUnitWithNamespaces(""" class [|C|] { } namespace [|N|] { class D { } } """, """ Name="C" Glyph=ClassInternal HasItems=False Name="N" Glyph=Namespace HasItems=True """); [Fact] public Task TestNestedBlockNamespaces() => TestCompilationUnitWithNamespaces(""" namespace [|Outer|] { namespace Inner { class C { } } } """, """ Name="Outer" Glyph=Namespace HasItems=True """); [Fact] public Task TestNestedNamespaceMembers() => TestNamespaceDeclaration(""" namespace Outer { namespace [|Inner|] { class C { } } } """, """ Name="Inner" Glyph=Namespace HasItems=True """); [Fact] public Task TestNamespaceMembersWithTypes() => TestNamespaceDeclaration(""" namespace N { class [|C|] { } struct [|S|] { } } """, """ Name="C" Glyph=ClassInternal HasItems=False Name="S" Glyph=StructureInternal HasItems=False """); [Fact] public Task TestNamespaceMembersWithNestedNamespaceAndTypes() => TestNamespaceDeclaration(""" namespace N { class [|C|] { } namespace [|Inner|] { class D { } } } """, """ Name="C" Glyph=ClassInternal HasItems=False Name="Inner" Glyph=Namespace HasItems=True """); [Fact] public Task TestFileScopedNamespaceMembers() => TestNamespaceDeclaration(""" namespace N; class [|C|] { } enum [|E|] { } """, """ Name="C" Glyph=ClassInternal HasItems=False Name="E" Glyph=EnumInternal HasItems=False """); [Fact] public Task TestNamespaceWithDelegateAndEnum() => TestNamespaceDeclaration(""" namespace N { delegate void [|D|](); enum [|E|] { A } } """, """ Name="D() : void" Glyph=DelegateInternal HasItems=False Name="E" Glyph=EnumInternal HasItems=True """); #endregion }