|
// 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 Xunit;
namespace Microsoft.AspNetCore.Razor.Language.Legacy;
public class ParserHelpersTest
{
[Theory]
[InlineData("&", "&")]
[InlineData("<", "<")]
[InlineData(">", ">")]
[InlineData(""", "\"")]
[InlineData("'", "'")]
[InlineData(" ", "\u00A0")]
[InlineData("©", "\u00A9")]
[InlineData("®", "\u00AE")]
[InlineData("™", "\u2122")]
[InlineData("…", "\u2026")]
[InlineData("—", "\u2014")]
[InlineData("–", "\u2013")]
[InlineData("«", "\u00AB")]
[InlineData("»", "\u00BB")]
[InlineData("Á", "\u00C1")]
[InlineData("á", "\u00E1")]
[InlineData("Α", "\u0391")]
[InlineData("α", "\u03B1")]
public void TryGetHtmlEntity_ValidNamedEntities_ReturnsTrue(string input, string expectedReplacement)
{
// Arrange
var content = input.AsMemory();
// Act
var result = ParserHelpers.TryGetHtmlEntity(content, out var entity, out var replacement);
// Assert
Assert.True(result);
Assert.Equal(input, entity.ToString());
Assert.Equal(expectedReplacement, replacement);
}
[Theory]
[InlineData("A", "A")] // Basic decimal
[InlineData("a", "a")] // Basic decimal
[InlineData(" ", " ")] // Space character
[InlineData("©", "\u00A9")] // Copyright symbol
[InlineData("€", "\u20AC")] // Euro symbol
[InlineData("", "\uFFFF")] // Max BMP character
[InlineData("™", "\u2122")] // Trademark symbol
public void TryGetHtmlEntity_ValidDecimalEntities_ReturnsTrue(string input, string expectedReplacement)
{
// Arrange
var content = input.AsMemory();
// Act
var result = ParserHelpers.TryGetHtmlEntity(content, out var entity, out var replacement);
// Assert
Assert.True(result);
Assert.Equal(input, entity.ToString());
Assert.Equal(expectedReplacement, replacement);
}
[Theory]
[InlineData("A", "A")] // Basic hex lowercase
[InlineData("A", "A")] // Basic hex uppercase
[InlineData("a", "a")] // Basic hex lowercase
[InlineData("a", "a")] // Basic hex uppercase
[InlineData(" ", " ")] // Space character
[InlineData("©", "\u00A9")] // Copyright symbol
[InlineData("©", "\u00A9")] // Copyright symbol uppercase
[InlineData("€", "\u20AC")] // Euro symbol
[InlineData("", "\uFFFF")] // Max BMP character
public void TryGetHtmlEntity_ValidHexEntities_ReturnsTrue(string input, string expectedReplacement)
{
// Arrange
var content = input.AsMemory();
// Act
var result = ParserHelpers.TryGetHtmlEntity(content, out var entity, out var replacement);
// Assert
Assert.True(result);
Assert.Equal(input, entity.ToString());
Assert.Equal(expectedReplacement, replacement);
}
[Theory]
[InlineData("�x41;", "A")] // 0x prefix lowercase
[InlineData("�X41;", "A")] // 0x prefix uppercase
[InlineData("�x61;", "a")] // 0x prefix lowercase
[InlineData("�X61;", "a")] // 0x prefix uppercase
[InlineData("�x20;", " ")] // Space character
[InlineData("�xa9;", "\u00A9")] // Copyright symbol
[InlineData("�XA9;", "\u00A9")] // Copyright symbol uppercase
public void TryGetHtmlEntity_InvalidHexEntitiesWithZeroPrefix_ReturnsTrue(string input, string expectedReplacement)
{
// Arrange
var content = input.AsMemory();
// Act
var result = ParserHelpers.TryGetHtmlEntity(content, out var entity, out var replacement);
// Assert
Assert.True(result);
Assert.Equal(input, entity.ToString());
Assert.Equal(expectedReplacement, replacement);
}
[Theory]
[InlineData("&")] // Just ampersand
[InlineData("&")] // No semicolon
[InlineData("&invalid;")] // Invalid named entity
[InlineData("¬valid;")] // Invalid named entity
[InlineData("&xyz123;")] // Invalid named entity
[InlineData("&#;")] // Empty numeric entity
[InlineData("&#x;")] // Empty hex entity
[InlineData("�x;")] // Empty 0x entity
[InlineData("&#abc;")] // Invalid decimal digits
[InlineData("&#xghi;")] // Invalid hex digits
[InlineData("�xghi;")] // Invalid 0x hex digits
[InlineData("& extra")] // No semicolon with extra content
[InlineData("&am p;")] // Space in entity name
public void TryGetHtmlEntity_InvalidEntities_ReturnsFalse(string input)
{
// Arrange
var content = input.AsMemory();
// Act
var result = ParserHelpers.TryGetHtmlEntity(content, out var entity, out var replacement);
// Assert
Assert.False(result);
Assert.True(entity.IsEmpty);
Assert.Null(replacement);
}
[Theory]
[InlineData("�")] // Null character (below valid range)
[InlineData("")] // Control character (below valid range)
[InlineData("�")] // Surrogate range start (0xD800)
[InlineData("�")] // Surrogate range end (0xDFFF)
[InlineData("𐀀")] // Above BMP (0x10000)
[InlineData("")] // Max Unicode (0x10FFFF)
[InlineData("�")] // Above max Unicode
[InlineData("�")] // Hex null character
[InlineData("�")] // Hex surrogate start
[InlineData("�")] // Hex surrogate end
[InlineData("𐀀")] // Hex above BMP
public void TryGetHtmlEntity_OutOfRangeNumericEntities_ReturnsFalse(string input)
{
// Arrange
var content = input.AsMemory();
// Act
var result = ParserHelpers.TryGetHtmlEntity(content, out var entity, out var replacement);
// Assert
Assert.False(result);
Assert.True(entity.IsEmpty);
Assert.Null(replacement);
}
[Fact]
public void TryGetHtmlEntity_EmptyInput_ReturnsFalse()
{
// Arrange
var content = ReadOnlyMemory<char>.Empty;
// Act
var result = ParserHelpers.TryGetHtmlEntity(content, out var entity, out var replacement);
// Assert
Assert.False(result);
Assert.True(entity.IsEmpty);
Assert.Null(replacement);
}
[Theory]
[InlineData("a")] // Doesn't start with &
[InlineData("hello")] // Regular text
[InlineData("123")] // Numbers
[InlineData("")] // Empty string
public void TryGetHtmlEntity_NotStartingWithAmpersand_ReturnsFalse(string input)
{
// Arrange
var content = input.AsMemory();
// Act
var result = ParserHelpers.TryGetHtmlEntity(content, out var entity, out var replacement);
// Assert
Assert.False(result);
Assert.True(entity.IsEmpty);
Assert.Null(replacement);
}
[Theory]
[InlineData("&extra", "&", "&")]
[InlineData("<more", "<", "<")]
[InlineData("Atext", "A", "A")]
[InlineData("Amore", "A", "A")]
[InlineData("©right", "©", "\u00A9")]
public void TryGetHtmlEntity_EntityWithExtraContent_ParsesOnlyEntity(string input, string expectedEntity, string expectedReplacement)
{
// Arrange
var content = input.AsMemory();
// Act
var result = ParserHelpers.TryGetHtmlEntity(content, out var entity, out var replacement);
// Assert
Assert.True(result);
Assert.Equal(expectedEntity, entity.ToString());
Assert.Equal(expectedReplacement, replacement);
}
[Theory]
[InlineData("&")] // Valid entity name without semicolon
[InlineData("<")] // Valid entity name without semicolon
[InlineData(">")] // Valid entity name without semicolon
[InlineData("©")] // Valid entity name without semicolon
[InlineData("A")] // Valid numeric without semicolon
[InlineData("A")] // Valid hex without semicolon
public void TryGetHtmlEntity_ValidEntityWithoutSemicolon_ReturnsFalse(string input)
{
// Arrange
var content = input.AsMemory();
// Act
var result = ParserHelpers.TryGetHtmlEntity(content, out var entity, out var replacement);
// Assert
Assert.False(result);
Assert.True(entity.IsEmpty);
Assert.Null(replacement);
}
[Theory]
[InlineData("& ")] // Entity followed by space
[InlineData("&<")] // Multiple entities
[InlineData("&text")] // Entity followed by text
public void TryGetHtmlEntity_EntityFollowedByOtherCharacters_StopsAtSemicolon(string input)
{
// Arrange
var content = input.AsMemory();
// Act
var result = ParserHelpers.TryGetHtmlEntity(content, out var entity, out var replacement);
// Assert
Assert.True(result);
Assert.Equal("&", entity.ToString());
Assert.Equal("&", replacement);
}
[Theory]
[InlineData("&a;")] // Single character entity (invalid)
[InlineData("&ab;")] // Two character entity (invalid)
[InlineData("&123;")] // Numeric named entity (invalid)
[InlineData("&a1b;")] // Mixed alphanumeric (invalid)
public void TryGetHtmlEntity_ShortInvalidNamedEntities_ReturnsFalse(string input)
{
// Arrange
var content = input.AsMemory();
// Act
var result = ParserHelpers.TryGetHtmlEntity(content, out var entity, out var replacement);
// Assert
Assert.False(result);
Assert.True(entity.IsEmpty);
Assert.Null(replacement);
}
[Theory]
[InlineData("&#-1;")] // Negative number
[InlineData("&#x-1;")] // Negative hex
[InlineData("�x-1;")] // Negative 0x hex
[InlineData(".5;")] // Decimal point
[InlineData(".5;")] // Decimal point in hex
public void TryGetHtmlEntity_InvalidNumericFormats_ReturnsFalse(string input)
{
// Arrange
var content = input.AsMemory();
// Act
var result = ParserHelpers.TryGetHtmlEntity(content, out var entity, out var replacement);
// Assert
Assert.False(result);
Assert.True(entity.IsEmpty);
Assert.Null(replacement);
}
[Fact]
public void TryGetHtmlEntity_LongValidEntity_ReturnsTrue()
{
// Arrange
var input = "∳"; // One of the longer entity names
var content = input.AsMemory();
// Act
var result = ParserHelpers.TryGetHtmlEntity(content, out var entity, out var replacement);
// Assert
Assert.True(result);
Assert.Equal(input, entity.ToString());
Assert.Equal("\u2233", replacement);
}
[Theory]
// Decimal boundary tests
[InlineData("!", "!")] // Boundary: 0x21 (valid)
[InlineData(" ", " ")] // Boundary: 0x20 (valid - minimum valid)
[InlineData("")] // Boundary: 0x1F (invalid - just below minimum)
[InlineData("퟿", "\uD7FF")] // Boundary: 0xD7FF (valid, just before surrogate range)
[InlineData("�")] // Boundary: 0xD800 (invalid - surrogate range start)
[InlineData("�")] // Boundary: 0xDFFF (invalid - surrogate range end)
[InlineData("", "\uE000")] // Boundary: 0xE000 (valid, just after surrogate range)
[InlineData("", "\uFFFF")] // Boundary: 0xFFFF (valid - maximum valid)
[InlineData("𐀀")] // Boundary: 0x10000 (invalid - just above maximum)
// Hexadecimal boundary tests
[InlineData("!", "!")] // Boundary: 0x21 (valid)
[InlineData(" ", " ")] // Boundary: 0x20 (valid - minimum valid)
[InlineData("")] // Boundary: 0x1F (invalid - just below minimum)
[InlineData("퟿", "\uD7FF")] // Boundary: 0xD7FF (valid, just before surrogate range)
[InlineData("�")] // Boundary: 0xD800 (invalid - surrogate range start)
[InlineData("�")] // Boundary: 0xDFFF (invalid - surrogate range end)
[InlineData("", "\uE000")] // Boundary: 0xE000 (valid, just after surrogate range)
[InlineData("", "\uFFFF")] // Boundary: 0xFFFF (valid - maximum valid)
[InlineData("𐀀")] // Boundary: 0x10000 (invalid - just above maximum)
public void TryGetHtmlEntity_BoundaryValues_HandledCorrectly(string input, string? expectedReplacement = null)
{
// Arrange
var content = input.AsMemory();
var shouldSucceed = expectedReplacement != null;
// Act
var result = ParserHelpers.TryGetHtmlEntity(content, out var entity, out var replacement);
// Assert
if (shouldSucceed)
{
Assert.True(result);
Assert.Equal(input, entity.ToString());
Assert.Equal(expectedReplacement, replacement);
}
else
{
Assert.False(result);
Assert.True(entity.IsEmpty);
Assert.Null(replacement);
}
}
[Theory]
[InlineData("&", 5)] // Full entity
[InlineData("&extra", 5)] // Entity with extra content
[InlineData("A", 5)] // Numeric entity
[InlineData("A", 6)] // Hex entity
[InlineData("�x41;", 7)] // 0x hex entity
public void TryGetHtmlEntity_ReturnsCorrectEntityLength(string input, int expectedEntityLength)
{
// Arrange
var content = input.AsMemory();
// Act
var result = ParserHelpers.TryGetHtmlEntity(content, out var entity, out var replacement);
// Assert
Assert.True(result);
Assert.Equal(expectedEntityLength, entity.Length);
Assert.NotNull(replacement);
}
}
|