File: InheritanceMargin\InheritanceMarginViewModelTests.vb
Web Access
Project: src\src\VisualStudio\Core\Test\Microsoft.VisualStudio.LanguageServices.UnitTests.vbproj (Microsoft.VisualStudio.LanguageServices.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.
 
Imports System.Collections.Immutable
Imports System.Runtime.Remoting.Messaging
Imports System.Threading
Imports System.Windows
Imports System.Windows.Documents
Imports Microsoft.CodeAnalysis
Imports Microsoft.CodeAnalysis.Editor.Shared.Utilities
Imports Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces
Imports Microsoft.CodeAnalysis.InheritanceMargin
Imports Microsoft.CodeAnalysis.Shared.Extensions
Imports Microsoft.CodeAnalysis.Test.Utilities
Imports Microsoft.VisualStudio.Core.Imaging
Imports Microsoft.VisualStudio.Imaging
Imports Microsoft.VisualStudio.Imaging.Interop
Imports Microsoft.VisualStudio.LanguageServices.Implementation.InheritanceMargin
Imports Microsoft.VisualStudio.LanguageServices.Implementation.InheritanceMargin.MarginGlyph
Imports Microsoft.VisualStudio.Text.Classification
Imports Roslyn.Test.Utilities
 
Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.InheritanceMargin
 
    <Trait(Traits.Feature, Traits.Features.InheritanceMargin)>
    <UseExportProvider>
    Public Class InheritanceMarginViewModelTests
 
        Private Shared ReadOnly s_defaultMargin As Thickness = New Thickness(4, 1, 4, 1)
 
        Private Shared ReadOnly s_indentMargin As Thickness = New Thickness(22, 1, 4, 1)
 
        Private Structure GlyphViewModelData
            Public ReadOnly Property ImageMoniker As ImageMoniker
            Public ReadOnly Property ToolTipText As String
            Public ReadOnly Property AutomationName As String
            Public ReadOnly Property ScaleFactor As Double
            Public ReadOnly Property MenuItems As MenuItemViewModelData()
 
            Public Sub New(imageMoniker As ImageMoniker, toolTipText As String, automationName As String, scaleFactor As Double, ParamArray menuItems() As MenuItemViewModelData)
                Me.ImageMoniker = imageMoniker
                Me.ToolTipText = toolTipText
                Me.AutomationName = automationName
                Me.ScaleFactor = scaleFactor
                Me.MenuItems = menuItems
            End Sub
        End Structure
 
        Private Structure MenuItemViewModelData
            Public ReadOnly Property AutomationName As String
            Public ReadOnly Property DisplayContent As String
            Public ReadOnly Property ImageMoniker As ImageMoniker
            Public ReadOnly Property ViewModelType As Type
            Public ReadOnly Property MenuItems As MenuItemViewModelData()
 
            Public Sub New(automationName As String, displayContent As String, imageMoniker As ImageMoniker, viewModelType As Type, ParamArray menuItems() As MenuItemViewModelData)
                Me.AutomationName = automationName
                Me.DisplayContent = displayContent
                Me.ImageMoniker = imageMoniker
                Me.ViewModelType = viewModelType
                Me.MenuItems = menuItems
            End Sub
        End Structure
 
        Private Shared Async Function VerifyAsync(markup As String, languageName As String, expectedViewModels As Dictionary(Of Integer, GlyphViewModelData)) As Task
            ' Add an lf before the document so that the line number starts
            ' with 1, which meets the line number in the editor (but in fact all things start from 0)
            Dim workspaceFile =
            <Workspace>
                <Project Language=<%= languageName %> CommonReferences="true">
                    <Document><%= vbLf %>
                        <%= markup.Replace(vbCrLf, vbLf) %>
                    </Document>
                </Project>
            </Workspace>
 
            Dim cancellationToken As CancellationToken = CancellationToken.None
            Using workspace = EditorTestWorkspace.Create(workspaceFile)
                Dim testDocument = workspace.Documents.Single()
                Dim document = workspace.CurrentSolution.GetDocument(testDocument.Id)
                Dim service = document.GetRequiredLanguageService(Of IInheritanceMarginService)
 
                Dim classificationTypeMap = workspace.ExportProvider.GetExportedValue(Of ClassificationTypeMap)
                Dim classificationFormatMap = workspace.ExportProvider.GetExportedValue(Of IClassificationFormatMapService)
 
                ' For these tests, we need to be on UI thread, so don't call ConfigureAwait(False)
                Dim root = Await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(True)
                Dim inheritanceItems = Await service.GetInheritanceMemberItemsAsync(
                    document, root.FullSpan, includeGlobalImports:=True, frozenPartialSemantics:=True, cancellationToken).ConfigureAwait(True)
 
                Dim acutalLineToTagDictionary = inheritanceItems.GroupBy(Function(item) item.LineNumber) _
                    .ToDictionary(Function(grouping) grouping.Key,
                                  Function(grouping)
                                      Dim lineNumber = grouping.Key
                                      Dim items = grouping.Select(Function(g) g).ToImmutableArray()
                                      Return New InheritanceMarginTag(lineNumber, items)
                                  End Function)
                Assert.Equal(expectedViewModels.Count, acutalLineToTagDictionary.Count)
 
                For Each kvp In expectedViewModels
                    Dim lineNumber = kvp.Key
                    Dim expectedViewModel = kvp.Value
                    Assert.True(acutalLineToTagDictionary.ContainsKey(lineNumber))
 
                    Dim acutalTag = acutalLineToTagDictionary(lineNumber)
                    ' Editor TestView zoom level is 100 based.
                    Dim actualViewModel = InheritanceMarginGlyphViewModel.Create(
                        classificationTypeMap, classificationFormatMap.GetClassificationFormatMap("tooltip"), acutalTag, 100)
 
                    VerifyTwoViewModelAreSame(expectedViewModel, actualViewModel)
                Next
 
            End Using
        End Function
 
        Private Shared Sub VerifyTwoViewModelAreSame(expected As GlyphViewModelData, actual As InheritanceMarginGlyphViewModel)
            Assert.Equal(expected.ImageMoniker, actual.ImageMoniker)
            Dim actualTextGetFromTextBlock = actual.ToolTipTextBlock.Inlines _
                .OfType(Of Run).Select(Function(run) run.Text) _
                .Aggregate(Function(text1, text2) text1 + text2)
            ' When the text block is created, a unicode 'left to right' would be inserted between the space.
            ' Make sure it is removed.
            Dim leftToRightMarker = Char.ConvertFromUtf32(&H200E)
            Dim actualText = actualTextGetFromTextBlock.Replace(leftToRightMarker, String.Empty)
            Assert.Equal(expected.ToolTipText, actualText)
            Assert.Equal(expected.AutomationName, actual.AutomationName)
            Assert.Equal(expected.MenuItems.Length, actual.MenuItemViewModels.Length)
            Assert.Equal(expected.ScaleFactor, actual.ScaleFactor)
 
            For i = 0 To expected.MenuItems.Length - 1
                Dim expectedMenuItem = expected.MenuItems(i)
                Dim actualMenuItem = actual.MenuItemViewModels(i)
                VerifyMenuItem(expectedMenuItem, actualMenuItem)
            Next
 
        End Sub
 
        Private Shared Sub VerifyMenuItem(expected As MenuItemViewModelData, actual As MenuItemViewModel)
            Assert.Equal(expected.AutomationName, actual.AutomationName)
            Assert.Equal(expected.DisplayContent, actual.DisplayContent)
            Assert.Equal(expected.ImageMoniker, actual.ImageMoniker)
 
            Assert.IsType(expected.ViewModelType, actual)
 
            If expected.ViewModelType = GetType(MemberMenuItemViewModel) Then
                Dim acutalMemberMenuItem = CType(actual, MemberMenuItemViewModel)
                Assert.Equal(expected.MenuItems.Length, acutalMemberMenuItem.Targets.Length)
                For i = 0 To expected.MenuItems.Length - 1
                    VerifyMenuItem(expected.MenuItems(i), acutalMemberMenuItem.Targets(i))
                Next
            End If
        End Sub
 
        <WpfFact>
        Public Function TestClassImplementsInterfaceRelationship() As Task
            Dim markup = "
public interface IBar
{
}
public class Bar : IBar
{
}"
            Dim tooltipTextForIBar = String.Format(ServicesVSResources._0_is_inherited, "interface IBar")
            Dim tooltipTextForBar = String.Format(ServicesVSResources._0_is_inherited, "class Bar")
            Return VerifyAsync(markup, LanguageNames.CSharp, New Dictionary(Of Integer, GlyphViewModelData) From {
                {2, New GlyphViewModelData(
                    KnownMonikers.Implemented,
                    tooltipTextForIBar,
                    tooltipTextForIBar,
                    1,
                    New MenuItemViewModelData(ServicesVSResources.Implementing_types, ServicesVSResources.Implementing_types, KnownMonikers.Implemented, GetType(HeaderMenuItemViewModel)),
                    New MenuItemViewModelData("Bar", "Bar", KnownMonikers.ClassPublic, GetType(TargetMenuItemViewModel)))},
                {5, New GlyphViewModelData(
                    KnownMonikers.Implementing,
                    tooltipTextForBar,
                    tooltipTextForBar,
                    1,
                    New MenuItemViewModelData(ServicesVSResources.Implemented_interfaces, ServicesVSResources.Implemented_interfaces, KnownMonikers.Implementing, GetType(HeaderMenuItemViewModel)),
                    New MenuItemViewModelData("IBar", "IBar", KnownMonikers.InterfacePublic, GetType(TargetMenuItemViewModel)))}})
        End Function
 
        <WpfFact>
        Public Function TestClassOverridesAbstractClassRelationship() As Task
            Dim markup = "
public abstract class AbsBar
{
    public abstract void Foo();
}
 
public class Bar : AbsBar
{
    public override void Foo();
}"
 
            Dim tooltipTextForAbsBar = String.Format(ServicesVSResources._0_is_inherited, "class AbsBar")
            Dim tooltipTextForAbstractFoo = String.Format(ServicesVSResources._0_is_inherited, "abstract void AbsBar.Foo()")
            Dim tooltipTextForBar = String.Format(ServicesVSResources._0_is_inherited, "class Bar")
            Dim tooltipTextForOverrideFoo = String.Format(ServicesVSResources._0_is_inherited, "override void Bar.Foo()")
            Return VerifyAsync(markup, LanguageNames.CSharp, New Dictionary(Of Integer, GlyphViewModelData) From {
                {2, New GlyphViewModelData(
                    KnownMonikers.Overridden,
                    tooltipTextForAbsBar,
                    tooltipTextForAbsBar,
                    1,
                    New MenuItemViewModelData(ServicesVSResources.Derived_types, ServicesVSResources.Derived_types, KnownMonikers.Overridden, GetType(HeaderMenuItemViewModel)),
                    New MenuItemViewModelData("Bar", "Bar", KnownMonikers.ClassPublic, GetType(TargetMenuItemViewModel)))},
                {4, New GlyphViewModelData(
                    KnownMonikers.Overridden,
                    tooltipTextForAbstractFoo,
                    tooltipTextForAbstractFoo,
                    1,
                    New MenuItemViewModelData(ServicesVSResources.Overriding_members, ServicesVSResources.Overriding_members, KnownMonikers.Overridden, GetType(HeaderMenuItemViewModel)),
                    New MenuItemViewModelData("Bar.Foo", "Bar.Foo", KnownMonikers.MethodPublic, GetType(TargetMenuItemViewModel)))},
                {7, New GlyphViewModelData(
                    KnownMonikers.Overriding,
                    tooltipTextForBar,
                    tooltipTextForBar,
                    1,
                    New MenuItemViewModelData(ServicesVSResources.Base_Types, ServicesVSResources.Base_Types, KnownMonikers.Overriding, GetType(HeaderMenuItemViewModel)),
                    New MenuItemViewModelData("AbsBar", "AbsBar", KnownMonikers.ClassPublic, GetType(TargetMenuItemViewModel)))},
                {9, New GlyphViewModelData(
                    KnownMonikers.Overriding,
                    tooltipTextForOverrideFoo,
                    tooltipTextForOverrideFoo,
                    1,
                    New MenuItemViewModelData(ServicesVSResources.Overridden_members, ServicesVSResources.Overridden_members, KnownMonikers.Overriding, GetType(HeaderMenuItemViewModel)),
                    New MenuItemViewModelData("AbsBar.Foo", "AbsBar.Foo", KnownMonikers.MethodPublic, GetType(TargetMenuItemViewModel)))}})
        End Function
 
        <WpfFact>
        Public Function TestInterfaceImplementsInterfaceRelationship() As Task
            Dim markup = "
public interface IBar1 { }
public interface IBar2 : IBar1 { }
public interface IBar3 : IBar2 { }
"
            Dim tooltipTextForIBar1 = String.Format(ServicesVSResources._0_is_inherited, "interface IBar1")
            Dim tooltipTextForIBar2 = String.Format(ServicesVSResources._0_is_inherited, "interface IBar2")
            Dim tooltipTextForIBar3 = String.Format(ServicesVSResources._0_is_inherited, "interface IBar3")
            Return VerifyAsync(markup, LanguageNames.CSharp, New Dictionary(Of Integer, GlyphViewModelData) From {
                {2, New GlyphViewModelData(
                    KnownMonikers.Implemented,
                    tooltipTextForIBar1,
                    tooltipTextForIBar1,
                    1,
                    New MenuItemViewModelData(ServicesVSResources.Implementing_types, ServicesVSResources.Implementing_types, KnownMonikers.Implemented, GetType(HeaderMenuItemViewModel)),
                    New MenuItemViewModelData("IBar2", "IBar2", KnownMonikers.InterfacePublic, GetType(TargetMenuItemViewModel)),
                    New MenuItemViewModelData("IBar3", "IBar3", KnownMonikers.InterfacePublic, GetType(TargetMenuItemViewModel)))},
                {3, New GlyphViewModelData(
                    KnownMonikers.ImplementingImplemented,
                    tooltipTextForIBar2,
                    tooltipTextForIBar2,
                    1,
                    New MenuItemViewModelData(ServicesVSResources.Inherited_interfaces, ServicesVSResources.Inherited_interfaces, KnownMonikers.Implementing, GetType(HeaderMenuItemViewModel)),
                    New MenuItemViewModelData("IBar1", "IBar1", KnownMonikers.InterfacePublic, GetType(TargetMenuItemViewModel)),
                    New MenuItemViewModelData(ServicesVSResources.Implementing_types, ServicesVSResources.Implementing_types, KnownMonikers.Implemented, GetType(HeaderMenuItemViewModel)),
                    New MenuItemViewModelData("IBar3", "IBar3", KnownMonikers.InterfacePublic, GetType(TargetMenuItemViewModel)))},
                {4, New GlyphViewModelData(
                    KnownMonikers.Implementing,
                    tooltipTextForIBar3,
                    tooltipTextForIBar3,
                    1,
                    New MenuItemViewModelData(ServicesVSResources.Inherited_interfaces, ServicesVSResources.Inherited_interfaces, KnownMonikers.Implementing, GetType(HeaderMenuItemViewModel)),
                    New MenuItemViewModelData("IBar1", "IBar1", KnownMonikers.InterfacePublic, GetType(TargetMenuItemViewModel)),
                    New MenuItemViewModelData("IBar2", "IBar2", KnownMonikers.InterfacePublic, GetType(TargetMenuItemViewModel)))}})
        End Function
 
        <WpfFact>
        Public Function TestClassDerivesClass() As Task
            Dim markup = "
public class Bar1 {}
public class Bar2 : Bar1 {}
public class Bar3 : Bar2 {}"
 
            Dim tooltipTextForBar1 = String.Format(ServicesVSResources._0_is_inherited, "class Bar1")
            Dim targetForBar1 = ImmutableArray.Create(Of MenuItemViewModel)(New HeaderMenuItemViewModel(ServicesVSResources.Derived_types, KnownMonikers.Overridden)).
                Add(New TargetMenuItemViewModel("Bar2", KnownMonikers.ClassPublic, Nothing)).Add(New TargetMenuItemViewModel("Bar3", KnownMonikers.ClassPublic, Nothing))
 
            Dim tooltipTextForBar2 = String.Format(ServicesVSResources._0_is_inherited, "class Bar2")
 
            Dim targetForBar2 = ImmutableArray.Create(Of MenuItemViewModel)(
                New HeaderMenuItemViewModel(ServicesVSResources.Base_Types, KnownMonikers.Overriding)).
                    Add(New TargetMenuItemViewModel("Bar1", KnownMonikers.ClassPublic, Nothing)).
                        Add(New HeaderMenuItemViewModel(ServicesVSResources.Derived_types, KnownMonikers.Overridden)).
                    Add(New TargetMenuItemViewModel("Bar3", KnownMonikers.ClassPublic, Nothing))
 
            Dim tooltipTextForBar3 = String.Format(ServicesVSResources._0_is_inherited, "class Bar3")
            Dim targetForBar3 = ImmutableArray.Create(Of MenuItemViewModel)(New HeaderMenuItemViewModel(ServicesVSResources.Base_Types, KnownMonikers.Overriding)).
                Add(New TargetMenuItemViewModel("Bar1", KnownMonikers.ClassPublic, Nothing)).
                Add(New TargetMenuItemViewModel("Bar2", KnownMonikers.ClassPublic, Nothing))
 
            Return VerifyAsync(markup, LanguageNames.CSharp, New Dictionary(Of Integer, GlyphViewModelData) From {
                {2, New GlyphViewModelData(
                    KnownMonikers.Overridden,
                    tooltipTextForBar1,
                    tooltipTextForBar1,
                    1,
                    New MenuItemViewModelData(ServicesVSResources.Derived_types, ServicesVSResources.Derived_types, KnownMonikers.Overridden, GetType(HeaderMenuItemViewModel)),
                    New MenuItemViewModelData("Bar2", "Bar2", KnownMonikers.ClassPublic, GetType(TargetMenuItemViewModel)),
                    New MenuItemViewModelData("Bar3", "Bar3", KnownMonikers.ClassPublic, GetType(TargetMenuItemViewModel)))},
                {3, New GlyphViewModelData(
                    KnownMonikers.OverridingOverridden,
                    tooltipTextForBar2,
                    tooltipTextForBar2,
                    1,
                    New MenuItemViewModelData(ServicesVSResources.Base_Types, ServicesVSResources.Base_Types, KnownMonikers.Overriding, GetType(HeaderMenuItemViewModel)),
                    New MenuItemViewModelData("Bar1", "Bar1", KnownMonikers.ClassPublic, GetType(TargetMenuItemViewModel)),
                    New MenuItemViewModelData(ServicesVSResources.Derived_types, ServicesVSResources.Derived_types, KnownMonikers.Overridden, GetType(HeaderMenuItemViewModel)),
                    New MenuItemViewModelData("Bar3", "Bar3", KnownMonikers.ClassPublic, GetType(TargetMenuItemViewModel)))},
                {4, New GlyphViewModelData(
                    KnownMonikers.Overriding,
                    tooltipTextForBar3,
                    tooltipTextForBar3,
                    1,
                    New MenuItemViewModelData(ServicesVSResources.Base_Types, ServicesVSResources.Base_Types, KnownMonikers.Overriding, GetType(HeaderMenuItemViewModel)),
                    New MenuItemViewModelData("Bar1", "Bar1", KnownMonikers.ClassPublic, GetType(TargetMenuItemViewModel)),
                    New MenuItemViewModelData("Bar2", "Bar2", KnownMonikers.ClassPublic, GetType(TargetMenuItemViewModel)))}})
 
        End Function
 
        <WpfFact>
        Public Function TestMutipleMemberOnSameline() As Task
            Dim markup = "
using System;
interface IBar1
{
    public event EventHandler e1, e2;
}
 
public class BarSample : IBar1
{
    public virtual event EventHandler e1, e2;
}"
 
            Dim tooltipTextForIBar1 = String.Format(ServicesVSResources._0_is_inherited, "interface IBar1")
            Dim tooltipTextForE1AndE2InInterface = ServicesVSResources.Multiple_members_are_inherited
            Dim tooltipTextForBarSample = String.Format(ServicesVSResources._0_is_inherited, "class BarSample")
            Dim tooltipTextForE1AndE2InBarSample = ServicesVSResources.Multiple_members_are_inherited
            Return VerifyAsync(markup, LanguageNames.CSharp, New Dictionary(Of Integer, GlyphViewModelData) From {
                {3, New GlyphViewModelData(
                    KnownMonikers.Implemented,
                    tooltipTextForIBar1,
                    tooltipTextForIBar1,
                    1,
                    New MenuItemViewModelData(ServicesVSResources.Implementing_types, ServicesVSResources.Implementing_types, KnownMonikers.Implemented, GetType(HeaderMenuItemViewModel)),
                    New MenuItemViewModelData("BarSample", "BarSample", KnownMonikers.ClassPublic, GetType(TargetMenuItemViewModel)))},
                {5, New GlyphViewModelData(
                    KnownMonikers.Implemented,
                    tooltipTextForE1AndE2InInterface,
                    String.Format(ServicesVSResources.Multiple_members_are_inherited_on_line_0, 5),
                    1,
                    New MenuItemViewModelData("event EventHandler IBar1.e1", "event EventHandler IBar1.e1", KnownMonikers.EventPublic, GetType(MemberMenuItemViewModel),
                        New MenuItemViewModelData(ServicesVSResources.Implementing_members, ServicesVSResources.Implementing_members, KnownMonikers.Implemented, GetType(HeaderMenuItemViewModel)),
                        New MenuItemViewModelData("BarSample.e1", "BarSample.e1", KnownMonikers.EventPublic, GetType(TargetMenuItemViewModel))),
                    New MenuItemViewModelData("event EventHandler IBar1.e2", "event EventHandler IBar1.e2", KnownMonikers.EventPublic, GetType(MemberMenuItemViewModel),
                        New MenuItemViewModelData(ServicesVSResources.Implementing_members, ServicesVSResources.Implementing_members, KnownMonikers.Implemented, GetType(HeaderMenuItemViewModel)),
                        New MenuItemViewModelData("BarSample.e2", "BarSample.e2", KnownMonikers.EventPublic, GetType(TargetMenuItemViewModel))))},
                {8, New GlyphViewModelData(
                    KnownMonikers.Implementing,
                    tooltipTextForBarSample,
                    tooltipTextForBarSample,
                    1,
                    New MenuItemViewModelData(ServicesVSResources.Implemented_interfaces, ServicesVSResources.Implemented_interfaces, KnownMonikers.Implementing, GetType(HeaderMenuItemViewModel)),
                    New MenuItemViewModelData("IBar1", "IBar1", KnownMonikers.InterfaceInternal, GetType(TargetMenuItemViewModel)))},
                {10, New GlyphViewModelData(
                    KnownMonikers.Implementing,
                    tooltipTextForE1AndE2InBarSample,
                    String.Format(ServicesVSResources.Multiple_members_are_inherited_on_line_0, 10),
                    1,
                    New MenuItemViewModelData("virtual event EventHandler BarSample.e1", "virtual event EventHandler BarSample.e1", KnownMonikers.EventPublic, GetType(MemberMenuItemViewModel),
                        New MenuItemViewModelData(ServicesVSResources.Implemented_members, ServicesVSResources.Implemented_members, KnownMonikers.Implementing, GetType(HeaderMenuItemViewModel)),
                        New MenuItemViewModelData("IBar1.e1", "IBar1.e1", KnownMonikers.EventPublic, GetType(TargetMenuItemViewModel))),
                    New MenuItemViewModelData("virtual event EventHandler BarSample.e2", "virtual event EventHandler BarSample.e2", KnownMonikers.EventPublic, GetType(MemberMenuItemViewModel),
                        New MenuItemViewModelData(ServicesVSResources.Implemented_members, ServicesVSResources.Implemented_members, KnownMonikers.Implementing, GetType(HeaderMenuItemViewModel)),
                        New MenuItemViewModelData("IBar1.e2", "IBar1.e2", KnownMonikers.EventPublic, GetType(TargetMenuItemViewModel))))}})
        End Function
    End Class
End Namespace