// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Text; using Xunit; namespace Microsoft.AspNetCore.Razor.Language { public class StringSourceDocumentTest { [Fact] public void GetChecksum_ReturnsCopiedChecksum() { // Arrange var content = "Hello World!"; var document = new StringSourceDocument(content, Encoding.UTF8, "file.cshtml"); // Act var firstChecksum = document.GetChecksum(); var secondChecksum = document.GetChecksum(); // Assert Assert.Equal(firstChecksum, secondChecksum); Assert.NotSame(firstChecksum, secondChecksum); } [Fact] public void GetChecksum_ComputesCorrectChecksum_UTF8() { // Arrange var content = "Hello World!"; var document = new StringSourceDocument(content, Encoding.UTF8, "file.cshtml"); var expectedChecksum = new byte[] { 46, 247, 189, 230, 8, 206, 84, 4, 233, 125, 95, 4, 47, 149, 248, 159, 28, 35, 40, 113 }; // Act var checksum = document.GetChecksum(); // Assert Assert.Equal(expectedChecksum, checksum); } [Fact] public void GetChecksum_ComputesCorrectChecksum_UTF32() { // Arrange var content = "Hello World!"; var document = new StringSourceDocument(content, Encoding.UTF32, "file.cshtml"); var expectedChecksum = new byte[] { 8, 149, 159, 15, 242, 255, 115, 227, 219, 78, 61, 53, 127, 239, 77, 239, 215, 140, 248, 44 }; // Act var checksum = document.GetChecksum(); // Assert Assert.Equal(expectedChecksum, checksum); } [Fact] public void Indexer_ProvidesCharacterAccessToContent() { // Arrange var expectedContent = "Hello, World!"; var indexerBuffer = new char[expectedContent.Length]; var document = new StringSourceDocument(expectedContent, Encoding.UTF8, filePath: "file.cshtml"); // Act for (var i = 0; i < document.Length; i++) { indexerBuffer[i] = document[i]; } // Assert var output = new string(indexerBuffer); Assert.Equal(expectedContent, output); } [Fact] public void Length() { // Arrange var expectedContent = "Hello, World!"; var document = new StringSourceDocument(expectedContent, Encoding.UTF8, filePath: "file.cshtml"); // Act & Assert Assert.Equal(expectedContent.Length, document.Length); } [Fact] public void FilePath() { // Arrange var content = "Hello, World!"; // Act var document = new StringSourceDocument(content, Encoding.UTF8, filePath: "file.cshtml"); // Assert Assert.Equal("file.cshtml", document.FilePath); } [Fact] public void FilePath_Null() { // Arrange var content = "Hello, World!"; // Act var document = new StringSourceDocument(content, Encoding.UTF8, filePath: null); // Assert Assert.Null(document.FilePath); } [Fact] public void CopyTo_PartialCopyFromStart() { // Arrange var content = "Hello, World!"; var document = new StringSourceDocument(content, Encoding.UTF8, filePath: null); var expectedContent = "Hello"; var charBuffer = new char[expectedContent.Length]; // Act document.CopyTo(0, charBuffer, 0, expectedContent.Length); // Assert var copiedContent = new string(charBuffer); Assert.Equal(expectedContent, copiedContent); } [Fact] public void CopyTo_PartialCopyDestinationOffset() { // Arrange var content = "Hello, World!"; var document = new StringSourceDocument(content, Encoding.UTF8, filePath: null); var expectedContent = "$Hello"; var charBuffer = new char[expectedContent.Length]; charBuffer[0] = '$'; // Act document.CopyTo(0, charBuffer, 1, "Hello".Length); // Assert var copiedContent = new string(charBuffer); Assert.Equal(expectedContent, copiedContent); } [Fact] public void CopyTo_PartialCopySourceOffset() { // Arrange var content = "Hello, World!"; var document = new StringSourceDocument(content, Encoding.UTF8, filePath: null); var expectedContent = "World"; var charBuffer = new char[expectedContent.Length]; // Act document.CopyTo(7, charBuffer, 0, expectedContent.Length); // Assert var copiedContent = new string(charBuffer); Assert.Equal(expectedContent, copiedContent); } [Fact] public void CopyTo_WithEncoding() { // Arrange var content = "Hi"; var document = new StringSourceDocument(content, Encoding.UTF8, filePath: null); var charBuffer = new char[2]; // Act document.CopyTo(0, charBuffer, 0, 2); // Assert var copiedContent = new string(charBuffer); Assert.Equal("Hi", copiedContent); } [Fact] public void CopyTo_CanCopyMultipleTimes() { // Arrange var content = "Hi"; var document = new StringSourceDocument(content, Encoding.UTF8, filePath: null); // Act & Assert // // (we should be able to do this twice to prove that the underlying data isn't disposed) for (var i = 0; i < 2; i++) { var charBuffer = new char[2]; document.CopyTo(0, charBuffer, 0, 2); var copiedContent = new string(charBuffer); Assert.Equal("Hi", copiedContent); } } [Fact] public void Lines_Count_EmptyDocument() { // Arrange var content = string.Empty; var document = new StringSourceDocument(content, Encoding.UTF8, filePath: null); // Act var actual = document.Lines.Count; // Assert Assert.Equal(1, actual); } [Fact] public void Lines_GetLineLength_EmptyDocument() { // Arrange var content = string.Empty; var document = new StringSourceDocument(content, Encoding.UTF8, filePath: null); // Act var actual = document.Lines.GetLineLength(0); // Assert Assert.Equal(0, actual); } [Fact] public void Lines_GetLineLength_TrailingNewlineDoesNotStartNewLine() { // Arrange var content = "hello\n"; var document = new StringSourceDocument(content, Encoding.UTF8, filePath: null); // Act var actual = document.Lines.GetLineLength(0); // Assert Assert.Equal(6, actual); } [Fact] public void Lines_GetLineLength_TrailingNewlineDoesNotStartNewLine_CRLF() { // Arrange var content = "hello\r\n"; var document = new StringSourceDocument(content, Encoding.UTF8, filePath: null); // Act var actual = document.Lines.GetLineLength(0); // Assert Assert.Equal(7, actual); } [Fact] public void Lines_Simple_Document() { // Arrange var content = new StringBuilder() .Append("The quick brown").Append('\n') .Append("fox").Append("\r\n") .Append("jumps over the lazy dog.") .ToString(); var document = new StringSourceDocument(content, Encoding.UTF8, filePath: null); // Act var actual = GetAllSourceMappings(document); // Assert Assert.Equal(new int[] { 16, 5, 24 }, actual); } [Fact] public void Lines_CRLF_OnlyCountsAsASingleNewLine() { // Arrange var content = "Hello\r\nWorld!"; var document = new StringSourceDocument(content, Encoding.UTF8, filePath: null); // Act var actual = GetAllSourceMappings(document); // Assert Assert.Equal(new int[] { 7, 6 }, actual); } [Fact] public void Lines_CR_IsNewLine() { // Arrange var content = "Hello\rWorld!"; var document = new StringSourceDocument(content, Encoding.UTF8, filePath: null); // Act var actual = GetAllSourceMappings(document); // Assert Assert.Equal(new int[] { 6, 6 }, actual); } // CR handling is stateful in the parser, making sure we properly reset the state. [Fact] public void Lines_CR_IsNewLine_MultipleCRs() { // Arrange var content = "Hello\rBig\r\nWorld!"; var document = new StringSourceDocument(content, Encoding.UTF8, filePath: null); // Act var actual = GetAllSourceMappings(document); // Assert Assert.Equal(new int[] { 6, 5, 6 }, actual); } [Fact] public void Lines_LF_IsNewLine() { // Arrange var content = "Hello\nWorld!"; var document = new StringSourceDocument(content, Encoding.UTF8, filePath: null); // Act var actual = GetAllSourceMappings(document); // Assert Assert.Equal(new int[] { 6, 6 }, actual); } [Fact] public void Lines_Unicode0085_IsNewLine() { // Arrange var content = "Hello\u0085World!"; var document = new StringSourceDocument(content, Encoding.UTF8, filePath: null); // Act var actual = GetAllSourceMappings(document); // Assert Assert.Equal(new int[] { 6, 6 }, actual); } [Fact] public void Lines_Unicode2028_IsNewLine() { // Arrange var content = "Hello\u2028World!"; var document = new StringSourceDocument(content, Encoding.UTF8, filePath: null); // Act var actual = GetAllSourceMappings(document); // Assert Assert.Equal(new int[] { 6, 6 }, actual); } [Fact] public void Lines_Unicode2029_IsNewLine() { // Arrange var content = "Hello\u2029World!"; var document = new StringSourceDocument(content, Encoding.UTF8, filePath: null); // Act var actual = GetAllSourceMappings(document); // Assert Assert.Equal(new int[] { 6, 6 }, actual); } [Fact] public void Lines_GetLocation_IncludesAbsoluteIndexAndDocument() { // Arrange var content = "Hello, World!"; var document = new StringSourceDocument(content, Encoding.UTF8, filePath: "Hi.cshtml"); // Act var actual = document.Lines.GetLocation(1); // Assert Assert.Equal("Hi.cshtml", actual.FilePath); Assert.Equal(1, actual.AbsoluteIndex); } // Beginnings of lines are special because the BinarySearch in the implementation // will succeed. It's a different code path. [Fact] public void Lines_GetLocation_FirstCharacter() { // Arrange var content = "Hello\nBig\r\nWorld!"; var document = new StringSourceDocument(content, Encoding.UTF8, filePath: null); // Act var actual = document.Lines.GetLocation(0); // Assert Assert.Equal(0, actual.LineIndex); Assert.Equal(0, actual.CharacterIndex); } [Fact] public void Lines_GetLocation_EndOfFirstLine() { // Arrange var content = "Hello\nBig\r\nWorld!"; var document = new StringSourceDocument(content, Encoding.UTF8, filePath: null); // Act var actual = document.Lines.GetLocation(5); // Assert Assert.Equal(0, actual.LineIndex); Assert.Equal(5, actual.CharacterIndex); } [Fact] public void Lines_GetLocation_InteriorLine() { // Arrange var content = "Hello\nBig\r\nWorld!"; var document = new StringSourceDocument(content, Encoding.UTF8, filePath: null); // Act var actual = document.Lines.GetLocation(7); // Assert Assert.Equal(1, actual.LineIndex); Assert.Equal(1, actual.CharacterIndex); } [Fact] public void Lines_GetLocation_StartOfLastLine() { // Arrange var content = "Hello\nBig\r\nWorld!"; var document = new StringSourceDocument(content, Encoding.UTF8, filePath: null); // Act var actual = document.Lines.GetLocation(11); // Assert Assert.Equal(2, actual.LineIndex); Assert.Equal(0, actual.CharacterIndex); } [Fact] public void Lines_GetLocation_EndOfLastLine() { // Arrange var content = "Hello\nBig\r\nWorld!"; var document = new StringSourceDocument(content, Encoding.UTF8, filePath: null); // Act var actual = document.Lines.GetLocation(16); // Assert Assert.Equal(2, actual.LineIndex); Assert.Equal(5, actual.CharacterIndex); } private static int[] GetAllSourceMappings(RazorSourceDocument source) { var lines = new int[source.Lines.Count]; for (var i = 0; i < lines.Length; i++) { lines[i] = source.Lines.GetLineLength(i); } return lines; } } }