File: SolutionExplorer\CSharpSolutionExplorerSymbolTreeItemProviderTests.cs
Web Access
Project: src\src\Features\CSharpTest\Microsoft.CodeAnalysis.CSharp.Features.UnitTests.csproj (Microsoft.CodeAnalysis.CSharp.Features.UnitTests)
// 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);
    }
 
    [Fact]
    public async Task TestEmptyFile()
    {
        await TestCompilationUnit("", "");
    }
 
    [Fact]
    public async Task TestTopLevelClass()
    {
        await TestCompilationUnit("""
            class [|C|]
            {
            }
            """, """
            Name="C" Glyph=ClassInternal HasItems=False
            """);
    }
 
    [Fact]
    public async Task TestTwoTopLevelTypes()
    {
        await TestCompilationUnit("""
            class [|C|]
            {
            }
 
            class [|D|]
            {
            }
            """, """
            Name="C" Glyph=ClassInternal HasItems=False
            Name="D" Glyph=ClassInternal HasItems=False
            """);
    }
 
    [Fact]
    public async Task TestDelegatesAndEnums()
    {
        await TestCompilationUnit("""
            delegate string [|D|](int x);
 
            enum [|E|]
            {
            }
            """, """
            Name="D(int) : string" Glyph=DelegateInternal HasItems=False
            Name="E" Glyph=EnumInternal HasItems=False
            """);
    }
 
    [Fact]
    public async Task TestTypesInBlockNamespace()
    {
        await TestCompilationUnit("""
            namespace N
            {
                class [|C|]
                {
                }
 
                class [|D|]
                {
                }
            }
            """, """
            Name="C" Glyph=ClassInternal HasItems=False
            Name="D" Glyph=ClassInternal HasItems=False
            """);
    }
 
    [Fact]
    public async Task TestTypesInFileScopedNamespace()
    {
        await TestCompilationUnit("""
            namespace N;
 
            class [|C|]
            {
            }
 
            class [|D|]
            {
            }
            """, """
            Name="C" Glyph=ClassInternal HasItems=False
            Name="D" Glyph=ClassInternal HasItems=False
            """);
    }
 
    [Fact]
    public async Task TestTypesAcrossNamespaces()
    {
        await TestCompilationUnit("""
            class [|C|]
            {
            }
 
            namespace N
            {
                class [|D|]
                {
                }
            }
            """, """
            Name="C" Glyph=ClassInternal HasItems=False
            Name="D" Glyph=ClassInternal HasItems=False
            """);
    }
 
    [Theory, CombinatorialData]
    public async Task TestTypePermutations(
        [CombinatorialValues("Public", "Private", "Protected", "Internal")] string accessibility,
        [CombinatorialValues("Record", "Class", "Interface", "Struct")] string type)
    {
        await TestCompilationUnit($$"""
            {{accessibility.ToLowerInvariant()}} {{type.ToLowerInvariant()}} [|C|]
            {
            }
            """, $$"""
            Name="C" Glyph={{type switch { "Record" => "Class", "Struct" => "Structure", _ => type }}}{{accessibility}} HasItems=False
            """);
    }
 
    [Theory, CombinatorialData]
    public async Task TestTypeHasItems(
        [CombinatorialValues("Record", "Class", "Interface", "Struct")] string type)
    {
        await TestCompilationUnit($$"""
            {{type.ToLowerInvariant()}} [|C|]
            {
                int i;
            }
            """, $$"""
            Name="C" Glyph={{type switch { "Record" => "Class", "Struct" => "Structure", _ => type }}}Internal HasItems=True
            """);
    }
 
    [Fact]
    public async Task TestEnumHasItems()
    {
        await 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 async Task TestTypes(
        string parameterType, string resultType)
    {
        await TestCompilationUnit($$"""
            delegate void [|D|]({{parameterType}} x);
            """, $$"""
            Name="D({{resultType}}) : void" Glyph=DelegateInternal HasItems=False
            """);
    }
 
    [Fact]
    public async Task TestGenericClass()
    {
        await TestCompilationUnit("""
            class [|C|]<T>
            {
            }
            """, """
            Name="C<T>" Glyph=ClassInternal HasItems=False
            """);
    }
 
    [Fact]
    public async Task TestGenericDelegate()
    {
        await TestCompilationUnit("""
            delegate void [|D|]<T>();
            """, """
            Name="D<T>() : void" Glyph=DelegateInternal HasItems=False
            """);
    }
 
    [Fact]
    public async Task TestEnumMembers()
    {
        await 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 async Task TestClassMembers()
    {
        await 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 async Task TestExtension1()
    {
        await 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 async Task TestExtension2()
    {
        await TestNode<ExtensionBlockDeclarationSyntax>("""
            static class C
            {
                extension<T>(int i)
                {
                    public void [|M|]() { }
                }
            }
            """, """
            Name="M() : void" Glyph=ExtensionMethodPublic HasItems=False
            """);
    }
}