' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. Imports System.Threading.Tasks Imports Microsoft.CodeAnalysis Imports Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces Imports Microsoft.VisualStudio.LanguageServices.Implementation.CodeModel Imports Microsoft.VisualStudio.LanguageServices.Implementation.CodeModel.InternalElements Imports Microsoft.VisualStudio.LanguageServices.Implementation.Interop Imports Roslyn.Test.Utilities Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.CodeModel.CSharp Public Class CSharpFileCodeModelTests Inherits AbstractFileCodeModelTests Public Async Function TestEnumerationWithCountAndItem() As Task Dim code = namespace N { } class C { } interface I { } struct S { } enum E { } delegate void D(); Using workspaceAndFileCodeModel = Await CreateCodeModelTestStateAsync(GetWorkspaceDefinition(code)) Dim codeElements = workspaceAndFileCodeModel.FileCodeModel.CodeElements Dim count = codeElements.Count Assert.Equal(6, count) Dim expectedKinds = {EnvDTE.vsCMElement.vsCMElementNamespace, EnvDTE.vsCMElement.vsCMElementClass, EnvDTE.vsCMElement.vsCMElementInterface, EnvDTE.vsCMElement.vsCMElementStruct, EnvDTE.vsCMElement.vsCMElementEnum, EnvDTE.vsCMElement.vsCMElementDelegate} Dim expectedNames = {"N", "C", "I", "S", "E", "D"} For i = 0 To count - 1 Dim element = codeElements.Item(i + 1) Assert.Equal(expectedKinds(i), element.Kind) Assert.Equal(expectedNames(i), element.Name) Next Dim j As Integer For Each element As EnvDTE.CodeElement In codeElements Assert.Equal(expectedKinds(j), element.Kind) Assert.Equal(expectedNames(j), element.Name) j += 1 Next End Using End Function Public Async Function TestAssemblyLevelAttribute() As Task Dim code = [assembly: Foo(0, true, S = "x")] class FooAttribute : System.Attribute { public FooAttribute(int i, bool b) { } public string S { get { return string.Empty; } set { } } } Using workspaceAndFileCodeModel = Await CreateCodeModelTestStateAsync(GetWorkspaceDefinition(code)) Dim codeElements = workspaceAndFileCodeModel.FileCodeModel.CodeElements Dim count = codeElements.Count Assert.Equal(2, count) Dim codeAttribute = TryCast(codeElements.Item(1), EnvDTE80.CodeAttribute2) Assert.NotNull(codeAttribute) Assert.Same(workspaceAndFileCodeModel.FileCodeModel, codeAttribute.Parent) Assert.Equal("Foo", codeAttribute.Name) Assert.Equal("FooAttribute", codeAttribute.FullName) Assert.Equal("assembly", codeAttribute.Target) Assert.Equal("0, true, S = ""x""", codeAttribute.Value) Dim arguments = codeAttribute.Arguments Assert.Equal(3, arguments.Count) Dim arg1 = TryCast(arguments.Item(1), EnvDTE80.CodeAttributeArgument) Assert.NotNull(arg1) Assert.Equal("", arg1.Name) Assert.Equal("0", arg1.Value) Dim arg2 = TryCast(arguments.Item(2), EnvDTE80.CodeAttributeArgument) Assert.NotNull(arg2) Assert.Equal("", arg2.Name) Assert.Equal("true", arg2.Value) Dim arg3 = TryCast(arguments.Item(3), EnvDTE80.CodeAttributeArgument) Assert.NotNull(arg3) Assert.Equal("S", arg3.Name) Assert.Equal("""x""", arg3.Value) End Using End Function #Region "AddAttribute tests" Public Async Function TestAddAttribute1() As Task Dim code = class $$C { } Dim expected = [assembly: System.CLSCompliant(true)] class C { } Await TestAddAttribute(code, expected, New AttributeData With {.Name = "System.CLSCompliant", .Value = "true"}) End Function Public Async Function TestAddAttribute2() As Task Dim code = class $$C { } Dim expected = [assembly: System.CLSCompliant(true)] class C { } Await TestAddAttribute(code, expected, New AttributeData With {.Name = "System.CLSCompliant", .Value = "true", .Position = "C"}) End Function Public Async Function TestAddAttribute3() As Task Dim code = $$[assembly: System.Reflection.AssemblyCompany("Microsoft")] Dim expected = [assembly: System.Reflection.AssemblyCompany("Microsoft")] [assembly: System.CLSCompliant(true)] Await TestAddAttribute(code, expected, New AttributeData With {.Name = "System.CLSCompliant", .Value = "true", .Position = -1}) End Function Public Async Function TestAddAttribute4() As Task Dim code = $$[assembly: System.Reflection.AssemblyCompany("Microsoft")] class C { } Dim expected = [assembly: System.Reflection.AssemblyCompany("Microsoft")] [assembly: System.CLSCompliant(true)] class C { } Await TestAddAttribute(code, expected, New AttributeData With {.Name = "System.CLSCompliant", .Value = "true", .Position = -1}) End Function Public Async Function TestAddAttribute5() As Task Dim code = $$[assembly: System.Reflection.AssemblyCompany("Microsoft")] [assembly: System.Reflection.AssemblyCopyright("2012")] class C { } Dim expected = [assembly: System.Reflection.AssemblyCompany("Microsoft")] [assembly: System.Reflection.AssemblyCopyright("2012")] [assembly: System.CLSCompliant(true)] class C { } Await TestAddAttribute(code, expected, New AttributeData With {.Name = "System.CLSCompliant", .Value = "true", .Position = -1}) End Function Public Async Function TestAddAttribute6() As Task Dim code = /// <summary></summary> class $$C { } Dim expected = [assembly: System.CLSCompliant(true)] /// <summary></summary> class C { } Await TestAddAttribute(code, expected, New AttributeData With {.Name = "System.CLSCompliant", .Value = "true"}) End Function #End Region #Region "AddClass tests" Public Async Function TestAddClass1() As Task Dim code = class $$C { } Dim expected = class B { } class C { } Await TestAddClass(code, expected, New ClassData With {.Name = "B"}) End Function Public Async Function TestAddClass2() As Task Dim code = class $$C { } Dim expected = class B { } class C { } Await TestAddClass(code, expected, New ClassData With {.Name = "B"}) End Function Public Async Function TestAddClass3() As Task Dim code = class $$C { } Dim expected = class C { } class B { } Await TestAddClass(code, expected, New ClassData With {.Name = "B", .Position = "C"}) End Function Public Async Function TestAddClass4() As Task Dim code = class $$C { } Dim expected = class C { } class B { } Await TestAddClass(code, expected, New ClassData With {.Name = "B", .Position = "C"}) End Function Public Async Function TestAddClass5() As Task Dim code = class $$C { } Dim expected = class C { } class B : C { } Await TestAddClass(code, expected, New ClassData With {.Name = "B", .Position = "C", .Bases = {"C"}}) End Function Public Async Function TestAddClass6() As Task Dim code = class $$C { } Dim expected = class C { } class B : C { } Await TestAddClass(code, expected, New ClassData With {.Name = "B", .Position = "C", .Bases = "C"}) End Function Public Async Function TestAddClass7() As Task Dim code = interface $$I { } Dim expected = interface I { } class C : I { } Await TestAddClass(code, expected, New ClassData With {.Name = "C", .Position = "I", .Bases = {"I"}}) End Function Public Async Function TestAddClass8() As Task Dim code = interface $$I { } Dim expected = interface I { } class C : I { } Await TestAddClass(code, expected, New ClassData With {.Name = "C", .Position = "I", .Bases = "I"}) End Function Public Async Function TestAddClass9() As Task Dim code = class B { } interface $$I { } Dim expected = class B { } interface I { } class C : B, I { } Await TestAddClass(code, expected, New ClassData With {.Name = "C", .Position = "I", .Bases = "B", .ImplementedInterfaces = "I"}) End Function Public Async Function TestAddClass10() As Task Dim code = class B { } interface $$IFoo { } interface IBar { } Dim expected = class B { } interface IFoo { } interface IBar { } class C : B, IFoo, IBar { } Await TestAddClass(code, expected, New ClassData With {.Name = "C", .Position = "IBar", .Bases = "B", .ImplementedInterfaces = {"IFoo", "IBar"}}) End Function Public Async Function TestAddClass_Stress() As Task Dim code = class B { } interface $$IFoo { } interface IBar { } Await TestOperation(code, Sub(fileCodeModel) For i = 1 To 100 Dim name = $"C{i}" Dim newClass = fileCodeModel.AddClass(name, Position:=-1, Bases:="B", ImplementedInterfaces:={"IFoo", "IBar"}) Assert.NotNull(newClass) Assert.Equal(name, newClass.Name) Next End Sub) End Function #End Region #Region "AddDelegate tests" Public Async Function TestAddDelegate1() As Task Dim code = class $$C { } Dim expected = delegate void D(); class C { } Await TestAddDelegate(code, expected, New DelegateData With {.Name = "D", .Type = "void"}) End Function Public Async Function TestAddDelegate2() As Task Dim code = class $$C { } Dim expected = class C { } delegate int D(); Await TestAddDelegate(code, expected, New DelegateData With {.Name = "D", .Type = "int", .Position = "C"}) End Function #End Region #Region "AddEnum tests" Public Async Function TestAddEnum1() As Task Dim code = class $$C { } Dim expected = enum E { } class C { } Await TestAddEnum(code, expected, New EnumData With {.Name = "E"}) End Function Public Async Function TestAddEnum2() As Task Dim code = class $$C { } Dim expected = class C { } enum E { } Await TestAddEnum(code, expected, New EnumData With {.Name = "E", .Position = "C"}) End Function #End Region #Region "AddImport tests" Public Async Function TestAddImport1() As Task Dim code = class $$C { } Dim expected = using System; class C { } Await TestAddImport(code, expected, New ImportData With {.[Namespace] = "System"}) End Function Public Async Function TestAddImport2() As Task Dim code = class $$C { } Dim expected = using S = System; class C { } Await TestAddImport(code, expected, New ImportData With {.[Namespace] = "System", .Alias = "S"}) End Function Public Async Function TestAddImport3() As Task Dim code = using System.Collections.Generic; class $$C { } Dim expected = using System; using System.Collections.Generic; class C { } Await TestAddImport(code, expected, New ImportData With {.[Namespace] = "System"}) End Function Public Async Function TestAddImport4() As Task Dim code = using System.Collections.Generic; class $$C { } Dim expected = using System.Collections.Generic; using System; class C { } Await TestAddImport(code, expected, New ImportData With {.[Namespace] = "System", .Position = -1}) End Function #End Region #Region "AddInterface tests" Public Async Function TestAddInterface1() As Task Dim code = class $$C { } Dim expected = interface I { } class C { } Await TestAddInterface(code, expected, New InterfaceData With {.Name = "I"}) End Function Public Async Function TestAddInterface2() As Task Dim code = class $$C { } Dim expected = class C { } interface I { } Await TestAddInterface(code, expected, New InterfaceData With {.Name = "I", .Position = "C"}) End Function #End Region #Region "AddNamespace tests" Public Async Function TestAddNamespace1() As Task Dim code = class $$C { } Dim expected = namespace N { } class C { } Await TestAddNamespace(code, expected, New NamespaceData With {.Name = "N"}) End Function Public Async Function TestAddNamespace2() As Task Dim code = class $$C { } Dim expected = namespace N { } class C { } Await TestAddNamespace(code, expected, New NamespaceData With {.Name = "N", .Position = 0}) End Function Public Async Function TestAddNamespace3() As Task Dim code = class $$C { } Dim expected = class C { } namespace N { } Await TestAddNamespace(code, expected, New NamespaceData With {.Name = "N", .Position = "C"}) End Function Public Async Function TestAddNamespace4() As Task Dim code = $$ Dim expected = namespace N { } Await TestAddNamespace(code, expected, New NamespaceData With {.Name = "N"}) End Function Public Async Function TestAddNamespace5() As Task Dim code = $$using System; Dim expected = using System; namespace N { } Await TestAddNamespace(code, expected, New NamespaceData With {.Name = "N"}) End Function Public Async Function TestAddNamespace6() As Task Dim code = $$using System; Dim expected = using System; namespace N { } Await TestAddNamespace(code, expected, New NamespaceData With {.Name = "N", .Position = 0}) End Function Public Async Function TestAddNamespace7() As Task Dim code = $$using System; Dim expected = using System; namespace N { } Await TestAddNamespace(code, expected, New NamespaceData With {.Name = "N", .Position = Type.Missing}) End Function #End Region #Region "AddStruct tests" Public Async Function TestAddStruct1() As Task Dim code = class $$C { } Dim expected = struct S { } class C { } Await TestAddStruct(code, expected, New StructData With {.Name = "S"}) End Function Public Async Function TestAddStruct2() As Task Dim code = class $$C { } Dim expected = class C { } struct S { } Await TestAddStruct(code, expected, New StructData With {.Name = "S", .Position = "C"}) End Function #End Region #Region "Remove tests" Public Async Function TestRemove1() As Task Dim code = class $$C { } Dim expected = Await TestRemoveChild(code, expected, "C") End Function Public Async Function TestRemove2() As Task Dim code = /// <summary> /// /// </summary> Class $$C { } Dim expected = Await TestRemoveChild(code, expected, "C") End Function #End Region Public Async Function TestClosedDocument() As Task Dim code = class $$C { void M() { } } Using state = Await CreateCodeModelTestStateAsync(GetWorkspaceDefinition(code)) Dim codeClass = state.GetCodeElementAtCursor(Of EnvDTE80.CodeClass2) Assert.Equal(1, codeClass.Members.OfType(Of EnvDTE80.CodeFunction2)().Count()) Dim project = state.VisualStudioWorkspace.CurrentSolution.Projects.First() Dim documentId = project.DocumentIds.First() state.VisualStudioWorkspace.CloseDocument(documentId) Dim newSolution = state.VisualStudioWorkspace.CurrentSolution.RemoveDocument(documentId) state.VisualStudioWorkspace.TryApplyChanges(newSolution) ' throws COMException with HResult = E_FAIL Assert.Throws(Of System.Runtime.InteropServices.COMException)( Sub() Dim count = codeClass.Members.OfType(Of EnvDTE80.CodeFunction2)().Count() End Sub) End Using End Function Public Async Function TestCreateUnknownElementForConversionOperator() As Task Dim oldCode = class D { public static implicit operator D(double d) { return new D(); } } Dim changedCode = class D { } Dim changedDefinition = CommonReferences="true"> <%= changedCode.Value %> Using originalWorkspaceAndFileCodeModel = Await CreateCodeModelTestStateAsync(GetWorkspaceDefinition(oldCode)) Using changedworkspace = TestWorkspaceFactory.CreateWorkspace(changedDefinition, exportProvider:=VisualStudioTestExportProvider.ExportProvider) Dim originalDocument = originalWorkspaceAndFileCodeModel.Workspace.CurrentSolution.GetDocument(originalWorkspaceAndFileCodeModel.Workspace.Documents(0).Id) Dim originalTree = originalDocument.GetSyntaxTreeAsync().Result Dim changeDocument = changedworkspace.CurrentSolution.GetDocument(changedworkspace.Documents(0).Id) Dim changeTree = changeDocument.GetSyntaxTreeAsync().Result Dim codeModelEvent = originalWorkspaceAndFileCodeModel.CodeModelService.CollectCodeModelEvents(originalTree, changeTree) Dim fileCodeModel = originalWorkspaceAndFileCodeModel.FileCodeModelObject Dim element As EnvDTE.CodeElement = Nothing Dim parentElement As Object = Nothing fileCodeModel.GetElementsForCodeModelEvent(codeModelEvent.First(), element, parentElement) Assert.NotNull(element) Assert.NotNull(parentElement) Dim unknownCodeFunction = TryCast(element, EnvDTE.CodeFunction) Assert.Equal(unknownCodeFunction.Name, "implicit operator D") End Using End Using End Function Public Async Function TestChangeClassNameAndGetNameOfChildFunction() As Task Dim code = class C { void M() { } } Await TestOperation(code, Sub(fileCodeModel) Dim codeClass = TryCast(fileCodeModel.CodeElements.Item(1), EnvDTE.CodeClass) Assert.NotNull(codeClass) Assert.Equal("C", codeClass.Name) Dim codeFunction = TryCast(codeClass.Members.Item(1), EnvDTE.CodeFunction) Assert.NotNull(codeFunction) Assert.Equal("M", codeFunction.Name) codeClass.Name = "NewClassName" Assert.Equal("NewClassName", codeClass.Name) Assert.Equal("M", codeFunction.Name) End Sub) End Function Public Async Function TestCodeElements_PropertyAccessor() As Task Dim code = class C { int P { get { return 0; } } } Await TestOperation(code, Sub(fileCodeModel) Dim classC = TryCast(fileCodeModel.CodeElements.Item(1), EnvDTE.CodeClass) Assert.NotNull(classC) Assert.Equal("C", classC.Name) Dim propertyP = TryCast(classC.Members.Item(1), EnvDTE.CodeProperty) Assert.NotNull(propertyP) Assert.Equal("P", propertyP.Name) Dim getter = propertyP.Getter Assert.NotNull(getter) Dim searchedGetter = fileCodeModel.CodeElementFromPoint(getter.StartPoint, EnvDTE.vsCMElement.vsCMElementFunction) Dim parent = TryCast(getter.Collection.Parent, EnvDTE.CodeProperty) Assert.NotNull(parent) Assert.Equal("P", parent.Name) ' This assert is very important! ' ' We are testing that we don't regress a bug where a property accessor creates its ' parent incorrectly such that *existing* Code Model objects for its parent ("P") get a different ' NodeKey that makes the existing objects invalid. If the bug regresses, the line below will ' fail with an ArguementException when trying to use propertyP's NodeKey to lookup its node. ' (Essentially, its NodeKey will be {C.P,2} rather than {C.P,1}). Assert.Equal("P", propertyP.Name) ' Sanity: ensure that the NodeKeys are correct Dim member1 = ComAggregate.GetManagedObject(Of AbstractCodeMember)(parent) Dim member2 = ComAggregate.GetManagedObject(Of AbstractCodeMember)(propertyP) Assert.Equal("C.P", member1.NodeKey.Name) Assert.Equal(1, member1.NodeKey.Ordinal) Assert.Equal("C.P", member2.NodeKey.Name) Assert.Equal(1, member2.NodeKey.Ordinal) End Sub) End Function Public Async Function TestCodeElements_EventAccessor() As Task Dim code = class C { event System.EventHandler E { add { } remove { } } } Await TestOperation(code, Sub(fileCodeModel) Dim classC = TryCast(fileCodeModel.CodeElements.Item(1), EnvDTE.CodeClass) Assert.NotNull(classC) Assert.Equal("C", classC.Name) Dim eventE = TryCast(classC.Members.Item(1), EnvDTE80.CodeEvent) Assert.NotNull(eventE) Assert.Equal("E", eventE.Name) Dim adder = eventE.Adder Assert.NotNull(adder) Dim searchedAdder = fileCodeModel.CodeElementFromPoint(adder.StartPoint, EnvDTE.vsCMElement.vsCMElementFunction) Dim parent = TryCast(adder.Collection.Parent, EnvDTE80.CodeEvent) Assert.NotNull(parent) Assert.Equal("E", parent.Name) ' This assert is very important! ' ' We are testing that we don't regress a bug where an event accessor creates its ' parent incorrectly such that *existing* Code Model objects for its parent ("P") get a different ' NodeKey that makes the existing objects invalid. If the bug regresses, the line below will ' fail with an ArguementException when trying to use propertyP's NodeKey to lookup its node. ' (Essentially, its NodeKey will be {C.E,2} rather than {C.E,1}). Assert.Equal("E", eventE.Name) ' Sanity: ensure that the NodeKeys are correct Dim member1 = ComAggregate.GetManagedObject(Of AbstractCodeMember)(parent) Dim member2 = ComAggregate.GetManagedObject(Of AbstractCodeMember)(eventE) Assert.Equal("C.E", member1.NodeKey.Name) Assert.Equal(1, member1.NodeKey.Ordinal) Assert.Equal("C.E", member2.NodeKey.Name) Assert.Equal(1, member2.NodeKey.Ordinal) End Sub) End Function Protected Overrides ReadOnly Property LanguageName As String Get Return LanguageNames.CSharp End Get End Property End Class End Namespace