File: Syntax\GreenNodeTests.cs
Web Access
Project: src\src\Razor\src\Compiler\Microsoft.AspNetCore.Razor.Language\test\Microsoft.AspNetCore.Razor.Language.UnitTests.csproj (Microsoft.AspNetCore.Razor.Language.UnitTests)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System;
using System.Collections.Generic;
using Xunit;
 
namespace Microsoft.AspNetCore.Razor.Language.Syntax;
 
public class GreenNodeTests
{
    [Fact]
    public void GetEnumerator_EmptyNode_ReturnsNodeAndToken()
    {
        // Tree structure:
        //   MarkupTextLiteral (node)
        //   └── Text: "" (token)
 
        // Arrange
        var token = InternalSyntax.SyntaxFactory.Token(SyntaxKind.Text, "");
        var node = InternalSyntax.SyntaxFactory.MarkupTextLiteral(token);
 
        // Act
        var enumerator = node.GetEnumerator();
        var elements = new List<GreenNode>();
 
        while (enumerator.MoveNext())
        {
            elements.Add(enumerator.Current);
        }
 
        // Assert
        Assert.Equal(2, elements.Count);
        Assert.Same(node, elements[0]);  // Node first
        Assert.Same(token, elements[1]); // Then token
    }
 
    [Fact]
    public void GetEnumerator_SingleNode_ReturnsNodeAndToken()
    {
        // Tree structure:
        //   MarkupTextLiteral (node)
        //   └── Text: "Hello" (token)
 
        // Arrange
        var token = InternalSyntax.SyntaxFactory.Token(SyntaxKind.Text, "Hello");
        var node = InternalSyntax.SyntaxFactory.MarkupTextLiteral(token);
 
        // Act
        var enumerator = node.GetEnumerator();
        var elements = new List<GreenNode>();
 
        while (enumerator.MoveNext())
        {
            elements.Add(enumerator.Current);
        }
 
        // Assert
        Assert.Equal(2, elements.Count);
        Assert.Same(node, elements[0]);  // Node first
        Assert.Same(token, elements[1]); // Then token
    }
 
    [Fact]
    public void GetEnumerator_NodeWithChildren_PerformsDepthFirstTraversal()
    {
        // Tree structure:
        //   GenericBlock (root)
        //   ├── MarkupTextLiteral (child1)
        //   │   └── Text: "Hello" (token1)
        //   ├── MarkupTextLiteral (child2)
        //   │   └── Whitespace: " " (token2)
        //   └── GenericBlock (child3)
        //       └── MarkupTextLiteral (grandchild)
        //           └── Text: "World" (token3)
 
        // Arrange
        var token1 = InternalSyntax.SyntaxFactory.Token(SyntaxKind.Text, "Hello");
        var child1 = InternalSyntax.SyntaxFactory.MarkupTextLiteral(token1);
 
        var token2 = InternalSyntax.SyntaxFactory.Token(SyntaxKind.Whitespace, " ");
        var child2 = InternalSyntax.SyntaxFactory.MarkupTextLiteral(token2);
 
        var token3 = InternalSyntax.SyntaxFactory.Token(SyntaxKind.Text, "World");
        var grandchild = InternalSyntax.SyntaxFactory.MarkupTextLiteral(token3);
 
        var child3 = InternalSyntax.SyntaxFactory.GenericBlock(grandchild);
 
        var root = InternalSyntax.SyntaxFactory.GenericBlock([child1, child2, child3]);
 
        // Act
        var enumerator = root.GetEnumerator();
        var elements = new List<GreenNode>();
 
        while (enumerator.MoveNext())
        {
            elements.Add(enumerator.Current);
        }
 
        // Assert
        Assert.Equal(8, elements.Count);
        Assert.Same(root, elements[0]);       // Root visited first
        Assert.Same(child1, elements[1]);     // First child
        Assert.Same(token1, elements[2]);     // First child's token
        Assert.Same(child2, elements[3]);     // Second child
        Assert.Same(token2, elements[4]);     // Second child's token
        Assert.Same(child3, elements[5]);     // Third child (parent of grandchild)
        Assert.Same(grandchild, elements[6]); // Grandchild visited after its parent
        Assert.Same(token3, elements[7]);     // Grandchild's token
    }
 
    [Fact]
    public void GetEnumerator_ComplexTree_MaintainsDepthFirstOrder()
    {
        // Tree structure:
        //   GenericBlock (root)
        //   ├── MarkupTextLiteral (child1)
        //   │   └── Whitespace: " " (token1)
        //   └── GenericBlock (child2)
        //       ├── MarkupTextLiteral (grandchild1)
        //       │   └── Text: "A" (token2)
        //       └── MarkupTextLiteral (grandchild2)
        //           └── Text: "B" (token3)
 
        // Arrange
        var token1 = InternalSyntax.SyntaxFactory.Token(SyntaxKind.Whitespace, " ");
        var child1 = InternalSyntax.SyntaxFactory.MarkupTextLiteral(token1);
 
        var token2 = InternalSyntax.SyntaxFactory.Token(SyntaxKind.Text, "A");
        var grandchild1 = InternalSyntax.SyntaxFactory.MarkupTextLiteral(token2);
 
        var token3 = InternalSyntax.SyntaxFactory.Token(SyntaxKind.Text, "B");
        var grandchild2 = InternalSyntax.SyntaxFactory.MarkupTextLiteral(token3);
 
        var child2 = InternalSyntax.SyntaxFactory.GenericBlock([grandchild1, grandchild2]);
 
        var root = InternalSyntax.SyntaxFactory.GenericBlock([child1, child2]);
 
        // Act
        var enumerator = root.GetEnumerator();
        var elements = new List<GreenNode>();
 
        while (enumerator.MoveNext())
        {
            elements.Add(enumerator.Current);
        }
 
        // Assert
        Assert.Equal(8, elements.Count);
        Assert.Same(root, elements[0]);
        Assert.Same(child1, elements[1]);
        Assert.Same(token1, elements[2]);     // child1's token
        Assert.Same(child2, elements[3]);
        Assert.Same(grandchild1, elements[4]);
        Assert.Same(token2, elements[5]);     // grandchild1's token
        Assert.Same(grandchild2, elements[6]);
        Assert.Same(token3, elements[7]);     // grandchild2's token
    }
 
    [Fact]
    public void GetEnumerator_CanBeUsedInForeachLoop()
    {
        // Tree structure:
        //   GenericBlock (root)
        //   ├── MarkupTextLiteral (child1)
        //   │   └── Text: "Hello" (token1)
        //   └── MarkupTextLiteral (child2)
        //       └── Text: "World" (token2)
 
        // Arrange
        var token1 = InternalSyntax.SyntaxFactory.Token(SyntaxKind.Text, "Hello");
        var child1 = InternalSyntax.SyntaxFactory.MarkupTextLiteral(token1);
 
        var token2 = InternalSyntax.SyntaxFactory.Token(SyntaxKind.Text, "World");
        var child2 = InternalSyntax.SyntaxFactory.MarkupTextLiteral(token2);
 
        var root = InternalSyntax.SyntaxFactory.GenericBlock([child1, child2]);
 
        // Act
        var elements = new List<GreenNode>();
        foreach (var node in root)
        {
            elements.Add(node);
        }
 
        // Assert
        Assert.Equal(5, elements.Count);
        Assert.Same(root, elements[0]);
        Assert.Same(child1, elements[1]);
        Assert.Same(token1, elements[2]);     // child1's token
        Assert.Same(child2, elements[3]);
        Assert.Same(token2, elements[4]);     // child2's token
    }
 
    [Fact]
    public void GetEnumerator_MultipleEnumerators_AreIndependent()
    {
        // Tree structure:
        //   GenericBlock (root)
        //   └── MarkupTextLiteral (child)
        //       └── Text: "Test" (token)
 
        // Arrange
        var token = InternalSyntax.SyntaxFactory.Token(SyntaxKind.Text, "Test");
        var child = InternalSyntax.SyntaxFactory.MarkupTextLiteral(token);
 
        var root = InternalSyntax.SyntaxFactory.GenericBlock(child);
 
        // Act
        var enumerator1 = root.GetEnumerator();
        var enumerator2 = root.GetEnumerator();
 
        var hasNext1 = enumerator1.MoveNext();
        var hasNext2 = enumerator2.MoveNext();
 
        // Assert
        Assert.True(hasNext1);
        Assert.True(hasNext2);
        Assert.Same(root, enumerator1.Current);
        Assert.Same(root, enumerator2.Current);
    }
 
    [Fact]
    public void GetEnumerator_TokenNode_ReturnsSelfOnly()
    {
        // Tree structure:
        //   Text: "Hello" (token)
 
        // Arrange
        var token = InternalSyntax.SyntaxFactory.Token(SyntaxKind.Text, "Hello");
 
        // Act
        var enumerator = token.GetEnumerator();
        var elements = new List<GreenNode>();
 
        while (enumerator.MoveNext())
        {
            elements.Add(enumerator.Current);
        }
 
        // Assert
        Assert.Single(elements);
        Assert.Same(token, elements[0]);
    }
 
    [Fact]
    public void GetEnumerator_MixedMarkupAndCode_PerformsDepthFirstTraversal()
    {
        // Tree structure:
        //   GenericBlock (root)
        //   ├── MarkupTextLiteral (htmlNode)
        //   │   └── Text: "<div>" (htmlToken)
        //   ├── CSharpTransition (transitionNode)
        //   │   └── Transition: "@" (transitionToken)
        //   └── CSharpExpressionLiteral (codeNode)
        //       └── Identifier: "Model" (codeToken)
 
        // Arrange
        var htmlToken = InternalSyntax.SyntaxFactory.Token(SyntaxKind.Text, "<div>");
        var htmlNode = InternalSyntax.SyntaxFactory.MarkupTextLiteral(htmlToken);
 
        var transitionToken = InternalSyntax.SyntaxFactory.Token(SyntaxKind.Transition, "@");
        var transitionNode = InternalSyntax.SyntaxFactory.CSharpTransition(transitionToken);
 
        var codeToken = InternalSyntax.SyntaxFactory.Token(SyntaxKind.Identifier, "Model");
        var codeNode = InternalSyntax.SyntaxFactory.CSharpExpressionLiteral(codeToken);
 
        var root = InternalSyntax.SyntaxFactory.GenericBlock([htmlNode, transitionNode, codeNode]);
 
        // Act
        var enumerator = root.GetEnumerator();
        var elements = new List<GreenNode>();
 
        while (enumerator.MoveNext())
        {
            elements.Add(enumerator.Current);
        }
 
        // Assert
        Assert.Equal(7, elements.Count);
        Assert.Same(root, elements[0]);
        Assert.Same(htmlNode, elements[1]);
        Assert.Same(htmlToken, elements[2]);        // htmlNode's token
        Assert.Same(transitionNode, elements[3]);
        Assert.Same(transitionToken, elements[4]);  // transitionNode's token
        Assert.Same(codeNode, elements[5]);
        Assert.Same(codeToken, elements[6]);        // codeNode's token
    }
 
    [Fact]
    public void GetEnumerator_EnumeratesTokensAndNodes_InDepthFirstOrder()
    {
        // Tree structure:
        //   MarkupTextLiteral (node)
        //   └── Text: "Test" (token)
        //
        // Note: This test demonstrates that both nodes and tokens are enumerated
 
        // Arrange
        var token = InternalSyntax.SyntaxFactory.Token(SyntaxKind.Text, "Test");
        var node = InternalSyntax.SyntaxFactory.MarkupTextLiteral(token);
 
        // Act
        var enumerator = node.GetEnumerator();
        var elements = new List<GreenNode>();
        var nodeTypes = new List<bool>(); // true for node, false for token
 
        while (enumerator.MoveNext())
        {
            elements.Add(enumerator.Current);
            nodeTypes.Add(!enumerator.Current.IsToken);
        }
 
        // Assert
        Assert.Equal(2, elements.Count);
        Assert.Same(node, elements[0]);
        Assert.Same(token, elements[1]);
        Assert.True(nodeTypes[0]);   // First element is a node
        Assert.False(nodeTypes[1]);  // Second element is a token
    }
 
    [Fact]
    public void Tokens_SingleToken_ReturnsOnlyToken()
    {
        // Tree structure:
        //   Text: "Hello" (token)
 
        // Arrange
        var token = InternalSyntax.SyntaxFactory.Token(SyntaxKind.Text, "Hello");
 
        // Act
        var tokens = new List<InternalSyntax.SyntaxToken>();
        foreach (var t in token.Tokens())
        {
            tokens.Add(t);
        }
 
        // Assert
        Assert.Single(tokens);
        Assert.Same(token, tokens[0]);
    }
 
    [Fact]
    public void Tokens_NodeWithSingleToken_ReturnsOnlyToken()
    {
        // Tree structure:
        //   MarkupTextLiteral (node)
        //   └── Text: "Hello" (token)
 
        // Arrange
        var token = InternalSyntax.SyntaxFactory.Token(SyntaxKind.Text, "Hello");
        var node = InternalSyntax.SyntaxFactory.MarkupTextLiteral(token);
 
        // Act
        var tokens = new List<InternalSyntax.SyntaxToken>();
        foreach (var t in node.Tokens())
        {
            tokens.Add(t);
        }
 
        // Assert
        Assert.Single(tokens);
        Assert.Same(token, tokens[0]);
    }
 
    [Fact]
    public void Tokens_ComplexTree_ReturnsOnlyTokensInOrder()
    {
        // Tree structure:
        //   GenericBlock (root)
        //   ├── MarkupTextLiteral (child1)
        //   │   └── Text: "Hello" (token1)
        //   ├── MarkupTextLiteral (child2)
        //   │   └── Whitespace: " " (token2)
        //   └── GenericBlock (child3)
        //       └── MarkupTextLiteral (grandchild)
        //           └── Text: "World" (token3)
 
        // Arrange
        var token1 = InternalSyntax.SyntaxFactory.Token(SyntaxKind.Text, "Hello");
        var child1 = InternalSyntax.SyntaxFactory.MarkupTextLiteral(token1);
 
        var token2 = InternalSyntax.SyntaxFactory.Token(SyntaxKind.Whitespace, " ");
        var child2 = InternalSyntax.SyntaxFactory.MarkupTextLiteral(token2);
 
        var token3 = InternalSyntax.SyntaxFactory.Token(SyntaxKind.Text, "World");
        var grandchild = InternalSyntax.SyntaxFactory.MarkupTextLiteral(token3);
 
        var child3 = InternalSyntax.SyntaxFactory.GenericBlock(grandchild);
        var root = InternalSyntax.SyntaxFactory.GenericBlock([child1, child2, child3]);
 
        // Act
        var tokens = new List<InternalSyntax.SyntaxToken>();
        foreach (var token in root.Tokens())
        {
            tokens.Add(token);
        }
 
        // Assert
        Assert.Equal(3, tokens.Count);
        Assert.Same(token1, tokens[0]);  // "Hello"
        Assert.Same(token2, tokens[1]);  // " "
        Assert.Same(token3, tokens[2]);  // "World"
    }
 
    [Fact]
    public void Tokens_MixedMarkupAndCode_ReturnsAllTokensInDepthFirstOrder()
    {
        // Tree structure:
        //   GenericBlock (root)
        //   ├── MarkupTextLiteral (htmlNode)
        //   │   └── Text: "<div>" (htmlToken)
        //   ├── CSharpTransition (transitionNode)
        //   │   └── Transition: "@" (transitionToken)
        //   └── CSharpExpressionLiteral (codeNode)
        //       └── Identifier: "Model" (codeToken)
 
        // Arrange
        var htmlToken = InternalSyntax.SyntaxFactory.Token(SyntaxKind.Text, "<div>");
        var htmlNode = InternalSyntax.SyntaxFactory.MarkupTextLiteral(htmlToken);
 
        var transitionToken = InternalSyntax.SyntaxFactory.Token(SyntaxKind.Transition, "@");
        var transitionNode = InternalSyntax.SyntaxFactory.CSharpTransition(transitionToken);
 
        var codeToken = InternalSyntax.SyntaxFactory.Token(SyntaxKind.Identifier, "Model");
        var codeNode = InternalSyntax.SyntaxFactory.CSharpExpressionLiteral(codeToken);
 
        var root = InternalSyntax.SyntaxFactory.GenericBlock([htmlNode, transitionNode, codeNode]);
 
        // Act
        var tokens = new List<InternalSyntax.SyntaxToken>();
        foreach (var token in root.Tokens())
        {
            tokens.Add(token);
        }
 
        // Assert
        Assert.Equal(3, tokens.Count);
        Assert.Same(htmlToken, tokens[0]);      // "<div>"
        Assert.Same(transitionToken, tokens[1]); // "@"
        Assert.Same(codeToken, tokens[2]);      // "Model"
    }
 
    [Fact]
    public void Tokens_EmptyToken_ReturnsEmptyToken()
    {
        // Tree structure:
        //   MarkupTextLiteral (node)
        //   └── Text: "" (token)
 
        // Arrange
        var token = InternalSyntax.SyntaxFactory.Token(SyntaxKind.Text, "");
        var node = InternalSyntax.SyntaxFactory.MarkupTextLiteral(token);
 
        // Act
        var tokens = new List<InternalSyntax.SyntaxToken>();
        foreach (var t in node.Tokens())
        {
            tokens.Add(t);
        }
 
        // Assert
        Assert.Single(tokens);
        Assert.Same(token, tokens[0]);
        Assert.Equal("", tokens[0].Content);
    }
 
    [Fact]
    public void Tokens_CanBeEnumeratedMultipleTimes()
    {
        // Tree structure:
        //   GenericBlock (root)
        //   ├── MarkupTextLiteral (child1)
        //   │   └── Text: "A" (token1)
        //   └── MarkupTextLiteral (child2)
        //       └── Text: "B" (token2)
 
        // Arrange
        var token1 = InternalSyntax.SyntaxFactory.Token(SyntaxKind.Text, "A");
        var child1 = InternalSyntax.SyntaxFactory.MarkupTextLiteral(token1);
 
        var token2 = InternalSyntax.SyntaxFactory.Token(SyntaxKind.Text, "B");
        var child2 = InternalSyntax.SyntaxFactory.MarkupTextLiteral(token2);
 
        var root = InternalSyntax.SyntaxFactory.GenericBlock([child1, child2]);
 
        // Act - enumerate twice
        var firstEnumeration = new List<InternalSyntax.SyntaxToken>();
        foreach (var token in root.Tokens())
        {
            firstEnumeration.Add(token);
        }
 
        var secondEnumeration = new List<InternalSyntax.SyntaxToken>();
        foreach (var token in root.Tokens())
        {
            secondEnumeration.Add(token);
        }
 
        // Assert
        Assert.Equal(2, firstEnumeration.Count);
        Assert.Equal(2, secondEnumeration.Count);
        
        Assert.Same(token1, firstEnumeration[0]);
        Assert.Same(token2, firstEnumeration[1]);
        
        Assert.Same(token1, secondEnumeration[0]);
        Assert.Same(token2, secondEnumeration[1]);
    }
 
    [Fact]
    public void Tokens_WithManualEnumerator_WorksCorrectly()
    {
        // Tree structure:
        //   MarkupTextLiteral (node)
        //   └── Text: "Test" (token)
 
        // Arrange
        var token = InternalSyntax.SyntaxFactory.Token(SyntaxKind.Text, "Test");
        var node = InternalSyntax.SyntaxFactory.MarkupTextLiteral(token);
 
        // Act
        var tokenEnumerable = node.Tokens();
        var enumerator = tokenEnumerable.GetEnumerator();
        
        var tokens = new List<InternalSyntax.SyntaxToken>();
        while (enumerator.MoveNext())
        {
            tokens.Add(enumerator.Current);
        }
 
        // Assert
        Assert.Single(tokens);
        Assert.Same(token, tokens[0]);
    }
 
    [Fact]
    public void Tokens_FilterOutNodesAndKeepOnlyTokens()
    {
        // Tree structure:
        //   GenericBlock (root) <- filtered out
        //   └── MarkupTextLiteral (child) <- filtered out
        //       └── Text: "OnlyThis" (token) <- kept
 
        // Arrange
        var token = InternalSyntax.SyntaxFactory.Token(SyntaxKind.Text, "OnlyThis");
        var child = InternalSyntax.SyntaxFactory.MarkupTextLiteral(token);
        var root = InternalSyntax.SyntaxFactory.GenericBlock(child);
 
        // Act
        var tokens = new List<InternalSyntax.SyntaxToken>();
        foreach (var t in root.Tokens())
        {
            tokens.Add(t);
        }
 
        // Assert
        Assert.Single(tokens);
        Assert.Same(token, tokens[0]);
        Assert.Equal("OnlyThis", tokens[0].Content);
        Assert.True(tokens[0].IsToken);
    }
 
    [Fact]
    public void TokenEnumerable_Enumerator_Current_ThrowsBeforeFirstMoveNext()
    {
        // Arrange
        var token = InternalSyntax.SyntaxFactory.Token(SyntaxKind.Text, "Test");
        var node = InternalSyntax.SyntaxFactory.MarkupTextLiteral(token);
        var enumerator = node.Tokens().GetEnumerator();
 
        // Act & Assert
        try
        {
            _ = enumerator.Current;
        }
        catch (Exception ex)
        {
            // Note: We can't use Assert.Throws because enumerator is a ref-struct
            // and can't be captured in a lambda.
            Assert.IsType<NullReferenceException>(ex);
        }
    }
 
    [Fact]
    public void TokenEnumerable_Enumerator_Current_ReturnsCorrectToken()
    {
        // Tree structure:
        //   GenericBlock (root)
        //   ├── MarkupTextLiteral (child1)
        //   │   └── Text: "First" (token1)
        //   └── MarkupTextLiteral (child2)
        //       └── Text: "Second" (token2)
 
        // Arrange
        var token1 = InternalSyntax.SyntaxFactory.Token(SyntaxKind.Text, "First");
        var child1 = InternalSyntax.SyntaxFactory.MarkupTextLiteral(token1);
 
        var token2 = InternalSyntax.SyntaxFactory.Token(SyntaxKind.Text, "Second");
        var child2 = InternalSyntax.SyntaxFactory.MarkupTextLiteral(token2);
 
        var root = InternalSyntax.SyntaxFactory.GenericBlock([child1, child2]);
        var enumerator = root.Tokens().GetEnumerator();
 
        // Act & Assert
        Assert.True(enumerator.MoveNext());
        Assert.Same(token1, enumerator.Current);
        Assert.Equal("First", enumerator.Current.Content);
 
        Assert.True(enumerator.MoveNext());
        Assert.Same(token2, enumerator.Current);
        Assert.Equal("Second", enumerator.Current.Content);
 
        Assert.False(enumerator.MoveNext());
    }
 
    [Fact]
    public void ToString_SingleToken_ReturnsTokenContent()
    {
        // Tree structure:
        //   Text: "Hello" (token)
 
        // Arrange
        var token = InternalSyntax.SyntaxFactory.Token(SyntaxKind.Text, "Hello");
 
        // Act
        var result = token.ToString();
 
        // Assert
        Assert.Equal("Hello", result);
    }
 
    [Fact]
    public void ToString_EmptyToken_ReturnsEmptyString()
    {
        // Tree structure:
        //   Text: "" (token)
 
        // Arrange
        var token = InternalSyntax.SyntaxFactory.Token(SyntaxKind.Text, "");
 
        // Act
        var result = token.ToString();
 
        // Assert
        Assert.Equal("", result);
    }
 
    [Fact]
    public void ToString_NodeWithSingleToken_ReturnsTokenContent()
    {
        // Tree structure:
        //   MarkupTextLiteral (node)
        //   └── Text: "Hello World" (token)
 
        // Arrange
        var token = InternalSyntax.SyntaxFactory.Token(SyntaxKind.Text, "Hello World");
        var node = InternalSyntax.SyntaxFactory.MarkupTextLiteral(token);
 
        // Act
        var result = node.ToString();
 
        // Assert
        Assert.Equal("Hello World", result);
    }
 
    [Fact]
    public void ToString_ComplexTree_ConcatenatesAllTokensInDepthFirstOrder()
    {
        // Tree structure:
        //   GenericBlock (root)
        //   ├── MarkupTextLiteral (child1)
        //   │   └── Text: "Hello" (token1)
        //   ├── MarkupTextLiteral (child2)
        //   │   └── Whitespace: " " (token2)
        //   └── GenericBlock (child3)
        //       └── MarkupTextLiteral (grandchild)
        //           └── Text: "World" (token3)
 
        // Arrange
        var token1 = InternalSyntax.SyntaxFactory.Token(SyntaxKind.Text, "Hello");
        var child1 = InternalSyntax.SyntaxFactory.MarkupTextLiteral(token1);
 
        var token2 = InternalSyntax.SyntaxFactory.Token(SyntaxKind.Whitespace, " ");
        var child2 = InternalSyntax.SyntaxFactory.MarkupTextLiteral(token2);
 
        var token3 = InternalSyntax.SyntaxFactory.Token(SyntaxKind.Text, "World");
        var grandchild = InternalSyntax.SyntaxFactory.MarkupTextLiteral(token3);
 
        var child3 = InternalSyntax.SyntaxFactory.GenericBlock(grandchild);
        var root = InternalSyntax.SyntaxFactory.GenericBlock([child1, child2, child3]);
 
        // Act
        var result = root.ToString();
 
        // Assert
        Assert.Equal("Hello World", result);
    }
 
    [Fact]
    public void ToString_MixedMarkupAndCode_ConcatenatesAllTokenContent()
    {
        // Tree structure:
        //   GenericBlock (root)
        //   ├── MarkupTextLiteral (htmlNode)
        //   │   └── Text: "<div>" (htmlToken)
        //   ├── CSharpTransition (transitionNode)
        //   │   └── Transition: "@" (transitionToken)
        //   └── CSharpExpressionLiteral (codeNode)
        //       └── Identifier: "Model" (codeToken)
 
        // Arrange
        var htmlToken = InternalSyntax.SyntaxFactory.Token(SyntaxKind.Text, "<div>");
        var htmlNode = InternalSyntax.SyntaxFactory.MarkupTextLiteral(htmlToken);
 
        var transitionToken = InternalSyntax.SyntaxFactory.Token(SyntaxKind.Transition, "@");
        var transitionNode = InternalSyntax.SyntaxFactory.CSharpTransition(transitionToken);
 
        var codeToken = InternalSyntax.SyntaxFactory.Token(SyntaxKind.Identifier, "Model");
        var codeNode = InternalSyntax.SyntaxFactory.CSharpExpressionLiteral(codeToken);
 
        var root = InternalSyntax.SyntaxFactory.GenericBlock([htmlNode, transitionNode, codeNode]);
 
        // Act
        var result = root.ToString();
 
        // Assert
        Assert.Equal("<div>@Model", result);
    }
 
    [Fact]
    public void ToString_MultipleNestedNodes_ConcatenatesInCorrectOrder()
    {
        // Tree structure:
        //   GenericBlock (root)
        //   ├── MarkupTextLiteral (child1)
        //   │   └── Text: "Start" (token1)
        //   └── GenericBlock (child2)
        //       ├── MarkupTextLiteral (grandchild1)
        //       │   └── Text: "Middle" (token2)
        //       └── MarkupTextLiteral (grandchild2)
        //           └── Text: "End" (token3)
 
        // Arrange
        var token1 = InternalSyntax.SyntaxFactory.Token(SyntaxKind.Text, "Start");
        var child1 = InternalSyntax.SyntaxFactory.MarkupTextLiteral(token1);
 
        var token2 = InternalSyntax.SyntaxFactory.Token(SyntaxKind.Text, "Middle");
        var grandchild1 = InternalSyntax.SyntaxFactory.MarkupTextLiteral(token2);
 
        var token3 = InternalSyntax.SyntaxFactory.Token(SyntaxKind.Text, "End");
        var grandchild2 = InternalSyntax.SyntaxFactory.MarkupTextLiteral(token3);
 
        var child2 = InternalSyntax.SyntaxFactory.GenericBlock([grandchild1, grandchild2]);
        var root = InternalSyntax.SyntaxFactory.GenericBlock([child1, child2]);
 
        // Act
        var result = root.ToString();
 
        // Assert
        Assert.Equal("StartMiddleEnd", result);
    }
 
    [Fact]
    public void ToString_WithWhitespaceTokens_PreservesWhitespace()
    {
        // Tree structure:
        //   GenericBlock (root)
        //   ├── MarkupTextLiteral (child1)
        //   │   └── Text: "Hello" (token1)
        //   ├── MarkupTextLiteral (child2)
        //   │   └── Whitespace: "   " (token2)
        //   └── MarkupTextLiteral (child3)
        //       └── Text: "World" (token3)
 
        // Arrange
        var token1 = InternalSyntax.SyntaxFactory.Token(SyntaxKind.Text, "Hello");
        var child1 = InternalSyntax.SyntaxFactory.MarkupTextLiteral(token1);
 
        var token2 = InternalSyntax.SyntaxFactory.Token(SyntaxKind.Whitespace, "   ");
        var child2 = InternalSyntax.SyntaxFactory.MarkupTextLiteral(token2);
 
        var token3 = InternalSyntax.SyntaxFactory.Token(SyntaxKind.Text, "World");
        var child3 = InternalSyntax.SyntaxFactory.MarkupTextLiteral(token3);
 
        var root = InternalSyntax.SyntaxFactory.GenericBlock([child1, child2, child3]);
 
        // Act
        var result = root.ToString();
 
        // Assert
        Assert.Equal("Hello   World", result);
    }
 
    [Fact]
    public void ToString_WithSpecialCharacters_PreservesAllCharacters()
    {
        // Tree structure:
        //   GenericBlock (root)
        //   ├── MarkupTextLiteral (child1)
        //   │   └── Text: "Line1\n" (token1)
        //   └── MarkupTextLiteral (child2)
        //       └── Text: "Line2\t\r" (token2)
 
        // Arrange
        var token1 = InternalSyntax.SyntaxFactory.Token(SyntaxKind.Text, "Line1\n");
        var child1 = InternalSyntax.SyntaxFactory.MarkupTextLiteral(token1);
 
        var token2 = InternalSyntax.SyntaxFactory.Token(SyntaxKind.Text, "Line2\t\r");
        var child2 = InternalSyntax.SyntaxFactory.MarkupTextLiteral(token2);
 
        var root = InternalSyntax.SyntaxFactory.GenericBlock([child1, child2]);
 
        // Act
        var result = root.ToString();
 
        // Assert
        Assert.Equal("Line1\nLine2\t\r", result);
    }
 
    [Fact]
    public void ToString_WithUnicodeCharacters_PreservesUnicode()
    {
        // Tree structure:
        //   MarkupTextLiteral (node)
        //   └── Text: "Hello 🌍 World! ñáéíóú" (token)
 
        // Arrange
        var token = InternalSyntax.SyntaxFactory.Token(SyntaxKind.Text, "Hello 🌍 World! ñáéíóú");
        var node = InternalSyntax.SyntaxFactory.MarkupTextLiteral(token);
 
        // Act
        var result = node.ToString();
 
        // Assert
        Assert.Equal("Hello 🌍 World! ñáéíóú", result);
    }
 
    [Fact]
    public void ToString_EmptyNodeWithEmptyTokens_ReturnsEmptyString()
    {
        // Tree structure:
        //   GenericBlock (root)
        //   ├── MarkupTextLiteral (child1)
        //   │   └── Text: "" (token1)
        //   └── MarkupTextLiteral (child2)
        //       └── Text: "" (token2)
 
        // Arrange
        var token1 = InternalSyntax.SyntaxFactory.Token(SyntaxKind.Text, "");
        var child1 = InternalSyntax.SyntaxFactory.MarkupTextLiteral(token1);
 
        var token2 = InternalSyntax.SyntaxFactory.Token(SyntaxKind.Text, "");
        var child2 = InternalSyntax.SyntaxFactory.MarkupTextLiteral(token2);
 
        var root = InternalSyntax.SyntaxFactory.GenericBlock([child1, child2]);
 
        // Act
        var result = root.ToString();
 
        // Assert
        Assert.Equal("", result);
    }
 
    [Fact]
    public void ToString_ComplexRazorExample_ConcatenatesCorrectly()
    {
        // Tree structure representing something like: "if (condition) { @Model.Name }"
        //   GenericBlock (root)
        //   ├── MarkupTextLiteral
        //   │   └── Text: "if (condition) { " (token1)
        //   ├── CSharpTransition
        //   │   └── Transition: "@" (token2)
        //   ├── CSharpExpressionLiteral
        //   │   └── Identifier: "Model" (token3)
        //   ├── MarkupTextLiteral
        //   │   └── Text: "." (token4)
        //   ├── CSharpExpressionLiteral
        //   │   └── Identifier: "Name" (token5)
        //   └── MarkupTextLiteral
        //       └── Text: " }" (token6)
 
        // Arrange
        var token1 = InternalSyntax.SyntaxFactory.Token(SyntaxKind.Text, "if (condition) { ");
        var child1 = InternalSyntax.SyntaxFactory.MarkupTextLiteral(token1);
 
        var token2 = InternalSyntax.SyntaxFactory.Token(SyntaxKind.Transition, "@");
        var child2 = InternalSyntax.SyntaxFactory.CSharpTransition(token2);
 
        var token3 = InternalSyntax.SyntaxFactory.Token(SyntaxKind.Identifier, "Model");
        var child3 = InternalSyntax.SyntaxFactory.CSharpExpressionLiteral(token3);
 
        var token4 = InternalSyntax.SyntaxFactory.Token(SyntaxKind.Text, ".");
        var child4 = InternalSyntax.SyntaxFactory.MarkupTextLiteral(token4);
 
        var token5 = InternalSyntax.SyntaxFactory.Token(SyntaxKind.Identifier, "Name");
        var child5 = InternalSyntax.SyntaxFactory.CSharpExpressionLiteral(token5);
 
        var token6 = InternalSyntax.SyntaxFactory.Token(SyntaxKind.Text, " }");
        var child6 = InternalSyntax.SyntaxFactory.MarkupTextLiteral(token6);
 
        var root = InternalSyntax.SyntaxFactory.GenericBlock([child1, child2, child3, child4, child5, child6]);
 
        // Act
        var result = root.ToString();
 
        // Assert
        Assert.Equal("if (condition) { @Model.Name }", result);
    }
 
    [Fact]
    public void ToString_WidthMatchesStringLength()
    {
        // Tree structure:
        //   GenericBlock (root)
        //   ├── MarkupTextLiteral (child1)
        //   │   └── Text: "Hello" (token1)
        //   └── MarkupTextLiteral (child2)
        //       └── Text: " World!" (token2)
 
        // Arrange
        var token1 = InternalSyntax.SyntaxFactory.Token(SyntaxKind.Text, "Hello");
        var child1 = InternalSyntax.SyntaxFactory.MarkupTextLiteral(token1);
 
        var token2 = InternalSyntax.SyntaxFactory.Token(SyntaxKind.Text, " World!");
        var child2 = InternalSyntax.SyntaxFactory.MarkupTextLiteral(token2);
 
        var root = InternalSyntax.SyntaxFactory.GenericBlock([child1, child2]);
 
        // Act
        var result = root.ToString();
 
        // Assert
        Assert.Equal("Hello World!", result);
        Assert.Equal(result.Length, root.Width);
    }
 
    [Fact]
    public void ToString_ZeroWidth_ReturnsEmptyString()
    {
        // This test verifies the first optimization: if _width == 0, return string.Empty
 
        // Arrange - Create a node with zero width (empty token)
        var token = InternalSyntax.SyntaxFactory.Token(SyntaxKind.Text, "");
        
        // Act
        var result = token.ToString();
 
        // Assert
        Assert.Equal(string.Empty, result);
        Assert.Same(string.Empty, result); // Verify it's the same instance, not a new allocation
        Assert.Equal(0, token.Width);
    }
 
    [Fact]
    public void ToString_SingleTokenOptimization_ReturnsSameTokenContent()
    {
        // This test verifies the second optimization: single token returns token.Content directly
 
        // Arrange - Create a node with exactly one token
        var tokenContent = "Hello World";
        var token = InternalSyntax.SyntaxFactory.Token(SyntaxKind.Text, tokenContent);
        var node = InternalSyntax.SyntaxFactory.MarkupTextLiteral(token);
 
        // Act
        var result = node.ToString();
 
        // Assert
        Assert.Equal(tokenContent, result);
        // Verify it's the same string instance (optimization check)
        Assert.Same(tokenContent, result);
    }
 
    [Fact]
    public void ToString_SingleTokenDirectCall_ReturnsSameTokenContent()
    {
        // Test the optimization when calling ToString() directly on a token
 
        // Arrange
        var tokenContent = "Direct token call";
        var token = InternalSyntax.SyntaxFactory.Token(SyntaxKind.Text, tokenContent);
 
        // Act
        var result = token.ToString();
 
        // Assert
        Assert.Equal(tokenContent, result);
        Assert.Same(tokenContent, result); // Should be the same instance
    }
 
    [Fact]
    public void ToString_MultipleTokens_AllocatesNewString()
    {
        // This test verifies that when there are multiple tokens, a new string is allocated
 
        // Arrange - Create a node with multiple tokens
        var token1Content = "Hello";
        var token2Content = " World";
        
        var token1 = InternalSyntax.SyntaxFactory.Token(SyntaxKind.Text, token1Content);
        var child1 = InternalSyntax.SyntaxFactory.MarkupTextLiteral(token1);
 
        var token2 = InternalSyntax.SyntaxFactory.Token(SyntaxKind.Text, token2Content);
        var child2 = InternalSyntax.SyntaxFactory.MarkupTextLiteral(token2);
 
        var root = InternalSyntax.SyntaxFactory.GenericBlock([child1, child2]);
 
        // Act
        var result = root.ToString();
 
        // Assert
        Assert.Equal("Hello World", result);
        // Verify it's NOT the same instance as either token (new allocation)
        Assert.NotSame(token1Content, result);
        Assert.NotSame(token2Content, result);
    }
 
    [Fact]
    public void ToString_EmptyTokensInComplexTree_ReturnsEmptyString()
    {
        // Test zero width optimization with a more complex tree structure
 
        // Arrange - Create a tree with only empty tokens
        var emptyToken1 = InternalSyntax.SyntaxFactory.Token(SyntaxKind.Text, "");
        var child1 = InternalSyntax.SyntaxFactory.MarkupTextLiteral(emptyToken1);
 
        var emptyToken2 = InternalSyntax.SyntaxFactory.Token(SyntaxKind.Whitespace, "");
        var child2 = InternalSyntax.SyntaxFactory.MarkupTextLiteral(emptyToken2);
 
        var root = InternalSyntax.SyntaxFactory.GenericBlock([child1, child2]);
 
        // Act
        var result = root.ToString();
 
        // Assert
        Assert.Equal(string.Empty, result);
        Assert.Same(string.Empty, result);
        Assert.Equal(0, root.Width);
    }
 
    [Fact]
    public void ToString_SingleEmptyTokenInNode_ReturnsEmptyString()
    {
        // Test single token optimization when that token is empty
 
        // Arrange
        var emptyToken = InternalSyntax.SyntaxFactory.Token(SyntaxKind.Text, "");
        var node = InternalSyntax.SyntaxFactory.MarkupTextLiteral(emptyToken);
 
        // Act
        var result = node.ToString();
 
        // Assert
        Assert.Equal(string.Empty, result);
        Assert.Same(string.Empty, result); // Should return string.Empty directly
    }
 
    [Fact]
    public void ToString_SingleTokenWithLongContent_ReturnsTokenContent()
    {
        // Test single token optimization with longer content
 
        // Arrange
        var longContent = "This is a much longer piece of content that would normally require allocation but should be optimized";
        var token = InternalSyntax.SyntaxFactory.Token(SyntaxKind.Text, longContent);
        var node = InternalSyntax.SyntaxFactory.MarkupTextLiteral(token);
 
        // Act
        var result = node.ToString();
 
        // Assert
        Assert.Equal(longContent, result);
        Assert.Same(longContent, result); // Should be the same instance
    }
 
    [Fact]
    public void ToString_OptimizationPathVsAllocationPath_ProduceSameResult()
    {
        // Verify that both code paths produce the same result
 
        // Arrange - Single token (optimization path)
        var content = "Test Content";
        var singleToken = InternalSyntax.SyntaxFactory.Token(SyntaxKind.Text, content);
        var singleNode = InternalSyntax.SyntaxFactory.MarkupTextLiteral(singleToken);
 
        // Arrange - Multiple tokens that concatenate to same content (allocation path)
        var token1 = InternalSyntax.SyntaxFactory.Token(SyntaxKind.Text, "Test");
        var child1 = InternalSyntax.SyntaxFactory.MarkupTextLiteral(token1);
 
        var token2 = InternalSyntax.SyntaxFactory.Token(SyntaxKind.Text, " ");
        var child2 = InternalSyntax.SyntaxFactory.MarkupTextLiteral(token2);
 
        var token3 = InternalSyntax.SyntaxFactory.Token(SyntaxKind.Text, "Content");
        var child3 = InternalSyntax.SyntaxFactory.MarkupTextLiteral(token3);
 
        var multiNode = InternalSyntax.SyntaxFactory.GenericBlock([child1, child2, child3]);
 
        // Act
        var singleResult = singleNode.ToString();
        var multiResult = multiNode.ToString();
 
        // Assert
        Assert.Equal(singleResult, multiResult);
        Assert.Same(content, singleResult); // Single token should be optimized
        Assert.NotSame(content, multiResult); // Multiple tokens should allocate new string
    }
 
    [Fact]
    public void ToString_SingleTokenOptimization_SkipsZeroWidthTokens()
    {
        // This test verifies the optimization when there are zero-width tokens followed by a single non-zero-width token
        // The algorithm should skip the zero-width tokens and use the single token optimization for the non-zero-width token
 
        // Arrange - Create a tree with zero-width tokens followed by one non-zero-width token
        var zeroWidthToken1 = InternalSyntax.SyntaxFactory.Token(SyntaxKind.Text, "");
        var child1 = InternalSyntax.SyntaxFactory.MarkupTextLiteral(zeroWidthToken1);
 
        var zeroWidthToken2 = InternalSyntax.SyntaxFactory.Token(SyntaxKind.Whitespace, "");
        var child2 = InternalSyntax.SyntaxFactory.MarkupTextLiteral(zeroWidthToken2);
 
        var nonZeroTokenContent = "ActualContent";
        var nonZeroToken = InternalSyntax.SyntaxFactory.Token(SyntaxKind.Text, nonZeroTokenContent);
        var child3 = InternalSyntax.SyntaxFactory.MarkupTextLiteral(nonZeroToken);
 
        var root = InternalSyntax.SyntaxFactory.GenericBlock([child1, child2, child3]);
 
        // Act
        var result = root.ToString();
 
        // Assert
        Assert.Equal(nonZeroTokenContent, result);
        // Verify it uses the single token optimization (same string instance)
        Assert.Same(nonZeroTokenContent, result);
        Assert.Equal(nonZeroTokenContent.Length, root.Width);
    }
 
    [Fact]
    public void ToString_SingleTokenOptimization_WithMultipleZeroWidthTokensAndOneNonZero()
    {
        // Test edge case with many zero-width tokens and one content token
 
        // Arrange - Create multiple zero-width tokens followed by one with content
        var emptyToken1 = InternalSyntax.SyntaxFactory.Token(SyntaxKind.Text, "");
        var child1 = InternalSyntax.SyntaxFactory.MarkupTextLiteral(emptyToken1);
 
        var emptyToken2 = InternalSyntax.SyntaxFactory.Token(SyntaxKind.Whitespace, "");
        var child2 = InternalSyntax.SyntaxFactory.MarkupTextLiteral(emptyToken2);
 
        var emptyToken3 = InternalSyntax.SyntaxFactory.Token(SyntaxKind.Text, "");
        var child3 = InternalSyntax.SyntaxFactory.MarkupTextLiteral(emptyToken3);
 
        var contentTokenValue = "OnlyRealToken";
        var contentToken = InternalSyntax.SyntaxFactory.Token(SyntaxKind.Text, contentTokenValue);
        var child4 = InternalSyntax.SyntaxFactory.MarkupTextLiteral(contentToken);
 
        var root = InternalSyntax.SyntaxFactory.GenericBlock([child1, child2, child3, child4]);
 
        // Act
        var result = root.ToString();
 
        // Assert
        Assert.Equal(contentTokenValue, result);
        Assert.Same(contentTokenValue, result); // Should use single token optimization
    }
 
    [Fact]
    public void ToString_NoOptimization_WithZeroWidthTokenBetweenNonZeroTokens()
    {
        // Test that optimization is NOT used when there are multiple non-zero-width tokens,
        // even if separated by zero-width tokens
 
        // Arrange
        var token1Content = "First";
        var token1 = InternalSyntax.SyntaxFactory.Token(SyntaxKind.Text, token1Content);
        var child1 = InternalSyntax.SyntaxFactory.MarkupTextLiteral(token1);
 
        var zeroWidthToken = InternalSyntax.SyntaxFactory.Token(SyntaxKind.Text, "");
        var child2 = InternalSyntax.SyntaxFactory.MarkupTextLiteral(zeroWidthToken);
 
        var token3Content = "Second";
        var token3 = InternalSyntax.SyntaxFactory.Token(SyntaxKind.Text, token3Content);
        var child3 = InternalSyntax.SyntaxFactory.MarkupTextLiteral(token3);
 
        var root = InternalSyntax.SyntaxFactory.GenericBlock([child1, child2, child3]);
 
        // Act
        var result = root.ToString();
 
        // Assert
        Assert.Equal("FirstSecond", result);
        // Should NOT use optimization (new string allocation)
        Assert.NotSame(token1Content, result);
        Assert.NotSame(token3Content, result);
    }
 
    [Fact]
    public void ToString_SingleTokenOptimization_WithTrailingZeroWidthTokens()
    {
        // Test optimization when non-zero-width token is followed by zero-width tokens
 
        // Arrange
        var contentTokenValue = "MainContent";
        var contentToken = InternalSyntax.SyntaxFactory.Token(SyntaxKind.Text, contentTokenValue);
        var child1 = InternalSyntax.SyntaxFactory.MarkupTextLiteral(contentToken);
 
        var emptyToken1 = InternalSyntax.SyntaxFactory.Token(SyntaxKind.Text, "");
        var child2 = InternalSyntax.SyntaxFactory.MarkupTextLiteral(emptyToken1);
 
        var emptyToken2 = InternalSyntax.SyntaxFactory.Token(SyntaxKind.Whitespace, "");
        var child3 = InternalSyntax.SyntaxFactory.MarkupTextLiteral(emptyToken2);
 
        var root = InternalSyntax.SyntaxFactory.GenericBlock([child1, child2, child3]);
 
        // Act
        var result = root.ToString();
 
        // Assert
        Assert.Equal(contentTokenValue, result);
        Assert.Same(contentTokenValue, result); // Should use single token optimization
    }
}