File: RefactoringHelpers\RefactoringHelpersTestBase.cs
Web Access
Project: src\src\EditorFeatures\TestUtilities\Microsoft.CodeAnalysis.EditorFeatures.Test.Utilities.csproj (Microsoft.CodeAnalysis.EditorFeatures.Test.Utilities)
// 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;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeRefactorings;
using Microsoft.CodeAnalysis.Test.Utilities;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Test.Utilities;
using Roslyn.Utilities;
using Xunit;
 
namespace Microsoft.CodeAnalysis.Editor.UnitTests.RefactoringHelpers;
 
[UseExportProvider]
public abstract class RefactoringHelpersTestBase<TWorkspaceFixture> : TestBase
    where TWorkspaceFixture : TestWorkspaceFixture, new()
{
    private readonly TestFixtureHelper<TWorkspaceFixture> _fixtureHelper = new();
 
    private protected ReferenceCountedDisposable<TWorkspaceFixture> GetOrCreateWorkspaceFixture()
        => _fixtureHelper.GetOrCreateFixture();
 
    protected Task TestAsync<TNode>(string text, bool allowEmptyNodes = false) where TNode : SyntaxNode
        => TestAsync(text, Functions<TNode>.True, allowEmptyNodes);
 
    protected async Task TestAsync<TNode>(string text, Func<TNode, bool> predicate, bool allowEmptyNodes = false) where TNode : SyntaxNode
    {
        text = GetSelectionAndResultSpans(text, out var selection, out var result);
        var resultNode = await GetNodeForSelectionAsync(text, selection, predicate, allowEmptyNodes).ConfigureAwait(false);
 
        Assert.NotNull(resultNode);
        Assert.Equal(result, resultNode!.Span);
    }
 
    protected async Task TestUnderselectedAsync<TNode>(string text) where TNode : SyntaxNode
    {
        text = GetSelectionSpan(text, out var selection);
        var resultNode = await GetNodeForSelectionAsync<TNode>(text, selection, Functions<TNode>.True).ConfigureAwait(false);
 
        Assert.NotNull(resultNode);
        Assert.True(CodeRefactoringHelpers.IsNodeUnderselected(resultNode, selection));
    }
 
    protected async Task TestNotUnderselectedAsync<TNode>(string text) where TNode : SyntaxNode
    {
        text = GetSelectionAndResultSpans(text, out var selection, out var result);
        var resultNode = await GetNodeForSelectionAsync<TNode>(text, selection, Functions<TNode>.True).ConfigureAwait(false);
 
        Assert.NotNull(resultNode);
        Assert.Equal(result, resultNode!.Span);
        Assert.False(CodeRefactoringHelpers.IsNodeUnderselected(resultNode, selection));
    }
 
    protected Task TestMissingAsync<TNode>(string text, bool allowEmptyNodes = false) where TNode : SyntaxNode
        => TestMissingAsync(text, Functions<TNode>.True, allowEmptyNodes);
 
    protected async Task TestMissingAsync<TNode>(string text, Func<TNode, bool> predicate, bool allowEmptyNodes = false) where TNode : SyntaxNode
    {
        text = GetSelectionSpan(text, out var selection);
 
        var resultNode = await GetNodeForSelectionAsync<TNode>(text, selection, predicate, allowEmptyNodes).ConfigureAwait(false);
        Assert.Null(resultNode);
    }
 
    private static string GetSelectionSpan(string text, out TextSpan selection)
    {
        MarkupTestFile.GetSpans(text, out text, out IDictionary<string, ImmutableArray<TextSpan>> spans);
 
        if (spans.Count != 1 ||
            !spans.TryGetValue(string.Empty, out var selections) || selections.Length != 1)
        {
            throw new ArgumentException("Invalid missing test format: only `[|...|]` (selection) should be present.");
        }
 
        selection = selections.Single();
        return text;
    }
 
    private static string GetSelectionAndResultSpans(string text, out TextSpan selection, out TextSpan result)
    {
        MarkupTestFile.GetSpans(text, out text, out IDictionary<string, ImmutableArray<TextSpan>> spans);
 
        if (spans.Count != 2 ||
            !spans.TryGetValue(string.Empty, out var selections) || selections.Length != 1 ||
            !spans.TryGetValue("result", out var results) || results.Length != 1)
        {
            throw new ArgumentException("Invalid test format: both `[|...|]` (selection) and `{|result:...|}` (retrieved node span) selections are required for a test.");
        }
 
        selection = selections.Single();
        result = results.Single();
 
        return text;
    }
 
    private async Task<TNode?> GetNodeForSelectionAsync<TNode>(string text, TextSpan selection, Func<TNode, bool> predicate, bool allowEmptyNodes = false) where TNode : SyntaxNode
    {
        using var workspaceFixture = GetOrCreateWorkspaceFixture();
 
        var document = workspaceFixture.Target.UpdateDocument(text, SourceCodeKind.Regular);
        var relevantNodes = await document.GetRelevantNodesAsync<TNode>(selection, allowEmptyNodes, CancellationToken.None).ConfigureAwait(false);
 
        return relevantNodes.FirstOrDefault(predicate);
    }
}