using Inspectron.Epson.Templates.Language; namespace Inspectron.Epson.Templates.Tests.Language; public class CommentTests { #region Single-line comments [Fact] public void Tokenize_SingleLineComment_ProducesNoTextTokens() { var lexer = new Lexer("@* This is a comment"); var result = lexer.Tokenize(); Assert.False(result.HasErrors); Assert.DoesNotContain(result.Tokens, t => t.Type == TokenType.Text); } [Fact] public void Tokenize_InlineSingleLineComment_ProducesNoTextTokens() { var lexer = new Lexer("@* This is a comment *@"); var result = lexer.Tokenize(); Assert.False(result.HasErrors); Assert.DoesNotContain(result.Tokens, t => t.Type == TokenType.Text); } [Fact] public void Tokenize_IndentedSingleLineComment_IsRecognizedAsComment() { var lexer = new Lexer(" @* indented comment"); var result = lexer.Tokenize(); Assert.False(result.HasErrors); Assert.DoesNotContain(result.Tokens, t => t.Type == TokenType.Text); } [Fact] public void Tokenize_TextBeforeAndAfterCommentLine_IsPreserved() { var lexer = new Lexer("Before\n@* comment\nAfter"); var result = lexer.Tokenize(); Assert.False(result.HasErrors); Assert.Contains(result.Tokens, t => t.Type == TokenType.Text && t.Value == "Before"); Assert.Contains(result.Tokens, t => t.Type == TokenType.Text && t.Value == "After"); Assert.DoesNotContain(result.Tokens, t => t.Type == TokenType.Text && t.Value.Contains("comment")); } [Fact] public void Tokenize_InlineCommentWithContentAfter_PreservesContentAfter() { var lexer = new Lexer("@* comment *@After"); var result = lexer.Tokenize(); Assert.False(result.HasErrors); Assert.Contains(result.Tokens, t => t.Type == TokenType.Text && t.Value == "After"); Assert.DoesNotContain(result.Tokens, t => t.Type == TokenType.Text && t.Value.Contains("comment")); } #endregion #region Multi-line comments [Fact] public void Tokenize_MultiLineComment_ProducesNoTextTokens() { var lexer = new Lexer("@*\nThis is multi-line\ncomment text\n*@"); var result = lexer.Tokenize(); Assert.False(result.HasErrors); Assert.DoesNotContain(result.Tokens, t => t.Type == TokenType.Text); } [Fact] public void Tokenize_MultiLineComment_ContentBeforeAndAfterPreserved() { var lexer = new Lexer("Before\n@*\ncomment\n*@\nAfter"); var result = lexer.Tokenize(); Assert.False(result.HasErrors); Assert.Contains(result.Tokens, t => t.Type == TokenType.Text && t.Value == "Before"); Assert.Contains(result.Tokens, t => t.Type == TokenType.Text && t.Value == "After"); Assert.DoesNotContain(result.Tokens, t => t.Type == TokenType.Text && t.Value.Contains("comment")); } [Fact] public void Tokenize_UnclosedMultiLineComment_ProducesError() { var lexer = new Lexer("@*\nThis comment is never closed"); var result = lexer.Tokenize(); Assert.True(result.HasErrors); Assert.Contains(result.Errors, e => e.Message.Contains("Unclosed multi-line comment")); } [Fact] public void Tokenize_MultiLineCommentWithContentAfterCloser_PreservesContent() { var lexer = new Lexer("@*\ncomment\n*@After"); var result = lexer.Tokenize(); Assert.False(result.HasErrors); Assert.Contains(result.Tokens, t => t.Type == TokenType.Text && t.Value == "After"); } #endregion #region Edge cases [Fact] public void Tokenize_StarSeparator_StillWorksNotConfusedWithComment() { var lexer = new Lexer("***"); var result = lexer.Tokenize(); Assert.False(result.HasErrors); Assert.Contains(result.Tokens, t => t.Type == TokenType.StarSeparator); } [Fact] public void Tokenize_DirectivesInsideComment_AreIgnored() { var lexer = new Lexer("@*\n@if condition\n@foreach item in items\n*@"); var result = lexer.Tokenize(); Assert.False(result.HasErrors); Assert.DoesNotContain(result.Tokens, t => t.Type == TokenType.If); Assert.DoesNotContain(result.Tokens, t => t.Type == TokenType.Foreach); } [Fact] public void Tokenize_BindingsInsideComment_AreIgnored() { var lexer = new Lexer("@* {PropertyName} *@"); var result = lexer.Tokenize(); Assert.False(result.HasErrors); Assert.DoesNotContain(result.Tokens, t => t.Type == TokenType.Binding); } [Fact] public void Tokenize_CommentWithMultipleStars_HandledCorrectly() { var lexer = new Lexer("@*** This has extra stars ***@"); var result = lexer.Tokenize(); Assert.False(result.HasErrors); Assert.DoesNotContain(result.Tokens, t => t.Type == TokenType.Text && t.Value.Contains("stars")); } [Fact] public void Tokenize_EmptyMultiLineComment_HandledCorrectly() { var lexer = new Lexer("@*\n*@"); var result = lexer.Tokenize(); Assert.False(result.HasErrors); Assert.DoesNotContain(result.Tokens, t => t.Type == TokenType.Text); } [Fact] public void Tokenize_CommentInComplexTemplate_WorksCorrectly() { var template = @"#bold,center# Title # @* This is a header comment *@ --- @if HasItems @* Loop through items *@ @foreach item in Items {item.Name} @end @end"; var lexer = new Lexer(template); var result = lexer.Tokenize(); Assert.False(result.HasErrors); Assert.Contains(result.Tokens, t => t.Type == TokenType.StyleStart); Assert.Contains(result.Tokens, t => t.Type == TokenType.DashSeparator); Assert.Contains(result.Tokens, t => t.Type == TokenType.If); Assert.Contains(result.Tokens, t => t.Type == TokenType.Foreach); Assert.DoesNotContain(result.Tokens, t => t.Type == TokenType.Text && t.Value.Contains("comment")); } [Fact] public void Tokenize_MultipleCommentsInTemplate_AllStripped() { var template = @"Line 1 @* Comment 1 *@ Line 2 @* Comment 2 still comment *@ Line 3"; var lexer = new Lexer(template); var result = lexer.Tokenize(); Assert.False(result.HasErrors); Assert.Contains(result.Tokens, t => t.Type == TokenType.Text && t.Value == "Line 1"); Assert.Contains(result.Tokens, t => t.Type == TokenType.Text && t.Value == "Line 2"); Assert.Contains(result.Tokens, t => t.Type == TokenType.Text && t.Value == "Line 3"); Assert.DoesNotContain(result.Tokens, t => t.Type == TokenType.Text && t.Value.Contains("Comment")); } [Fact] public void Tokenize_SeparatorInsideMultiLineComment_IsIgnored() { var lexer = new Lexer("@*\n---\n===\n***\n*@"); var result = lexer.Tokenize(); Assert.False(result.HasErrors); Assert.DoesNotContain(result.Tokens, t => t.Type == TokenType.DashSeparator); Assert.DoesNotContain(result.Tokens, t => t.Type == TokenType.EqualsSeparator); Assert.DoesNotContain(result.Tokens, t => t.Type == TokenType.StarSeparator); } #endregion #region Comment lines should not produce empty lines [Fact] public void Tokenize_ConsecutiveCommentLines_ProduceNoNewLineTokens() { var lexer = new Lexer("@**@\n@**@\n@**@"); var result = lexer.Tokenize(); Assert.False(result.HasErrors); // Should only have EOF token, no NewLine tokens for comment-only lines Assert.DoesNotContain(result.Tokens, t => t.Type == TokenType.NewLine); } [Fact] public void Tokenize_CommentLineBetweenContent_NoExtraNewLines() { var lexer = new Lexer("Line1\n@**@\nLine2"); var result = lexer.Tokenize(); Assert.False(result.HasErrors); // Should have exactly 2 NewLine tokens (after Line1 and after Line2) var newLineCount = result.Tokens.Count(t => t.Type == TokenType.NewLine); Assert.Equal(2, newLineCount); } [Fact] public void Tokenize_MultipleConsecutiveCommentLines_NoNewLines() { var lexer = new Lexer("Header\n@* comment 1 *@\n@* comment 2 *@\n@* comment 3 *@\nFooter"); var result = lexer.Tokenize(); Assert.False(result.HasErrors); Assert.Contains(result.Tokens, t => t.Type == TokenType.Text && t.Value == "Header"); Assert.Contains(result.Tokens, t => t.Type == TokenType.Text && t.Value == "Footer"); // Should have exactly 2 NewLine tokens (after Header and after Footer) var newLineCount = result.Tokens.Count(t => t.Type == TokenType.NewLine); Assert.Equal(2, newLineCount); } [Fact] public void Tokenize_EmptyLinesBetweenContent_ProducesNewLines() { var lexer = new Lexer("Line1\n\n\nLine2"); var result = lexer.Tokenize(); Assert.False(result.HasErrors); // Should have 4 NewLine tokens (after Line1, after each empty line, after Line2) var newLineCount = result.Tokens.Count(t => t.Type == TokenType.NewLine); Assert.Equal(4, newLineCount); } [Fact] public void Tokenize_EmptyLinesPreserved_CommentsRemoved() { var lexer = new Lexer("Header\n\n@* comment *@\n\nFooter"); var result = lexer.Tokenize(); Assert.False(result.HasErrors); // Should have 4 NewLine tokens: after Header, after first empty line, after second empty line, after Footer // The comment line should NOT produce a NewLine var newLineCount = result.Tokens.Count(t => t.Type == TokenType.NewLine); Assert.Equal(4, newLineCount); } #endregion }