|
// 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.Linq;
using System.Text;
using Microsoft.CodeAnalysis.Editor.UnitTests.Utilities;
using Microsoft.CodeAnalysis.EmbeddedLanguages.Common;
using Microsoft.CodeAnalysis.EmbeddedLanguages.StackFrame;
using Microsoft.CodeAnalysis.EmbeddedLanguages.VirtualChars;
using Roslyn.Test.Utilities;
using Xunit;
using static Microsoft.CodeAnalysis.Editor.UnitTests.EmbeddedLanguages.StackFrame.StackFrameSyntaxFactory;
namespace Microsoft.CodeAnalysis.Editor.UnitTests.EmbeddedLanguages.StackFrame;
using StackFrameToken = EmbeddedSyntaxToken<StackFrameKind>;
public partial class StackFrameParserTests
{
private static void Verify(
string input,
StackFrameMethodDeclarationNode? methodDeclaration = null,
bool expectFailure = false,
StackFrameFileInformationNode? fileInformation = null,
StackFrameToken? eolTokenOpt = null)
{
FuzzyTest(input);
var tree = StackFrameParser.TryParse(input);
if (expectFailure)
{
Assert.Null(tree);
return;
}
AssertEx.NotNull(tree);
VerifyCharacterSpans(input, tree);
if (methodDeclaration is null)
{
Assert.Null(tree.Root.MethodDeclaration);
}
else
{
StackFrameUtils.AssertEqual(methodDeclaration, tree.Root.MethodDeclaration);
}
if (fileInformation is null)
{
Assert.Null(tree.Root.FileInformationExpression);
}
else
{
StackFrameUtils.AssertEqual(fileInformation, tree.Root.FileInformationExpression);
}
var eolToken = eolTokenOpt.HasValue
? eolTokenOpt.Value
: CreateToken(StackFrameKind.EndOfFrame, "");
StackFrameUtils.AssertEqual(eolToken, tree.Root.EndOfLineToken);
}
/// <summary>
/// Tests that with a given input, no crashes are found
/// with multiple substrings of the input
/// </summary>
private static void FuzzyTest(string input)
{
for (var i = 0; i < input.Length - 1; i++)
{
StackFrameParser.TryParse(input[i..]);
StackFrameParser.TryParse(input[..^i]);
for (var j = 0; j + i < input.Length; j++)
{
var start = input[..j];
var end = input[(j + i)..];
StackFrameParser.TryParse(start + end);
}
}
}
private static void VerifyCharacterSpans(string originalText, StackFrameTree tree)
{
AssertEx.EqualOrDiff(originalText, tree.Root.ToFullString());
// Manually enumerate to verify that it works as expected and the spans align.
// This should be the same as ToFullString, but this tests that enumeration of the
// tokens yields the correct order (which we can't guarantee with ToFullString depending
// on implementation).
var textSeq = VirtualCharSequence.Create(0, originalText);
var index = 0;
List<VirtualChar> enumeratedParsedCharacters = [];
foreach (var charSeq in StackFrameUtils.Enumerate(tree.Root))
{
foreach (var ch in charSeq)
{
enumeratedParsedCharacters.Add(ch);
if (textSeq[index++] != ch)
{
Assert.True(false, PrintDifference());
}
}
}
// Make sure we enumerated the total input
Assert.Equal(textSeq.Length, index);
string PrintDifference()
{
var sb = new StringBuilder();
var start = Math.Max(0, index - 10);
var end = Math.Min(index, originalText.Length - 1);
sb.Append("Expected: \t");
PrintString(originalText, start, end, sb);
sb.AppendLine();
sb.Append("Actual: \t");
var enumeratedString = new string(enumeratedParsedCharacters.Select(ch => (char)ch.Value).ToArray());
PrintString(enumeratedString, start, end, sb);
sb.AppendLine();
return sb.ToString();
static void PrintString(string s, int start, int end, StringBuilder sb)
{
if (start > 0)
{
sb.Append("...");
}
sb.Append(s[start..end]);
if (end < s.Length - 1)
{
sb.Append("...");
}
}
}
}
}
|