File: Intellisense\TestState.vb
Web Access
Project: src\src\EditorFeatures\TestUtilities2\Microsoft.CodeAnalysis.EditorFeatures.Test.Utilities2.vbproj (Microsoft.CodeAnalysis.EditorFeatures.Test.Utilities2)
' 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.Threading
Imports Microsoft.CodeAnalysis.Completion
Imports Microsoft.CodeAnalysis.Editor.CommandHandlers
Imports Microsoft.CodeAnalysis.Editor.CSharp.CompleteStatement
Imports Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.AsyncCompletion
Imports Microsoft.CodeAnalysis.Editor.[Shared].Utilities
Imports Microsoft.CodeAnalysis.Editor.UnitTests.Extensions
Imports Microsoft.CodeAnalysis.Editor.UnitTests.Utilities
Imports Microsoft.CodeAnalysis.Formatting
Imports Microsoft.CodeAnalysis.LanguageService
Imports Microsoft.CodeAnalysis.Shared.TestHooks
Imports Microsoft.CodeAnalysis.SignatureHelp
Imports Microsoft.CodeAnalysis.Test.Utilities
Imports Microsoft.VisualStudio.Commanding
Imports Microsoft.VisualStudio.Language.Intellisense
Imports Microsoft.VisualStudio.Language.Intellisense.AsyncCompletion
Imports Microsoft.VisualStudio.Text
Imports Microsoft.VisualStudio.Text.Editor
Imports Microsoft.VisualStudio.Text.Editor.Commanding.Commands
Imports Roslyn.Utilities
 
Namespace Microsoft.CodeAnalysis.Editor.UnitTests.IntelliSense
    Friend Class TestState
        Inherits AbstractCommandHandlerTestState
 
        Private Const timeoutMs = 60000
        Friend ReadOnly EditorCompletionCommandHandler As ICommandHandler
        Friend ReadOnly CompletionPresenterProvider As ICompletionPresenterProvider
 
        Protected ReadOnly SessionTestState As IIntelliSenseTestState
        Private ReadOnly SignatureHelpBeforeCompletionCommandHandler As SignatureHelpBeforeCompletionCommandHandler
        Protected ReadOnly SignatureHelpAfterCompletionCommandHandler As SignatureHelpAfterCompletionCommandHandler
        Protected ReadOnly CompleteStatementCommandHandler As CompleteStatementCommandHandler
        Private ReadOnly FormatCommandHandler As FormatCommandHandler
 
        Public Shared ReadOnly CompositionWithoutCompletionTestParts As TestComposition = EditorTestCompositions.EditorFeaturesWpf.
            AddExcludedPartTypes(
                GetType(IIntelliSensePresenter(Of ISignatureHelpPresenterSession, ISignatureHelpSession)),
                GetType(FormatCommandHandler)).
            AddParts(
                GetType(TestSignatureHelpPresenter),
                GetType(IntelliSenseTestState),
                GetType(MockCompletionPresenterProvider))
 
        Friend ReadOnly Property CurrentSignatureHelpPresenterSession As TestSignatureHelpPresenterSession
            Get
                Return SessionTestState.CurrentSignatureHelpPresenterSession
            End Get
        End Property
 
        ' Do not call directly. Use TestStateFactory
        Friend Sub New(workspaceElement As XElement,
                       excludedTypes As IEnumerable(Of Type),
                       extraExportedTypes As IEnumerable(Of Type),
                       includeFormatCommandHandler As Boolean,
                       workspaceKind As String,
                       Optional makeSeparateBufferForCursor As Boolean = False,
                       Optional roles As ImmutableArray(Of String) = Nothing)
            MyBase.New(workspaceElement, GetComposition(excludedTypes, extraExportedTypes, includeFormatCommandHandler), workspaceKind:=workspaceKind, makeSeparateBufferForCursor, roles)
 
            ' Disable editor's responsive completion option to ensure a deterministic test behavior
            MyBase.TextView.Options.GlobalOptions.SetOptionValue(DefaultOptions.ResponsiveCompletionOptionId, False)
            MyBase.TextView.Options.GlobalOptions.SetOptionValue(DefaultOptions.IndentStyleId, IndentingStyle.Smart)
 
            Dim language = Me.Workspace.CurrentSolution.Projects.First().Language
 
            Me.SessionTestState = GetExportedValue(Of IIntelliSenseTestState)()
 
            Me.SignatureHelpBeforeCompletionCommandHandler = GetExportedValue(Of SignatureHelpBeforeCompletionCommandHandler)()
 
            Me.SignatureHelpAfterCompletionCommandHandler = GetExportedValue(Of SignatureHelpAfterCompletionCommandHandler)()
            Me.CompleteStatementCommandHandler = GetExportedValue(Of CompleteStatementCommandHandler)()
 
            Me.FormatCommandHandler = If(includeFormatCommandHandler, GetExportedValue(Of FormatCommandHandler)(), Nothing)
            Me.CompleteStatementCommandHandler = Workspace.ExportProvider.GetCommandHandler(Of CompleteStatementCommandHandler)(NameOf(CompleteStatementCommandHandler))
 
            CompletionPresenterProvider = GetExportedValues(Of ICompletionPresenterProvider)().
                Single(Function(e As ICompletionPresenterProvider) e.GetType().FullName = "Microsoft.CodeAnalysis.Editor.UnitTests.IntelliSense.MockCompletionPresenterProvider")
            EditorCompletionCommandHandler = GetExportedValues(Of ICommandHandler)().
                Single(Function(e As ICommandHandler) e.GetType().Name = PredefinedCompletionNames.CompletionCommandHandler)
        End Sub
 
        Private Overloads Shared Function GetComposition(
            excludedTypes As IEnumerable(Of Type),
            extraExportedTypes As IEnumerable(Of Type),
            includeFormatCommandHandler As Boolean) As TestComposition
 
            Dim composition = CompositionWithoutCompletionTestParts.
                AddExcludedPartTypes(excludedTypes).
                AddParts(extraExportedTypes)
 
            If includeFormatCommandHandler Then
                ' FormatCommandHandler would generally be included in the catalog, but is excluded from tests by adding
                ' it to the list of excluded part types. Here we validate the input state and restore the default
                ' behavior of the catalog by removing FormatCommandHandler from the excluded parts list.
                Assert.Contains(GetType(FormatCommandHandler).Assembly, composition.Assemblies)
                Assert.Contains(GetType(FormatCommandHandler), composition.ExcludedPartTypes)
                composition = composition.RemoveExcludedPartTypes(GetType(FormatCommandHandler))
            End If
 
            Return composition
        End Function
 
#Region "Editor Related Operations"
 
        Protected Overloads Sub ExecuteTypeCharCommand(args As TypeCharCommandArgs, finalHandler As Action, context As CommandExecutionContext, completionCommandHandler As IChainedCommandHandler(Of TypeCharCommandArgs))
            Dim sigHelpHandler = DirectCast(SignatureHelpBeforeCompletionCommandHandler, IChainedCommandHandler(Of TypeCharCommandArgs))
            Dim formatHandler = DirectCast(FormatCommandHandler, IChainedCommandHandler(Of TypeCharCommandArgs))
 
            If formatHandler Is Nothing Then
                sigHelpHandler.ExecuteCommand(
                    args, Sub() completionCommandHandler.ExecuteCommand(
                                    args, Sub() CompleteStatementCommandHandler.ExecuteCommand(args, finalHandler, context), context), context)
            Else
                formatHandler.ExecuteCommand(
                    args, Sub() sigHelpHandler.ExecuteCommand(
                                    args, Sub() completionCommandHandler.ExecuteCommand(
                                                    args, Sub() CompleteStatementCommandHandler.ExecuteCommand(args, finalHandler, context), context), context), context)
            End If
        End Sub
 
        Public Overloads Sub SendTab()
            Dim handler = GetHandler(Of IChainedCommandHandler(Of TabKeyCommandArgs))()
            MyBase.SendTab(Sub(a, n, c) handler.ExecuteCommand(a, n, c), Sub() EditorOperations.InsertText(vbTab))
        End Sub
 
        Public Overloads Sub SendReturn()
            Dim handler = GetHandler(Of IChainedCommandHandler(Of ReturnKeyCommandArgs))()
            MyBase.SendReturn(Sub(a, n, c) handler.ExecuteCommand(a, n, c), Sub() EditorOperations.InsertNewLine())
        End Sub
 
        Public Sub SendBackspaces(count As Integer)
            For i = 0 To count - 1
                Me.SendBackspace()
            Next
        End Sub
 
        Public Overrides Sub SendBackspace()
            Dim compHandler = GetHandler(Of IChainedCommandHandler(Of BackspaceKeyCommandArgs))()
            MyBase.SendBackspace(Sub(a, n, c) compHandler.ExecuteCommand(a, n, c), AddressOf MyBase.SendBackspace)
        End Sub
 
        Public Overrides Sub SendDelete()
            Dim compHandler = GetHandler(Of IChainedCommandHandler(Of DeleteKeyCommandArgs))()
            MyBase.SendDelete(Sub(a, n, c) compHandler.ExecuteCommand(a, n, c), AddressOf MyBase.SendDelete)
        End Sub
 
        Public Sub SendDeleteToSpecificViewAndBuffer(view As IWpfTextView, buffer As ITextBuffer)
            Dim compHandler = GetHandler(Of IChainedCommandHandler(Of DeleteKeyCommandArgs))()
            compHandler.ExecuteCommand(New DeleteKeyCommandArgs(view, buffer), AddressOf MyBase.SendDelete, TestCommandExecutionContext.Create())
        End Sub
 
        Private Overloads Sub ExecuteTypeCharCommand(args As TypeCharCommandArgs, finalHandler As Action, context As CommandExecutionContext)
            Dim compHandler = GetHandler(Of IChainedCommandHandler(Of TypeCharCommandArgs))()
            ExecuteTypeCharCommand(args, finalHandler, context, compHandler)
        End Sub
 
        Public Overloads Sub SendTypeChars(typeChars As String)
            MyBase.SendTypeChars(typeChars, Sub(a, n, c) ExecuteTypeCharCommand(a, n, c))
        End Sub
 
        Public Async Function SendTypeCharsAndWaitForUiRenderAsync(typeChars As String) As Task
            Dim uiRender = WaitForUIRenderedAsync()
            SendTypeChars(typeChars)
            Await uiRender
        End Function
 
        Public Overloads Sub SendEscape()
            MyBase.SendEscape(Sub(a, n, c) EditorCompletionCommandHandler.ExecuteCommand(a, Sub() SignatureHelpAfterCompletionCommandHandler.ExecuteCommand(a, n, c), c), Sub() Return)
        End Sub
 
        Public Overloads Sub SendDownKey()
            MyBase.SendDownKey(
                Sub(a, n, c) EditorCompletionCommandHandler.ExecuteCommand(a, Sub() SignatureHelpAfterCompletionCommandHandler.ExecuteCommand(a, n, c), c),
                Sub()
                    EditorOperations.MoveLineDown(extendSelection:=False)
                End Sub)
        End Sub
 
        Public Overloads Sub SendUpKey()
            MyBase.SendUpKey(
                Sub(a, n, c) EditorCompletionCommandHandler.ExecuteCommand(a, Sub() SignatureHelpAfterCompletionCommandHandler.ExecuteCommand(a, n, c), c),
                Sub()
                    EditorOperations.MoveLineUp(extendSelection:=False)
                End Sub)
        End Sub
 
        Public Overloads Sub SendPageUp()
            Dim handler = DirectCast(EditorCompletionCommandHandler, ICommandHandler(Of PageUpKeyCommandArgs))
            MyBase.SendPageUp(Sub(a, n, c) EditorCompletionCommandHandler.ExecuteCommand(a, n, c), Sub() Return)
        End Sub
 
        Public Overloads Sub SendCut()
            MyBase.SendCut(Sub(a, n, c) EditorCompletionCommandHandler.ExecuteCommand(a, n, c), Sub() Return)
        End Sub
 
        Public Overloads Sub SendPaste()
            MyBase.SendPaste(Sub(a, n, c) EditorCompletionCommandHandler.ExecuteCommand(a, n, c), Sub() Return)
        End Sub
 
        Public Overloads Sub SendInvokeCompletionList()
            MyBase.SendInvokeCompletionList(Sub(a, n, c) EditorCompletionCommandHandler.ExecuteCommand(a, n, c), Sub() Return)
        End Sub
 
        Public Async Function SendInvokeCompletionListAndWaitForUiRenderAsync() As Task
            Dim uiRender = WaitForUIRenderedAsync()
            MyBase.SendInvokeCompletionList(Sub(a, n, c) EditorCompletionCommandHandler.ExecuteCommand(a, n, c), Sub() Return)
            Await uiRender
        End Function
 
        Public Overloads Sub SendInsertSnippetCommand()
            MyBase.SendInsertSnippetCommand(Sub(a, n, c) EditorCompletionCommandHandler.ExecuteCommand(a, n, c), Sub() Return)
        End Sub
 
        Public Overloads Sub SendSurroundWithCommand()
            MyBase.SendSurroundWithCommand(Sub(a, n, c) EditorCompletionCommandHandler.ExecuteCommand(a, n, c), Sub() Return)
        End Sub
 
        Public Overloads Sub SendSave()
            MyBase.SendSave(Sub(a, n, c) EditorCompletionCommandHandler.ExecuteCommand(a, n, c), Sub() Return)
        End Sub
 
        Public Overloads Sub SendSelectAll()
 
            MyBase.SendSelectAll(Sub(a, n, c) EditorCompletionCommandHandler.ExecuteCommand(a, n, c), Sub() Return)
        End Sub
 
        Public Overrides Sub SendDeleteWordToLeft()
            Dim compHandler = DirectCast(EditorCompletionCommandHandler, ICommandHandler(Of WordDeleteToStartCommandArgs))
            MyBase.SendWordDeleteToStart(Sub(a, n, c) compHandler.ExecuteCommand(a, n, c), AddressOf MyBase.SendDeleteWordToLeft)
        End Sub
 
        Public Overloads Sub SendToggleCompletionMode()
            Dim handler = DirectCast(EditorCompletionCommandHandler, ICommandHandler(Of ToggleCompletionModeCommandArgs))
            MyBase.SendToggleCompletionMode(Sub(a, n, c) handler.ExecuteCommand(a, n, c), Sub() Return)
        End Sub
 
        Protected Function GetHandler(Of T As ICommandHandler)() As T
            Return DirectCast(EditorCompletionCommandHandler, T)
        End Function
 
#End Region
 
#Region "Completion Operations"
 
        Public Async Function SendCommitUniqueCompletionListItemAsync() As Task
            Await WaitForAsynchronousOperationsAsync()
 
            ' When we send the commit completion list item, it processes asynchronously; we can find out when it's complete
            ' by seeing that either the items are updated or the list is dismissed. We'll use a TaskCompletionSource to track
            ' when it's done which will release an async token.
            Dim sessionComplete = New TaskCompletionSource(Of Object)()
            Dim asynchronousOperationListenerProvider = Workspace.ExportProvider.GetExportedValue(Of AsynchronousOperationListenerProvider)()
            Dim asyncToken = asynchronousOperationListenerProvider.GetListener(FeatureAttribute.CompletionSet) _
                .BeginAsyncOperation(NameOf(SendCommitUniqueCompletionListItemAsync))
 
#Disable Warning BC42358 ' Because this call is not awaited, execution of the current method continues before the call is completed
            sessionComplete.Task.CompletesAsyncOperation(asyncToken)
            Dim waitingForUI = WaitForUIRenderedAsync()
#Enable Warning BC42358 ' Because this call is not awaited, execution of the current method continues before the call is completed
 
            Dim itemsUpdatedHandler = Sub(sender As Object, e As Data.ComputedCompletionItemsEventArgs)
                                          ' If there is 0 or more than one item left, then it means this was the filter operation that resulted and we're done. 
                                          ' Otherwise we know a Dismiss operation is coming so we should wait for it.
                                          If e.Items.Items.Count() <> 1 Then
                                              Dim threadingContext = Workspace.ExportProvider.GetExportedValue(Of IThreadingContext)()
 
                                              ' Set up a timeout path to make sure tests don't deadlock
                                              Dim asyncToken2 = asynchronousOperationListenerProvider.GetListener(FeatureAttribute.CompletionSet) _
                                                  .BeginAsyncOperation(NameOf(SendCommitUniqueCompletionListItemAsync))
                                              Task.Run(
                                                  Async Function() As Task
                                                      Using cancellationSource = CancellationTokenSource.CreateLinkedTokenSource(threadingContext.DisposalToken)
                                                          Await Task.WhenAny(sessionComplete.Task, Task.Delay(TimeSpan.FromSeconds(2), cancellationSource.Token))
                                                          If sessionComplete.TrySetException(New TimeoutException()) Then
                                                              Throw New TimeoutException()
                                                          End If
                                                      End Using
                                                  End Function).CompletesAsyncOperation(asyncToken2).ReportNonFatalErrorUnlessCancelledAsync(threadingContext.DisposalToken)
 
                                              ' Now set up the expected path of just waiting for the UI to complete rendering
                                              threadingContext.JoinableTaskFactory.RunAsync(
                                                  Async Function() As Task
                                                      Await waitingForUI
                                                      sessionComplete.SetResult(Nothing)
                                                  End Function)
                                          End If
                                      End Sub
 
            Dim sessionDismissedHandler = Sub(sender As Object, e As EventArgs) sessionComplete.TrySetResult(Nothing)
 
            Dim session As IAsyncCompletionSession
 
            Dim addHandlers = Sub(sender As Object, e As Data.CompletionTriggeredEventArgs)
                                  AddHandler e.CompletionSession.ItemsUpdated, itemsUpdatedHandler
                                  AddHandler e.CompletionSession.Dismissed, sessionDismissedHandler
                                  session = e.CompletionSession
                              End Sub
 
            Dim asyncCompletionBroker As IAsyncCompletionBroker = GetExportedValue(Of IAsyncCompletionBroker)()
            session = asyncCompletionBroker.GetSession(TextView)
            If session Is Nothing Then
                AddHandler asyncCompletionBroker.CompletionTriggered, addHandlers
            Else
                ' A session was already active so we'll fake the event
                addHandlers(asyncCompletionBroker, New Data.CompletionTriggeredEventArgs(session, TextView))
            End If
 
            MyBase.SendCommitUniqueCompletionListItem(Sub(a, n, c) EditorCompletionCommandHandler.ExecuteCommand(a, n, c), Sub() Return)
 
            Await WaitForAsynchronousOperationsAsync()
 
            RemoveHandler session.ItemsUpdated, itemsUpdatedHandler
            RemoveHandler session.Dismissed, sessionDismissedHandler
            RemoveHandler asyncCompletionBroker.CompletionTriggered, addHandlers
 
            ' It's possible for the wait to bail and give up if it was clear nothing was completing; ensure we clean up our
            ' async token so as not to interfere with later tests.
            sessionComplete.TrySetResult(Nothing)
        End Function
 
        Public Async Function AssertNoCompletionSession() As Task
            Await WaitForAsynchronousOperationsAsync()
            Dim session = GetExportedValue(Of IAsyncCompletionBroker)().GetSession(TextView)
            If session Is Nothing Then
                Return
            End If
 
            If session.IsDismissed Then
                Return
            End If
 
            Dim completionItems = session.GetComputedItems(CancellationToken.None)
 
            ' During the computation we can explicitly dismiss the session or we can return no items.
            ' Each of these conditions mean that there is no active completion.
            Assert.True(session.IsDismissed OrElse completionItems.Items.Count() = 0, "AssertNoCompletionSession")
        End Function
 
        Public Sub AssertNoCompletionSessionWithNoBlock()
            Dim session = GetExportedValue(Of IAsyncCompletionBroker)().GetSession(TextView)
            If session Is Nothing Then
                Return
            End If
 
            If session.IsDismissed Then
                Return
            End If
 
            ' If completionItems cannot be calculated in 5 seconds, no session exists.
            Dim task1 = Task.Delay(5000)
            Dim task2 = Task.Run(
                Sub()
                    Dim completionItems = session.GetComputedItems(CancellationToken.None)
 
                    ' In the non blocking mode, we are not interested for a session appeared later than in 5 seconds.
                    If task1.Status = TaskStatus.Running Then
                        ' During the computation we can explicitly dismiss the session or we can return no items.
                        ' Each of these conditions mean that there is no active completion.
                        Assert.True(session.IsDismissed OrElse completionItems.Items.Count() = 0)
                    End If
                End Sub)
 
            Task.WaitAny(task1, task2)
        End Sub
 
        Public Async Function GetCompletionSession(Optional projectionsView As ITextView = Nothing) As Task(Of IAsyncCompletionSession)
            Await WaitForAsynchronousOperationsAsync()
            Dim view = If(projectionsView, TextView)
 
            Return GetExportedValue(Of IAsyncCompletionBroker)().GetSession(view)
        End Function
 
        Public Async Function AssertCompletionSession(Optional projectionsView As ITextView = Nothing) As Task
            Await WaitForAsynchronousOperationsAsync()
            Dim view = If(projectionsView, TextView)
 
            Dim session = GetExportedValue(Of IAsyncCompletionBroker)().GetSession(view)
            Assert.NotNull(session)
        End Function
 
        Public Async Function AssertCompletionItemsDoNotContainAny(ParamArray displayText As String()) As Task
            Await WaitForAsynchronousOperationsAsync()
            Dim items = GetCompletionItems()
            Assert.False(displayText.Any(Function(v) items.Any(Function(i) i.DisplayText = v)))
        End Function
 
        Public Async Function AssertCompletionItemsContainAll(ParamArray displayText As String()) As Task
            Await WaitForAsynchronousOperationsAsync()
            Dim items = GetCompletionItems()
            Assert.All(displayText, Sub(v) Assert.Contains(v, items.Select(Function(i) i.DisplayText)))
        End Function
 
        Public Async Function AssertCompletionItemsContain(displayText As String, displayTextSuffix As String) As Task
            Await AssertCompletionItemsContain(Function(i) i.DisplayText = displayText AndAlso i.DisplayTextSuffix = displayTextSuffix)
        End Function
 
        Public Async Function AssertCompletionItemsContain(predicate As Func(Of CompletionItem, Boolean)) As Task
            Await WaitForAsynchronousOperationsAsync()
            Dim items = GetCompletionItems()
            Assert.True(items.Any(predicate))
        End Function
 
        Public Sub AssertItemsInOrder(expectedOrder As String())
            Dim session = GetExportedValue(Of IAsyncCompletionBroker)().GetSession(TextView)
            Assert.NotNull(session)
            Dim items = session.GetComputedItems(CancellationToken.None).Items
            Assert.Equal(expectedOrder.Count, items.Count)
            For i = 0 To expectedOrder.Count - 1
                Assert.Equal(expectedOrder(i), items(i).DisplayText)
            Next
        End Sub
 
        Public Sub AssertItemsInOrder(expectedOrder As (String, String)())
            Dim session = GetExportedValue(Of IAsyncCompletionBroker)().GetSession(TextView)
            Assert.NotNull(session)
            Dim items = session.GetComputedItems(CancellationToken.None).Items
            Assert.Equal(expectedOrder.Count, items.Count)
            For i = 0 To expectedOrder.Count - 1
                Assert.Equal(expectedOrder(i).Item1, items(i).DisplayText)
                Assert.Equal(expectedOrder(i).Item2, items(i).Suffix)
            Next
        End Sub
 
        Public Async Function AssertSelectedCompletionItem(
                Optional displayText As String = Nothing,
                Optional displayTextSuffix As String = Nothing,
                Optional description As String = Nothing,
                Optional isSoftSelected As Boolean? = Nothing,
                Optional isHardSelected As Boolean? = Nothing,
                Optional shouldFormatOnCommit As Boolean? = Nothing,
                Optional inlineDescription As String = Nothing,
                Optional automationText As String = Nothing,
                Optional projectionsView As ITextView = Nothing) As Task
 
            Await WaitForAsynchronousOperationsAsync()
            Dim view = If(projectionsView, TextView)
 
            Dim session = GetExportedValue(Of IAsyncCompletionBroker)().GetSession(view)
            Assert.NotNull(session)
            Dim items = session.GetComputedItems(CancellationToken.None)
 
            If isSoftSelected.HasValue Then
                If isSoftSelected.Value Then
                    Assert.True(items.UsesSoftSelection, "Current completion is not soft-selected. Expected: soft-selected")
                Else
                    Assert.False(items.UsesSoftSelection, "Current completion is soft-selected. Expected: not soft-selected")
                End If
            End If
 
            If isHardSelected.HasValue Then
                If isHardSelected.Value Then
                    Assert.True(Not items.UsesSoftSelection, "Current completion is not hard-selected. Expected: hard-selected")
                Else
                    Assert.True(items.UsesSoftSelection, "Current completion is hard-selected. Expected: not hard-selected")
                End If
            End If
 
            If displayText IsNot Nothing Then
                Assert.NotNull(items.SelectedItem)
                If displayTextSuffix IsNot Nothing Then
                    Assert.NotNull(items.SelectedItem)
                    Assert.Equal(displayText + displayTextSuffix, items.SelectedItem.DisplayText)
                Else
                    Assert.Equal(displayText, items.SelectedItem.DisplayText)
                End If
            End If
 
            If shouldFormatOnCommit.HasValue Then
                Assert.Equal(shouldFormatOnCommit.Value, GetRoslynCompletionItem(items.SelectedItem).Rules.FormatOnCommit)
            End If
 
            If description IsNot Nothing Then
                Dim itemDescription = Await GetSelectedItemDescriptionAsync()
                Assert.Equal(description, itemDescription.Text)
            End If
 
            If inlineDescription IsNot Nothing Then
                Assert.Equal(inlineDescription, items.SelectedItem.Suffix)
            End If
 
            If automationText IsNot Nothing Then
                Assert.Equal(automationText, items.SelectedItem.AutomationText)
            End If
        End Function
 
        Public Async Function GetSelectedItemDescriptionAsync() As Task(Of CompletionDescription)
            Dim document = Me.Workspace.CurrentSolution.Projects.First().Documents.First()
            Dim service = CompletionService.GetService(document)
            Dim roslynItem = GetSelectedItem()
            Dim options = CompletionOptions.Default
            Return Await service.GetDescriptionAsync(document, roslynItem, options, SymbolDescriptionOptions.Default)
        End Function
 
        Public Sub AssertCompletionItemExpander(isAvailable As Boolean, isSelected As Boolean)
            Dim presenter = DirectCast(CompletionPresenterProvider.GetOrCreate(Me.TextView), MockCompletionPresenter)
            Dim expander = presenter.GetExpander()
            If Not isAvailable Then
                Assert.False(isSelected)
                Assert.Null(expander)
            Else
                Assert.NotNull(expander)
                Assert.Equal(expander.IsSelected, isSelected)
            End If
        End Sub
 
        Public Async Function SetCompletionItemExpanderStateAndWaitForUiRenderAsync(isSelected As Boolean) As Task
            Dim uiRender = WaitForUIRenderedAsync()
            Dim presenter = DirectCast(CompletionPresenterProvider.GetOrCreate(Me.TextView), MockCompletionPresenter)
            Dim expander = presenter.GetExpander()
            Assert.NotNull(expander)
            presenter.SetExpander(isSelected)
            Await uiRender
        End Function
 
        Public Async Function AssertSessionIsNothingOrNoCompletionItemLike(text As String) As Task
            Await WaitForAsynchronousOperationsAsync()
            Dim session = GetExportedValue(Of IAsyncCompletionBroker)().GetSession(TextView)
            If Not session Is Nothing Then
                Await AssertCompletionItemsDoNotContainAny(text)
            End If
        End Function
 
        Public Function GetSelectedItem() As CompletionItem
            Dim session = GetExportedValue(Of IAsyncCompletionBroker)().GetSession(TextView)
            Assert.NotNull(session)
            Dim items = session.GetComputedItems(CancellationToken.None)
            Return GetRoslynCompletionItem(items.SelectedItem)
        End Function
 
        Public Sub CalculateItemsIfSessionExists()
            Dim session = GetExportedValue(Of IAsyncCompletionBroker)().GetSession(TextView)
            If session IsNot Nothing Then
                Dim item = session.GetComputedItems(CancellationToken.None).SelectedItem
            End If
        End Sub
 
        Public Function GetCompletionItems() As IList(Of CompletionItem)
            Dim session = GetExportedValue(Of IAsyncCompletionBroker)().GetSession(TextView)
            Assert.NotNull(session)
            Return session.GetComputedItems(CancellationToken.None).Items.Select(Function(item) GetRoslynCompletionItem(item)).ToList()
        End Function
 
        Private Shared Function GetRoslynCompletionItem(item As Data.CompletionItem) As CompletionItem
            If (item Is Nothing) Then
                Return Nothing
            End If
 
            Dim roslynItemData As CompletionItemData = Nothing
            If (CompletionItemData.TryGetData(item, roslynItemData) = False) Then
                Return Nothing
            End If
            Return roslynItemData.RoslynItem
        End Function
 
        Public Async Function RaiseFiltersChangedAndWaitForUiRenderAsync(args As ImmutableArray(Of Data.CompletionFilterWithState)) As Task
            Dim uiRender = WaitForUIRenderedAsync()
            Dim presenter = DirectCast(CompletionPresenterProvider.GetOrCreate(Me.TextView), MockCompletionPresenter)
            Dim newArgs = New Data.CompletionFilterChangedEventArgs(args)
            presenter.TriggerFiltersChanged(Me, newArgs)
            Await uiRender
        End Function
 
        Public Function GetCompletionItemFilters() As ImmutableArray(Of Data.CompletionFilterWithState)
            Dim presenter = DirectCast(CompletionPresenterProvider.GetOrCreate(Me.TextView), MockCompletionPresenter)
            Return presenter.GetFilters()
        End Function
 
        Public Function HasSuggestedItem() As Boolean
            Dim session = GetExportedValue(Of IAsyncCompletionBroker)().GetSession(TextView)
            Assert.NotNull(session)
            Dim computedItems = session.GetComputedItems(CancellationToken.None)
            Return computedItems.SuggestionItem IsNot Nothing
        End Function
 
        Public Sub AssertSuggestedItemSelected(displayText As String)
            Dim session = GetExportedValue(Of IAsyncCompletionBroker)().GetSession(TextView)
            Assert.NotNull(session)
            Dim computedItems = session.GetComputedItems(CancellationToken.None)
            Assert.True(computedItems.SuggestionItemSelected)
            Assert.Equal(computedItems.SuggestionItem.DisplayText, displayText)
        End Sub
 
        Public Function IsSoftSelected() As Boolean
            Dim session = GetExportedValue(Of IAsyncCompletionBroker)().GetSession(TextView)
            Assert.NotNull(session)
            Dim computedItems = session.GetComputedItems(CancellationToken.None)
            Return computedItems.UsesSoftSelection
        End Function
 
        Public Sub SendSelectCompletionItem(displayText As String)
            Dim session = GetExportedValue(Of IAsyncCompletionBroker)().GetSession(TextView)
            Dim operations = DirectCast(session, IAsyncCompletionSessionOperations)
            operations.SelectCompletionItem(session.GetComputedItems(CancellationToken.None).Items.Single(Function(i) i.DisplayText = displayText))
        End Sub
 
        Public Async Function WaitForUIRenderedAsync() As Task
            Dim tcs = New TaskCompletionSource(Of Boolean)
            Dim presenter = DirectCast(CompletionPresenterProvider.GetOrCreate(TextView), MockCompletionPresenter)
            Dim uiUpdated As EventHandler(Of Data.CompletionItemSelectedEventArgs)
 
            uiUpdated = Sub()
                            RemoveHandler presenter.UiUpdated, uiUpdated
                            tcs.TrySetResult(True)
                        End Sub
 
            AddHandler presenter.UiUpdated, uiUpdated
            Dim ct = New CancellationTokenSource(timeoutMs)
            Using registration = ct.Token.Register(Sub() tcs.TrySetCanceled(), useSynchronizationContext:=False)
                Await tcs.Task.ConfigureAwait(True)
            End Using
        End Function
 
        Public Overloads Sub SendTypeCharsToSpecificViewAndBuffer(typeChars As String, view As IWpfTextView, buffer As ITextBuffer)
            For Each ch In typeChars
                Dim localCh = ch
                ExecuteTypeCharCommand(New TypeCharCommandArgs(view, buffer, localCh), Sub() EditorOperations.InsertText(localCh.ToString()), TestCommandExecutionContext.Create())
            Next
        End Sub
 
        Public Async Function AssertLineTextAroundCaret(expectedTextBeforeCaret As String, expectedTextAfterCaret As String) As Task
            Await WaitForAsynchronousOperationsAsync()
 
            Dim actual = GetLineTextAroundCaretPosition()
 
            Assert.Equal(expectedTextBeforeCaret, actual.TextBeforeCaret)
            Assert.Equal(expectedTextAfterCaret, actual.TextAfterCaret)
        End Function
 
        Public Sub NavigateToDisplayText(targetText As String)
            Dim currentText = GetSelectedItem().DisplayText
 
            ' GetComputedItems provided by the Editor for tests does not guarantee that 
            ' the order of items match the order of items actually displayed in the completion popup.
            ' For example, they put starred items (intellicode) below non-starred ones.
            ' And the order they display those items in the UI is opposite.
            ' Therefore, we do the full traverse: down to the bottom and if not found up to the top.
            Do While currentText <> targetText
                SendDownKey()
                Dim newText = GetSelectedItem().DisplayText
                If currentText = newText Then
                    ' Nothing found on going down. Try going up
                    Do While currentText <> targetText
                        SendUpKey()
                        newText = GetSelectedItem().DisplayText
                        Assert.True(newText <> currentText, "Reached the bottom, then the top and didn't find the match")
                        currentText = newText
                    Loop
                End If
 
                currentText = newText
            Loop
        End Sub
 
#End Region
 
#Region "Signature Help Operations"
 
        Public Overloads Sub SendInvokeSignatureHelp()
            Dim handler = DirectCast(SignatureHelpBeforeCompletionCommandHandler, IChainedCommandHandler(Of InvokeSignatureHelpCommandArgs))
            MyBase.SendInvokeSignatureHelp(Sub(a, n, c) handler.ExecuteCommand(a, n, c), Sub() Return)
        End Sub
 
        Public Overloads Async Function AssertNoSignatureHelpSession(Optional block As Boolean = True) As Task
            If block Then
                Await WaitForAsynchronousOperationsAsync()
            End If
 
            Assert.Null(Me.CurrentSignatureHelpPresenterSession)
        End Function
 
        Public Overloads Async Function AssertSignatureHelpSession() As Task
            Await WaitForAsynchronousOperationsAsync()
            Assert.NotNull(Me.CurrentSignatureHelpPresenterSession)
        End Function
 
        Public Overloads Function GetSignatureHelpItems() As IList(Of SignatureHelpItem)
            Return CurrentSignatureHelpPresenterSession.SignatureHelpItems
        End Function
 
        Public Async Function AssertSignatureHelpItemsContainAll(displayText As String()) As Task
            Await WaitForAsynchronousOperationsAsync()
            Assert.True(displayText.All(Function(v) CurrentSignatureHelpPresenterSession.SignatureHelpItems.Any(
                                            Function(i) GetDisplayText(i, CurrentSignatureHelpPresenterSession.SelectedParameter.Value) = v)))
        End Function
 
        Public Async Function AssertSelectedSignatureHelpItem(Optional displayText As String = Nothing,
                               Optional documentation As String = Nothing,
                               Optional selectedParameter As String = Nothing) As Task
            Await WaitForAsynchronousOperationsAsync()
 
            If displayText IsNot Nothing Then
                Assert.Equal(displayText, GetDisplayText(Me.CurrentSignatureHelpPresenterSession.SelectedItem, Me.CurrentSignatureHelpPresenterSession.SelectedParameter.Value))
            End If
 
            If documentation IsNot Nothing Then
                Assert.Equal(documentation, Me.CurrentSignatureHelpPresenterSession.SelectedItem.DocumentationFactory(CancellationToken.None).GetFullText())
            End If
 
            If selectedParameter IsNot Nothing Then
                Assert.Equal(selectedParameter, GetDisplayText(
                    Me.CurrentSignatureHelpPresenterSession.SelectedItem.Parameters(
                        Me.CurrentSignatureHelpPresenterSession.SelectedParameter.Value).DisplayParts))
            End If
        End Function
#End Region
 
#Region "Helpers"
 
        Private Shared Function GetDisplayText(item As SignatureHelpItem, selectedParameter As Integer) As String
            Dim suffix = If(selectedParameter < item.Parameters.Count,
                            GetDisplayText(item.Parameters(selectedParameter).SuffixDisplayParts),
                            String.Empty)
            Return String.Join(
                String.Empty,
                GetDisplayText(item.PrefixDisplayParts),
                String.Join(
                    GetDisplayText(item.SeparatorDisplayParts),
                    item.Parameters.Select(Function(p) GetDisplayText(p.DisplayParts))),
                GetDisplayText(item.SuffixDisplayParts),
                suffix)
        End Function
 
        Private Shared Function GetDisplayText(parts As IEnumerable(Of TaggedText)) As String
            Return String.Join(String.Empty, parts.Select(Function(p) p.ToString()))
        End Function
 
#End Region
 
    End Class
End Namespace