File: Utilities\StackFrameUtils.cs
Web Access
Project: src\src\EditorFeatures\Test\Microsoft.CodeAnalysis.EditorFeatures.UnitTests.csproj (Microsoft.CodeAnalysis.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.
 
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Text;
using Microsoft.CodeAnalysis.EmbeddedLanguages.StackFrame;
using Roslyn.Test.Utilities;
using Xunit;
 
namespace Microsoft.CodeAnalysis.Editor.UnitTests.Utilities;
 
using StackFrameNodeOrToken = CodeAnalysis.EmbeddedLanguages.Common.EmbeddedSyntaxNodeOrToken<StackFrameKind, StackFrameNode>;
using StackFrameToken = CodeAnalysis.EmbeddedLanguages.Common.EmbeddedSyntaxToken<StackFrameKind>;
using StackFrameTrivia = CodeAnalysis.EmbeddedLanguages.Common.EmbeddedSyntaxTrivia<StackFrameKind>;
 
internal static class StackFrameUtils
{
    public static void AssertEqual(StackFrameNodeOrToken expected, StackFrameNodeOrToken actual)
    {
        Assert.Equal(expected.IsNode, actual.IsNode);
        if (expected.IsNode)
        {
            AssertEqual(expected.Node, actual.Node);
        }
        else
        {
            AssertEqual(expected.Token, actual.Token);
        }
    }
 
    public static void AssertEqual(StackFrameNode? expected, StackFrameNode? actual)
    {
        if (expected is null)
        {
            Assert.Null(actual);
            return;
        }
 
        AssertEx.NotNull(actual);
 
        Assert.Equal(expected.Kind, actual.Kind);
        Assert.True(expected.ChildCount == actual.ChildCount, PrintChildDifference(expected, actual));
 
        for (var i = 0; i < expected.ChildCount; i++)
        {
            AssertEqual(expected.ChildAt(i), actual.ChildAt(i));
        }
 
        static string PrintChildDifference(StackFrameNode expected, StackFrameNode actual)
        {
            var sb = new StringBuilder();
            sb.Append("Expected: ");
            Print(expected, sb);
            sb.AppendLine();
 
            sb.Append("Actual: ");
            Print(actual, sb);
 
            return sb.ToString();
        }
    }
 
    public static void Print(StackFrameNode node, StringBuilder sb)
    {
        foreach (var child in node)
        {
            if (child.IsNode)
            {
                Print(child.Node, sb);
            }
            else
            {
                if (!child.Token.LeadingTrivia.IsDefaultOrEmpty)
                {
                    Print(child.Token.LeadingTrivia, sb);
                }
 
                sb.Append(child.Token.VirtualChars.CreateString());
 
                if (!child.Token.TrailingTrivia.IsDefaultOrEmpty)
                {
                    Print(child.Token.TrailingTrivia, sb);
                }
            }
        }
    }
 
    public static void Print(ImmutableArray<StackFrameTrivia> triviaArray, StringBuilder sb)
    {
        if (triviaArray.IsDefault)
        {
            sb.Append("<default>");
            return;
        }
 
        if (triviaArray.IsEmpty)
        {
            sb.Append("<empty>");
            return;
        }
 
        foreach (var trivia in triviaArray)
        {
            sb.Append(trivia.VirtualChars.CreateString());
        }
    }
 
    public static void AssertEqual(StackFrameToken expected, StackFrameToken actual)
    {
        Assert.Equal(expected.Kind, actual.Kind);
        Assert.Equal(expected.IsMissing, actual.IsMissing);
        Assert.Equal(expected.VirtualChars.CreateString(), actual.VirtualChars.CreateString());
 
        AssertEqual(expected.LeadingTrivia, actual.LeadingTrivia, expected);
        AssertEqual(expected.TrailingTrivia, actual.TrailingTrivia, expected);
    }
 
    public static void AssertEqual(ImmutableArray<StackFrameTrivia> expected, ImmutableArray<StackFrameTrivia> actual, StackFrameToken token)
    {
        var diffMessage = PrintDiff();
 
        if (expected.IsDefault)
        {
            Assert.True(actual.IsDefault, diffMessage);
            return;
        }
 
        Assert.False(actual.IsDefault, diffMessage);
        Assert.True(expected.Length == actual.Length, diffMessage);
 
        for (var i = 0; i < expected.Length; i++)
        {
            AssertEqual(expected[i], actual[i]);
        }
 
        string PrintDiff()
        {
            var sb = new StringBuilder();
            sb.AppendLine($"Trivia is different on {token.Kind}");
            sb.Append("Expected: ");
 
            if (!expected.IsDefaultOrEmpty)
            {
                sb.Append('[');
            }
 
            Print(expected, sb);
 
            if (expected.IsDefaultOrEmpty)
            {
                sb.AppendLine();
            }
            else
            {
                sb.AppendLine("]");
            }
 
            sb.Append("Actual: ");
 
            if (!actual.IsDefaultOrEmpty)
            {
                sb.Append('[');
            }
 
            Print(actual, sb);
 
            if (!actual.IsDefaultOrEmpty)
            {
                sb.Append(']');
            }
 
            return sb.ToString();
        }
    }
 
    public static void AssertEqual(StackFrameTrivia expected, StackFrameTrivia actual)
    {
        Assert.Equal(expected.Kind, actual.Kind);
        Assert.Equal(expected.VirtualChars.CreateString(), actual.VirtualChars.CreateString());
    }
 
    public static IEnumerable<CodeAnalysis.EmbeddedLanguages.VirtualChars.VirtualCharSequence> Enumerate(StackFrameToken token)
    {
        foreach (var trivia in token.LeadingTrivia)
        {
            yield return trivia.VirtualChars;
        }
 
        yield return token.VirtualChars;
 
        foreach (var trivia in token.TrailingTrivia)
        {
            yield return trivia.VirtualChars;
        }
    }
 
    public static IEnumerable<CodeAnalysis.EmbeddedLanguages.VirtualChars.VirtualCharSequence> Enumerate(StackFrameNode node)
    {
        foreach (var nodeOrToken in node)
        {
            if (nodeOrToken.IsNode)
            {
                foreach (var charSequence in Enumerate(nodeOrToken.Node))
                {
                    yield return charSequence;
                }
            }
            else if (nodeOrToken.Token.Kind != StackFrameKind.None)
            {
                foreach (var charSequence in Enumerate(nodeOrToken.Token))
                {
                    yield return charSequence;
                }
            }
            else
            {
                // If we encounter a None token make sure it has default values
                Assert.True(nodeOrToken.Token.IsMissing);
                Assert.True(nodeOrToken.Token.LeadingTrivia.IsDefault);
                Assert.True(nodeOrToken.Token.TrailingTrivia.IsDefault);
                Assert.Null(nodeOrToken.Token.Value);
                Assert.True(nodeOrToken.Token.VirtualChars.IsDefault);
            }
        }
    }
}