File: ExtractMethod\ExtractMethodBase.cs
Web Access
Project: src\src\EditorFeatures\CSharpTest\Microsoft.CodeAnalysis.CSharp.EditorFeatures.UnitTests.csproj (Microsoft.CodeAnalysis.CSharp.EditorFeatures.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.
 
#nullable disable
 
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeCleanup;
using Microsoft.CodeAnalysis.CodeGeneration;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.ExtractMethod;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.ExtractMethod;
using Microsoft.CodeAnalysis.Test.Utilities;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Test.Utilities;
using Roslyn.Utilities;
using Xunit;
 
namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.ExtractMethod;
 
[UseExportProvider]
public class ExtractMethodBase
{
    protected static async Task ExpectExtractMethodToFailAsync(string codeWithMarker, string[] features = null)
    {
        ParseOptions parseOptions = null;
        if (features != null)
        {
            var featuresMapped = features.Select(x => KeyValuePairUtil.Create(x, string.Empty));
            parseOptions = new CSharpParseOptions().WithFeatures(featuresMapped);
        }
 
        using var workspace = EditorTestWorkspace.CreateCSharp(codeWithMarker, parseOptions: parseOptions);
        var testDocument = workspace.Documents.First();
        var textSpan = testDocument.SelectedSpans.Single();
        var treeAfterExtractMethod = await ExtractMethodAsync(workspace, testDocument, succeed: false);
    }
 
    protected static async Task ExpectExtractMethodToFailAsync(
        string codeWithMarker,
        string expected,
        CSharpParseOptions parseOptions = null)
    {
        using var workspace = EditorTestWorkspace.CreateCSharp(codeWithMarker, parseOptions: parseOptions);
        var testDocument = workspace.Documents.Single();
        var subjectBuffer = testDocument.GetTextBuffer();
 
        var tree = await ExtractMethodAsync(workspace, testDocument, succeed: false);
 
        using (var edit = subjectBuffer.CreateEdit())
        {
            edit.Replace(0, edit.Snapshot.Length, tree.ToFullString());
            edit.Apply();
        }
 
        if (expected == "")
            Assert.True(false, subjectBuffer.CurrentSnapshot.GetText());
 
        Assert.Equal(expected, subjectBuffer.CurrentSnapshot.GetText());
    }
 
    protected static async Task NotSupported_ExtractMethodAsync(string codeWithMarker)
    {
        using var workspace = EditorTestWorkspace.CreateCSharp(codeWithMarker);
        Assert.NotNull(await Record.ExceptionAsync(async () =>
        {
            var testDocument = workspace.Documents.Single();
            var tree = await ExtractMethodAsync(workspace, testDocument);
        }));
    }
 
    protected static async Task TestExtractMethodAsync(
        string codeWithMarker,
        string expected,
        bool temporaryFailing = false,
        CSharpParseOptions parseOptions = null)
    {
        using var workspace = EditorTestWorkspace.CreateCSharp(codeWithMarker, parseOptions: parseOptions);
        var testDocument = workspace.Documents.Single();
        var subjectBuffer = testDocument.GetTextBuffer();
 
        var tree = await ExtractMethodAsync(
            workspace, testDocument);
 
        using (var edit = subjectBuffer.CreateEdit())
        {
            edit.Replace(0, edit.Snapshot.Length, tree.ToFullString());
            edit.Apply();
        }
 
        var actual = subjectBuffer.CurrentSnapshot.GetText();
        if (temporaryFailing)
        {
            Assert.NotEqual(expected, actual);
        }
        else
        {
            if (expected != "")
            {
                AssertEx.EqualOrDiff(expected, actual);
            }
            else
            {
                // print out the entire diff to make adding tests simpler.
                Assert.Equal((object)expected, actual);
            }
        }
    }
 
    protected static async Task<SyntaxNode> ExtractMethodAsync(
        EditorTestWorkspace workspace,
        EditorTestHostDocument testDocument,
        bool succeed = true)
    {
        var document = workspace.CurrentSolution.GetDocument(testDocument.Id);
        Assert.NotNull(document);
 
        var options = new ExtractMethodGenerationOptions()
        {
            CodeGenerationOptions = CodeGenerationOptionsProviders.GetDefault(document.Project.Services),
            CodeCleanupOptions = await document.GetCodeCleanupOptionsAsync(CancellationToken.None),
        };
 
        var semanticDocument = await SemanticDocument.CreateAsync(document, CancellationToken.None);
        var validator = new CSharpSelectionValidator(semanticDocument, testDocument.SelectedSpans.Single(), localFunction: false);
 
        var (selectedCode, status) = await validator.GetValidSelectionAsync(CancellationToken.None);
        if (!succeed && status.Failed)
            return null;
 
        Assert.NotNull(selectedCode);
 
        // extract method
        var extractor = new CSharpMethodExtractor(selectedCode, options, localFunction: false);
        var result = extractor.ExtractMethod(status, CancellationToken.None);
        Assert.NotNull(result);
 
        // If the test expects us to succeed, validate that we did.  If it expects us to fail, ensure we either
        // failed or produced a message the user will have to confirm to continue. 
        if (succeed)
        {
            Assert.Equal(succeed, result.Succeeded);
        }
        else
        {
            Assert.True(!result.Succeeded || result.Reasons.Length > 0);
 
            if (!result.Succeeded)
                return null;
        }
 
        var (doc, _) = await result.GetDocumentAsync(CancellationToken.None);
        return doc == null
            ? null
            : await doc.GetSyntaxRootAsync();
    }
 
    protected static async Task TestSelectionAsync(string codeWithMarker, bool expectedFail = false, CSharpParseOptions parseOptions = null, TextSpan? textSpanOverride = null)
    {
        using var workspace = EditorTestWorkspace.CreateCSharp(codeWithMarker, parseOptions: parseOptions);
        var testDocument = workspace.Documents.Single();
        var namedSpans = testDocument.AnnotatedSpans;
 
        var document = workspace.CurrentSolution.GetDocument(testDocument.Id);
        Assert.NotNull(document);
 
        var semanticDocument = await SemanticDocument.CreateAsync(document, CancellationToken.None);
        var validator = new CSharpSelectionValidator(semanticDocument, textSpanOverride ?? namedSpans["b"].Single(), localFunction: false);
        var (result, status) = await validator.GetValidSelectionAsync(CancellationToken.None);
 
        if (expectedFail)
        {
            Assert.True(status.Failed || status.Reasons.Length > 0);
        }
        else
        {
            Assert.True(status.Succeeded);
        }
 
        if (status.Succeeded && result.SelectionChanged)
            Assert.Equal(namedSpans["r"].Single(), result.FinalSpan);
    }
 
    protected static async Task IterateAllAsync(string code)
    {
        using var workspace = EditorTestWorkspace.CreateCSharp(code, CodeAnalysis.CSharp.Test.Utilities.TestOptions.Regular);
        var document = workspace.CurrentSolution.GetDocument(workspace.Documents.First().Id);
        Assert.NotNull(document);
 
        var semanticDocument = await SemanticDocument.CreateAsync(document, CancellationToken.None);
        var root = await document.GetSyntaxRootAsync();
        var iterator = root.DescendantNodesAndSelf().Cast<SyntaxNode>();
 
        foreach (var node in iterator)
        {
            var validator = new CSharpSelectionValidator(semanticDocument, node.Span, localFunction: false);
            var (_, status) = await validator.GetValidSelectionAsync(CancellationToken.None);
 
            // check the obvious case
            if (node is not ExpressionSyntax && !node.UnderValidContext())
                Assert.True(status.Failed);
        }
    }
}