diff --git a/src/NetEvolve.CodeBuilder/CSharpCodeBuilder.Append.cs b/src/NetEvolve.CodeBuilder/CSharpCodeBuilder.Append.cs index b0c1117..2f198a9 100644 --- a/src/NetEvolve.CodeBuilder/CSharpCodeBuilder.Append.cs +++ b/src/NetEvolve.CodeBuilder/CSharpCodeBuilder.Append.cs @@ -197,7 +197,7 @@ public CSharpCodeBuilder Append(string? value) return this; } - if (value is "\n" or "\r") + if (value is "\n" or "\r" or "\r\n") { return AppendLine(); // Handle new line characters } diff --git a/src/NetEvolve.CodeBuilder/CSharpCodeBuilder.Documentation.cs b/src/NetEvolve.CodeBuilder/CSharpCodeBuilder.Documentation.cs index 3ba57a8..c889591 100644 --- a/src/NetEvolve.CodeBuilder/CSharpCodeBuilder.Documentation.cs +++ b/src/NetEvolve.CodeBuilder/CSharpCodeBuilder.Documentation.cs @@ -129,7 +129,8 @@ public CSharpCodeBuilder AppendXmlDocRemarks(string? remarks) return EnsureNewLineForXmlDoc() .AppendLine("/// ") - .AppendLine($"/// {remarks}") + .Append("/// ") + .AppendLine(remarks) .AppendLine("/// "); } @@ -176,6 +177,17 @@ public CSharpCodeBuilder AppendXmlDocException(string? exceptionType, string? de .AppendLine($"/// {description}"); } + /// + /// Appends an XML documentation exception element. + /// + /// The description of when the exception is thrown. + /// The type of exception that can be thrown. Must be a subclass of . + /// The current instance to allow for method chaining. + /// If the exception type or description is null or empty, the method returns without appending anything. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public CSharpCodeBuilder AppendXmlDocException(string? description) + where TException : Exception => AppendXmlDocException(typeof(TException).Name, description); + /// /// Appends multiple XML documentation exception elements. /// @@ -246,32 +258,34 @@ public CSharpCodeBuilder AppendXmlDocExample(IEnumerable? exampleLines) /// Appends an XML documentation see element for cross-references. /// /// The cross-reference to another member or type. + /// If set to true, uses 'href' instead of 'cref' for external links. /// The current instance to allow for method chaining. /// If the cref is null or empty, the method returns without appending anything. - public CSharpCodeBuilder AppendXmlDocSee(string? cref) + public CSharpCodeBuilder AppendXmlDocSee(string? cref, bool isHref = false) { if (string.IsNullOrEmpty(cref)) { return this; } - return EnsureNewLineForXmlDoc().AppendLine($"/// "); + return EnsureNewLineForXmlDoc().AppendLine($"/// "); } /// /// Appends an XML documentation seealso element for see-also references. /// /// The cross-reference to another member or type. + /// If set to true, uses 'href' instead of 'cref' for external links. /// The current instance to allow for method chaining. /// If the cref is null or empty, the method returns without appending anything. - public CSharpCodeBuilder AppendXmlDocSeeAlso(string? cref) + public CSharpCodeBuilder AppendXmlDocSeeAlso(string? cref, bool isHref = false) { if (string.IsNullOrEmpty(cref)) { return this; } - return EnsureNewLineForXmlDoc().AppendLine($"/// "); + return EnsureNewLineForXmlDoc().AppendLine($"/// "); } /// diff --git a/src/NetEvolve.CodeBuilder/CSharpCodeBuilder.Scope.cs b/src/NetEvolve.CodeBuilder/CSharpCodeBuilder.Scope.cs new file mode 100644 index 0000000..06cde62 --- /dev/null +++ b/src/NetEvolve.CodeBuilder/CSharpCodeBuilder.Scope.cs @@ -0,0 +1,91 @@ +namespace NetEvolve.CodeBuilder; + +using System; + +public partial record CSharpCodeBuilder +{ + /// + /// Creates a scope that automatically manages indentation levels. + /// + /// A that increments indentation on creation and decrements on disposal. + /// + /// The returned scope handler implements and is designed for use with + /// the 'using' statement. When the scope is created, indentation is incremented by one level. + /// When the scope is disposed (at the end of the using block), indentation is decremented. + /// + /// + /// + /// using (builder.Scope()) + /// { + /// builder.AppendLine("return true;"); + /// } + /// + /// + public IDisposable Scope() => new ScopeHandler(this); + + /// + /// A disposable struct that manages indentation scope for a . + /// + /// + /// This struct increments the indentation level when created and decrements it when disposed. + /// It is designed to work with the 'using' statement to provide automatic indentation management. + /// + private readonly struct ScopeHandler : IDisposable, IEquatable + { + private readonly CSharpCodeBuilder _builder; + + /// + /// Initializes a new instance of the struct and increments the indentation level. + /// + /// The instance to manage indentation for. + internal ScopeHandler(CSharpCodeBuilder builder) + { + _builder = builder; + _ = _builder.Append('{'); + } + + /// + /// Decrements the indentation level when the scope is disposed. + /// + /// + /// This method is called automatically when the 'using' statement block ends. + /// + public void Dispose() => _ = _builder.Append('}'); + + /// + /// Determines whether the specified object is equal to the current instance. + /// + /// The object to compare with the current instance. + /// Always returns false since ScopeHandler instances should not be compared. + public override readonly bool Equals(object? obj) => false; + + /// + /// Determines whether the specified ScopeHandler is equal to the current instance. + /// + /// The ScopeHandler to compare with the current instance. + /// Always returns false since ScopeHandler instances should not be compared. + public readonly bool Equals(ScopeHandler other) => false; + + /// + /// Returns the hash code for this instance. + /// + /// A hash code based on the internal builder reference. + public override readonly int GetHashCode() => _builder?.GetHashCode() ?? 0; + + /// + /// Determines whether two ScopeHandler instances are equal. + /// + /// The first instance to compare. + /// The second instance to compare. + /// Always returns false since ScopeHandler instances should not be compared. + public static bool operator ==(ScopeHandler _, ScopeHandler __) => false; + + /// + /// Determines whether two ScopeHandler instances are not equal. + /// + /// The first instance to compare. + /// The second instance to compare. + /// Always returns true since ScopeHandler instances should not be compared. + public static bool operator !=(ScopeHandler _, ScopeHandler __) => true; + } +} diff --git a/tests/NetEvolve.CodeBuilder.Tests.Integration/CSharpCodeBuilderTests.Formatting.cs b/tests/NetEvolve.CodeBuilder.Tests.Integration/CSharpCodeBuilderTests.Formatting.cs index d6994aa..08c0536 100644 --- a/tests/NetEvolve.CodeBuilder.Tests.Integration/CSharpCodeBuilderTests.Formatting.cs +++ b/tests/NetEvolve.CodeBuilder.Tests.Integration/CSharpCodeBuilderTests.Formatting.cs @@ -1,4 +1,4 @@ -namespace NetEvolve.CodeBuilder.Tests.Integration; +namespace NetEvolve.CodeBuilder.Tests.Integration; public partial class CSharpCodeBuilderTests { @@ -114,4 +114,30 @@ public async Task GenerateEnum_WithDocumentation_Should_ProduceCorrectOutput() _ = await Verify(result); } + + [Test] + public async Task Scopes_in_wild_nature() + { + var builder = new CSharpCodeBuilder() + .AppendLine("namespace Hello.World;") + .AppendLine() + .AppendXmlDocSummary("Represents the status of an order.") + .AppendLine("public enum Weekday"); + + using (builder.Scope()) + { + _ = builder + .AppendLine("Monday,") + .AppendLine("Tuesday,") + .AppendLine("Wednesday,") + .AppendLine("Thursday,") + .AppendLine("Friday,") + .AppendLine("Saturday,") + .AppendLine("Sunday,"); + } + + var result = builder.ToString(); + + _ = await Verify(result); + } } diff --git a/tests/NetEvolve.CodeBuilder.Tests.Integration/_snapshots/CSharpCodeBuilderTests.Scopes_in_wild_nature.verified.txt b/tests/NetEvolve.CodeBuilder.Tests.Integration/_snapshots/CSharpCodeBuilderTests.Scopes_in_wild_nature.verified.txt new file mode 100644 index 0000000..fe098fa --- /dev/null +++ b/tests/NetEvolve.CodeBuilder.Tests.Integration/_snapshots/CSharpCodeBuilderTests.Scopes_in_wild_nature.verified.txt @@ -0,0 +1,15 @@ +namespace Hello.World; + +/// +/// Represents the status of an order. +/// +public enum Weekday +{ + Monday, + Tuesday, + Wednesday, + Thursday, + Friday, + Saturday, + Sunday, +} diff --git a/tests/NetEvolve.CodeBuilder.Tests.Unit/CSharpCodeBuilderTests.Append.cs b/tests/NetEvolve.CodeBuilder.Tests.Unit/CSharpCodeBuilderTests.Append.cs index ce48611..282770b 100644 --- a/tests/NetEvolve.CodeBuilder.Tests.Unit/CSharpCodeBuilderTests.Append.cs +++ b/tests/NetEvolve.CodeBuilder.Tests.Unit/CSharpCodeBuilderTests.Append.cs @@ -112,7 +112,7 @@ public async Task Append_CharArray_Empty_Should_Not_Change_Builder() { var builder = new CSharpCodeBuilder(10); - _ = builder.Append([]); + _ = builder.Append((char[])[]); _ = await Assert.That(builder.ToString()).IsEqualTo(""); } @@ -244,4 +244,184 @@ public async Task Append_With_Capacity_Expansion_Should_Work_Correctly() _ = await Assert.That(builder.ToString()).IsEqualTo(longString); } + + // New tests for missing branches in String Append method + + [Test] + public async Task Append_String_NullChar_Should_Not_Change_Builder() + { + var builder = new CSharpCodeBuilder(); + + _ = builder.Append("\0"); + + _ = await Assert.That(builder.ToString()).IsEqualTo(string.Empty); + } + + [Test] + public async Task Append_String_NewLine_Should_Append_Line_Break() + { + var builder = new CSharpCodeBuilder(); + + _ = builder.Append("\n"); + + _ = await Assert.That(builder.ToString()).IsEqualTo(Environment.NewLine); + } + + [Test] + public async Task Append_String_CarriageReturn_Should_Append_Line_Break() + { + var builder = new CSharpCodeBuilder(); + + _ = builder.Append("\r"); + + _ = await Assert.That(builder.ToString()).IsEqualTo(Environment.NewLine); + } + + [Test] + public async Task Append_String_CRLF_Should_Append_Line_Break() + { + var builder = new CSharpCodeBuilder(); + + _ = builder.Append("\r\n"); + + _ = await Assert.That(builder.ToString()).IsEqualTo(Environment.NewLine); + } + + [Test] + public async Task Append_String_OpeningBrace_Should_Increment_Indent_And_Append_Line() + { + var builder = new CSharpCodeBuilder(); + + _ = builder.Append("{"); + + _ = await Assert.That(builder.ToString()).IsEqualTo("{" + Environment.NewLine); + + // Verify indent increased for next content + _ = builder.Append("test"); + + _ = await Assert.That(builder.ToString()).IsEqualTo("{" + Environment.NewLine + " test"); + } + + [Test] + public async Task Append_String_OpeningBracket_Should_Increment_Indent_And_Append_Line() + { + var builder = new CSharpCodeBuilder(); + + _ = builder.Append("["); + + _ = await Assert.That(builder.ToString()).IsEqualTo("[" + Environment.NewLine); + + // Verify indent increased for next content + _ = builder.Append("test"); + + _ = await Assert.That(builder.ToString()).IsEqualTo("[" + Environment.NewLine + " test"); + } + + [Test] + public async Task Append_String_ClosingBrace_Should_Decrement_Indent_And_Append_Line() + { + var builder = new CSharpCodeBuilder(); + + // First set indentation to 1 + _ = builder.Append("{").Append("test"); + + // Test closing brace + _ = builder.Append("}"); + + _ = await Assert + .That(builder.ToString()) + .IsEqualTo("{" + Environment.NewLine + " test" + Environment.NewLine + "}"); + + // Verify indent is decremented for next content + _ = builder.Append("after"); + + _ = await Assert + .That(builder.ToString()) + .IsEqualTo("{" + Environment.NewLine + " test" + Environment.NewLine + "}" + "after"); + } + + [Test] + public async Task Append_String_ClosingBracket_Should_Decrement_Indent_And_Append_Line() + { + var builder = new CSharpCodeBuilder(); + + // First set indentation to 1 + _ = builder.Append("[").Append("test"); + + // Test closing bracket + _ = builder.Append("]"); + + _ = await Assert + .That(builder.ToString()) + .IsEqualTo("[" + Environment.NewLine + " test" + Environment.NewLine + "]"); + + // Verify indent is decremented for next content + _ = builder.Append("after"); + + _ = await Assert + .That(builder.ToString()) + .IsEqualTo("[" + Environment.NewLine + " test" + Environment.NewLine + "]" + "after"); + } + + // New tests for missing branches in Char Append method + + [Test] + public async Task Append_Char_NullChar_Should_Not_Change_Builder() + { + var builder = new CSharpCodeBuilder(); + + _ = builder.Append('\0'); + + _ = await Assert.That(builder.ToString()).IsEqualTo(string.Empty); + } + + [Test] + public async Task Append_Char_NewLine_Should_Append_Line_Break() + { + var builder = new CSharpCodeBuilder(); + + _ = builder.Append('\n'); + + _ = await Assert.That(builder.ToString()).IsEqualTo(Environment.NewLine); + } + + [Test] + public async Task Append_Char_CarriageReturn_Should_Append_Line_Break() + { + var builder = new CSharpCodeBuilder(); + + _ = builder.Append('\r'); + + _ = await Assert.That(builder.ToString()).IsEqualTo(Environment.NewLine); + } + + [Test] + public async Task Append_Char_OpeningBrace_Should_Increment_Indent_And_Append_Line() + { + var builder = new CSharpCodeBuilder(); + + _ = builder.Append('{'); + + _ = await Assert.That(builder.ToString()).IsEqualTo("{" + Environment.NewLine); + + // Verify indent increased for next content + _ = builder.Append("test"); + + _ = await Assert.That(builder.ToString()).IsEqualTo("{" + Environment.NewLine + " test"); + } + + [Test] + public async Task Append_Char_OpeningBracket_Should_Increment_Indent_And_Append_Line() + { + var builder = new CSharpCodeBuilder(); + + _ = builder.Append('['); + + _ = await Assert.That(builder.ToString()).IsEqualTo("[" + Environment.NewLine); + + // Verify indent increased for next content + _ = builder.Append("test"); + + _ = await Assert.That(builder.ToString()).IsEqualTo("[" + Environment.NewLine + " test"); + } } diff --git a/tests/NetEvolve.CodeBuilder.Tests.Unit/CSharpCodeBuilderTests.Documentation.cs b/tests/NetEvolve.CodeBuilder.Tests.Unit/CSharpCodeBuilderTests.Documentation.cs index bbcf1bf..d5e2b54 100644 --- a/tests/NetEvolve.CodeBuilder.Tests.Unit/CSharpCodeBuilderTests.Documentation.cs +++ b/tests/NetEvolve.CodeBuilder.Tests.Unit/CSharpCodeBuilderTests.Documentation.cs @@ -44,7 +44,7 @@ public async Task AppendXmlDocSummary_WithSingleLine_Should_AppendSummaryElement { var builder = new CSharpCodeBuilder(); - var result = builder.AppendXmlDocSummary("This is a summary"); + var result = builder.AppendXmlDocSummary("This is a summary").ToString(); var expected = "/// " @@ -54,8 +54,7 @@ public async Task AppendXmlDocSummary_WithSingleLine_Should_AppendSummaryElement + "/// " + Environment.NewLine; - _ = await Assert.That(result).IsEqualTo(builder); - _ = await Assert.That(builder.ToString()).IsEqualTo(expected); + _ = await Assert.That(result).IsEqualTo(expected); } [Test] @@ -64,7 +63,7 @@ public async Task AppendXmlDocSummary_WithMultipleLines_Should_AppendSummaryElem var builder = new CSharpCodeBuilder(); var summaryLines = new[] { "First line", "Second line", "Third line" }; - var result = builder.AppendXmlDocSummary(summaryLines); + var result = builder.AppendXmlDocSummary(summaryLines).ToString(); var expected = "/// " @@ -78,8 +77,7 @@ public async Task AppendXmlDocSummary_WithMultipleLines_Should_AppendSummaryElem + "/// " + Environment.NewLine; - _ = await Assert.That(result).IsEqualTo(builder); - _ = await Assert.That(builder.ToString()).IsEqualTo(expected); + _ = await Assert.That(result).IsEqualTo(expected); } [Test] @@ -88,7 +86,7 @@ public async Task AppendXmlDocSummary_WithEmptyAndNullLines_Should_SkipEmptyLine var builder = new CSharpCodeBuilder(); var summaryLines = new List { "First line", "", null, "Last line" }; - var result = builder.AppendXmlDocSummary(summaryLines.Where(x => x is not null)!); + var result = builder.AppendXmlDocSummary(summaryLines.Where(x => x is not null)!).ToString(); var expected = "/// " @@ -100,8 +98,7 @@ public async Task AppendXmlDocSummary_WithEmptyAndNullLines_Should_SkipEmptyLine + "/// " + Environment.NewLine; - _ = await Assert.That(result).IsEqualTo(builder); - _ = await Assert.That(builder.ToString()).IsEqualTo(expected); + _ = await Assert.That(result).IsEqualTo(expected); } [Test] @@ -138,7 +135,7 @@ public async Task AppendXmlDocParams_WithMultipleParameters_Should_AppendAllPara ("param2", "Second parameter"), }; - var result = builder.AppendXmlDocParams(parameters); + var result = builder.AppendXmlDocParams(parameters).ToString(); var expected = "/// First parameter" @@ -146,8 +143,7 @@ public async Task AppendXmlDocParams_WithMultipleParameters_Should_AppendAllPara + "/// Second parameter" + Environment.NewLine; - _ = await Assert.That(result).IsEqualTo(builder); - _ = await Assert.That(builder.ToString()).IsEqualTo(expected); + _ = await Assert.That(result).IsEqualTo(expected); } [Test] @@ -168,7 +164,7 @@ public async Task AppendXmlDocRemarks_WithSingleLine_Should_AppendRemarksElement { var builder = new CSharpCodeBuilder(); - var result = builder.AppendXmlDocRemarks("This is a remark"); + var result = builder.AppendXmlDocRemarks("This is a remark").ToString(); var expected = "/// " @@ -178,8 +174,7 @@ public async Task AppendXmlDocRemarks_WithSingleLine_Should_AppendRemarksElement + "/// " + Environment.NewLine; - _ = await Assert.That(result).IsEqualTo(builder); - _ = await Assert.That(builder.ToString()).IsEqualTo(expected); + _ = await Assert.That(result).IsEqualTo(expected); } [Test] @@ -188,7 +183,7 @@ public async Task AppendXmlDocRemarks_WithMultipleLines_Should_AppendRemarksElem var builder = new CSharpCodeBuilder(); var remarksLines = new[] { "First remark line", "Second remark line", "Third remark line" }; - var result = builder.AppendXmlDocRemarks(remarksLines); + var result = builder.AppendXmlDocRemarks(remarksLines).ToString(); var expected = "/// " @@ -202,8 +197,7 @@ public async Task AppendXmlDocRemarks_WithMultipleLines_Should_AppendRemarksElem + "/// " + Environment.NewLine; - _ = await Assert.That(result).IsEqualTo(builder); - _ = await Assert.That(builder.ToString()).IsEqualTo(expected); + _ = await Assert.That(result).IsEqualTo(expected); } [Test] @@ -212,7 +206,7 @@ public async Task AppendXmlDocRemarks_WithEmptyAndNullLines_Should_SkipEmptyLine var builder = new CSharpCodeBuilder(); var remarksLines = new[] { "First remark", "", "Third remark" }; - var result = builder.AppendXmlDocRemarks(remarksLines); + var result = builder.AppendXmlDocRemarks(remarksLines).ToString(); var expected = "/// " @@ -224,8 +218,7 @@ public async Task AppendXmlDocRemarks_WithEmptyAndNullLines_Should_SkipEmptyLine + "/// " + Environment.NewLine; - _ = await Assert.That(result).IsEqualTo(builder); - _ = await Assert.That(builder.ToString()).IsEqualTo(expected); + _ = await Assert.That(result).IsEqualTo(expected); } [Test] @@ -234,14 +227,13 @@ public async Task AppendXmlDocRemarks_WithOnlyEmptyLines_Should_AppendOnlyOpenin var builder = new CSharpCodeBuilder(); var remarksLines = new[] { "", "", "" }; - var result = builder.AppendXmlDocRemarks(remarksLines); + var result = builder.AppendXmlDocRemarks(remarksLines).ToString(); // The current implementation has a bug where it opens the tag but doesn't close it // when there are no valid content lines var expected = "/// " + Environment.NewLine; - _ = await Assert.That(result).IsEqualTo(builder); - _ = await Assert.That(builder.ToString()).IsEqualTo(expected); + _ = await Assert.That(result).IsEqualTo(expected); } [Test] @@ -249,10 +241,9 @@ public async Task AppendXmlDocRemarks_WithNullCollection_Should_NotAppendAnythin { var builder = new CSharpCodeBuilder(); - var result = builder.AppendXmlDocRemarks((IEnumerable?)null); + var result = builder.AppendXmlDocRemarks((IEnumerable?)null).ToString(); - _ = await Assert.That(result).IsEqualTo(builder); - _ = await Assert.That(builder.ToString()).IsEqualTo(string.Empty); + _ = await Assert.That(result).IsEqualTo(string.Empty); } [Test] @@ -261,36 +252,13 @@ public async Task AppendXmlDocRemarks_WithEmptyCollection_Should_AppendOnlyOpeni var builder = new CSharpCodeBuilder(); var remarksLines = Array.Empty(); - var result = builder.AppendXmlDocRemarks(remarksLines); + var result = builder.AppendXmlDocRemarks(remarksLines).ToString(); // The current implementation has a bug where it opens the tag but doesn't close it // when there are no valid content lines var expected = "/// " + Environment.NewLine; - _ = await Assert.That(result).IsEqualTo(builder); - _ = await Assert.That(builder.ToString()).IsEqualTo(expected); - } - - [Test] - public async Task AppendXmlDocRemarks_WithNullString_Should_NotAppendAnything() - { - var builder = new CSharpCodeBuilder(); - - var result = builder.AppendXmlDocRemarks((string?)null); - - _ = await Assert.That(result).IsEqualTo(builder); - _ = await Assert.That(builder.ToString()).IsEqualTo(string.Empty); - } - - [Test] - public async Task AppendXmlDocRemarks_WithEmptyString_Should_NotAppendAnything() - { - var builder = new CSharpCodeBuilder(); - - var result = builder.AppendXmlDocRemarks(""); - - _ = await Assert.That(result).IsEqualTo(builder); - _ = await Assert.That(builder.ToString()).IsEqualTo(string.Empty); + _ = await Assert.That(result).IsEqualTo(expected); } [Test] @@ -298,7 +266,7 @@ public async Task AppendXmlDocExample_WithContent_Should_AppendExampleElement() { var builder = new CSharpCodeBuilder(); - var result = builder.AppendXmlDocExample("var example = new Example();"); + var result = builder.AppendXmlDocExample("var example = new Example();").ToString(); var expected = "/// " @@ -308,8 +276,7 @@ public async Task AppendXmlDocExample_WithContent_Should_AppendExampleElement() + "/// " + Environment.NewLine; - _ = await Assert.That(result).IsEqualTo(builder); - _ = await Assert.That(builder.ToString()).IsEqualTo(expected); + _ = await Assert.That(result).IsEqualTo(expected); } [Test] @@ -323,7 +290,7 @@ public async Task AppendXmlDocExample_WithMultipleLines_Should_AppendExampleElem "var result = builder.ToString();", }; - var result = builder.AppendXmlDocExample(exampleLines); + var result = builder.AppendXmlDocExample(exampleLines).ToString(); var expected = "/// " @@ -337,8 +304,7 @@ public async Task AppendXmlDocExample_WithMultipleLines_Should_AppendExampleElem + "/// " + Environment.NewLine; - _ = await Assert.That(result).IsEqualTo(builder); - _ = await Assert.That(builder.ToString()).IsEqualTo(expected); + _ = await Assert.That(result).IsEqualTo(expected); } [Test] @@ -347,7 +313,7 @@ public async Task AppendXmlDocExample_WithEmptyAndNullLines_Should_SkipEmptyLine var builder = new CSharpCodeBuilder(); var exampleLines = new[] { "var x = 1;", "", "var y = 2;" }; - var result = builder.AppendXmlDocExample(exampleLines); + var result = builder.AppendXmlDocExample(exampleLines).ToString(); var expected = "/// " @@ -359,62 +325,7 @@ public async Task AppendXmlDocExample_WithEmptyAndNullLines_Should_SkipEmptyLine + "/// " + Environment.NewLine; - _ = await Assert.That(result).IsEqualTo(builder); - _ = await Assert.That(builder.ToString()).IsEqualTo(expected); - } - - [Test] - public async Task AppendXmlDocExample_WithOnlyEmptyLines_Should_AppendOnlyOpeningTag() - { - var builder = new CSharpCodeBuilder(); - var exampleLines = new[] { "", "", "" }; - - var result = builder.AppendXmlDocExample(exampleLines); - - // The current implementation has a bug where it opens the tag but doesn't close it - // when there are no valid content lines - var expected = "/// " + Environment.NewLine; - - _ = await Assert.That(result).IsEqualTo(builder); - _ = await Assert.That(builder.ToString()).IsEqualTo(expected); - } - - [Test] - public async Task AppendXmlDocExample_WithEmptyCollection_Should_AppendOnlyOpeningTag() - { - var builder = new CSharpCodeBuilder(); - var exampleLines = Array.Empty(); - - var result = builder.AppendXmlDocExample(exampleLines); - - // The current implementation has a bug where it opens the tag but doesn't close it - // when there are no valid content lines - var expected = "/// " + Environment.NewLine; - - _ = await Assert.That(result).IsEqualTo(builder); - _ = await Assert.That(builder.ToString()).IsEqualTo(expected); - } - - [Test] - public async Task AppendXmlDocExample_WithNullString_Should_NotAppendAnything() - { - var builder = new CSharpCodeBuilder(); - - var result = builder.AppendXmlDocExample((string?)null); - - _ = await Assert.That(result).IsEqualTo(builder); - _ = await Assert.That(builder.ToString()).IsEqualTo(string.Empty); - } - - [Test] - public async Task AppendXmlDocExample_WithEmptyString_Should_NotAppendAnything() - { - var builder = new CSharpCodeBuilder(); - - var result = builder.AppendXmlDocExample(""); - - _ = await Assert.That(result).IsEqualTo(builder); - _ = await Assert.That(builder.ToString()).IsEqualTo(string.Empty); + _ = await Assert.That(result).IsEqualTo(expected); } [Test] @@ -428,19 +339,6 @@ public async Task AppendXmlDocSee_WithCref_Should_AppendSeeElement() _ = await Assert.That(builder.ToString()).IsEqualTo("/// " + Environment.NewLine); } - [Test] - public async Task AppendXmlDocSeeAlso_WithCref_Should_AppendSeeAlsoElement() - { - var builder = new CSharpCodeBuilder(); - - var result = builder.AppendXmlDocSeeAlso("System.Object"); - - _ = await Assert.That(result).IsEqualTo(builder); - _ = await Assert - .That(builder.ToString()) - .IsEqualTo("/// " + Environment.NewLine); - } - [Test] public async Task AppendXmlDocValue_WithDescription_Should_AppendValueElement() { @@ -472,10 +370,9 @@ public async Task AppendXmlDocInheritDoc_WithoutCref_Should_AppendInheritDocElem { var builder = new CSharpCodeBuilder(); - var result = builder.AppendXmlDocInheritDoc(); + var result = builder.AppendXmlDocInheritDoc().ToString(); - _ = await Assert.That(result).IsEqualTo(builder); - _ = await Assert.That(builder.ToString()).IsEqualTo("/// " + Environment.NewLine); + _ = await Assert.That(result).IsEqualTo("/// " + Environment.NewLine); } [Test] @@ -483,12 +380,9 @@ public async Task AppendXmlDocInheritDoc_WithCref_Should_AppendInheritDocElement { var builder = new CSharpCodeBuilder(); - var result = builder.AppendXmlDocInheritDoc("BaseClass.Method"); + var result = builder.AppendXmlDocInheritDoc("BaseClass.Method").ToString(); - _ = await Assert.That(result).IsEqualTo(builder); - _ = await Assert - .That(builder.ToString()) - .IsEqualTo("/// " + Environment.NewLine); + _ = await Assert.That(result).IsEqualTo("/// " + Environment.NewLine); } [Test] @@ -496,11 +390,10 @@ public async Task AppendXmlDocCustomElement_WithContentAndAttributes_Should_Appe { var builder = new CSharpCodeBuilder(); - var result = builder.AppendXmlDocCustomElement("custom", "Custom content", "attr=\"value\""); + var result = builder.AppendXmlDocCustomElement("custom", "Custom content", "attr=\"value\"").ToString(); - _ = await Assert.That(result).IsEqualTo(builder); _ = await Assert - .That(builder.ToString()) + .That(result) .IsEqualTo("/// Custom content" + Environment.NewLine); } @@ -509,10 +402,9 @@ public async Task AppendXmlDocCustomElement_WithoutContent_Should_AppendSelfClos { var builder = new CSharpCodeBuilder(); - var result = builder.AppendXmlDocCustomElement("custom", null, "attr=\"value\""); + var result = builder.AppendXmlDocCustomElement("custom", null, "attr=\"value\"").ToString(); - _ = await Assert.That(result).IsEqualTo(builder); - _ = await Assert.That(builder.ToString()).IsEqualTo("/// " + Environment.NewLine); + _ = await Assert.That(result).IsEqualTo("/// " + Environment.NewLine); } [Test] @@ -525,7 +417,8 @@ public async Task XmlDocumentationMethods_Should_SupportMethodChaining() .AppendXmlDocParam("param1", "First parameter") .AppendXmlDocParam("param2", "Second parameter") .AppendXmlDocReturns("Return value description") - .AppendXmlDocRemarks("Additional remarks"); + .AppendXmlDocRemarks("Additional remarks") + .ToString(); var expected = "/// " @@ -547,8 +440,7 @@ public async Task XmlDocumentationMethods_Should_SupportMethodChaining() + "/// " + Environment.NewLine; - _ = await Assert.That(result).IsEqualTo(builder); - _ = await Assert.That(builder.ToString()).IsEqualTo(expected); + _ = await Assert.That(result).IsEqualTo(expected); } [Test] @@ -559,7 +451,8 @@ public async Task XmlDocumentationMethods_WithIndentation_Should_RespectIndentat var result = builder .AppendXmlDocSummary("Indented summary") - .AppendXmlDocParam("param", "Parameter description"); + .AppendXmlDocParam("param", "Parameter description") + .ToString(); var expected = " /// " @@ -571,8 +464,7 @@ public async Task XmlDocumentationMethods_WithIndentation_Should_RespectIndentat + " /// Parameter description" + Environment.NewLine; - _ = await Assert.That(result).IsEqualTo(builder); - _ = await Assert.That(builder.ToString()).IsEqualTo(expected); + _ = await Assert.That(result).IsEqualTo(expected); } [Test] @@ -584,7 +476,8 @@ public async Task XmlDocumentationMethods_AfterContent_Should_StartOnNewLine() .Append("public class MyClass") .AppendXmlDocSummary("Method summary") .AppendXmlDocParam("param", "Parameter description") - .Append("public void MyMethod(string param) { }"); + .Append("public void MyMethod(string param) { }") + .ToString(); var expected = "public class MyClass" @@ -599,8 +492,7 @@ public async Task XmlDocumentationMethods_AfterContent_Should_StartOnNewLine() + Environment.NewLine + "public void MyMethod(string param) { }"; - _ = await Assert.That(result).IsEqualTo(builder); - _ = await Assert.That(builder.ToString()).IsEqualTo(expected); + _ = await Assert.That(result).IsEqualTo(expected); } [Test] @@ -608,7 +500,10 @@ public async Task XmlDocumentationMethods_AtStartOfBuilder_Should_NotAddExtraNew { var builder = new CSharpCodeBuilder(); - var result = builder.AppendXmlDocSummary("Method summary").AppendXmlDocParam("param", "Parameter description"); + var result = builder + .AppendXmlDocSummary("Method summary") + .AppendXmlDocParam("param", "Parameter description") + .ToString(); var expected = "/// " @@ -620,8 +515,7 @@ public async Task XmlDocumentationMethods_AtStartOfBuilder_Should_NotAddExtraNew + "/// Parameter description" + Environment.NewLine; - _ = await Assert.That(result).IsEqualTo(builder); - _ = await Assert.That(builder.ToString()).IsEqualTo(expected); + _ = await Assert.That(result).IsEqualTo(expected); } [Test] @@ -632,7 +526,8 @@ public async Task XmlDocumentationMethods_AfterNewLine_Should_NotAddExtraNewLine var result = builder .AppendLine("public class MyClass") .AppendXmlDocSummary("Method summary") - .AppendXmlDocParam("param", "Parameter description"); + .AppendXmlDocParam("param", "Parameter description") + .ToString(); var expected = "public class MyClass" @@ -646,8 +541,7 @@ public async Task XmlDocumentationMethods_AfterNewLine_Should_NotAddExtraNewLine + "/// Parameter description" + Environment.NewLine; - _ = await Assert.That(result).IsEqualTo(builder); - _ = await Assert.That(builder.ToString()).IsEqualTo(expected); + _ = await Assert.That(result).IsEqualTo(expected); } [Test] @@ -655,11 +549,10 @@ public async Task AppendXmlDocException_WithValidParameters_Should_AppendExcepti { var builder = new CSharpCodeBuilder(); - var result = builder.AppendXmlDocException("ArgumentNullException", "Thrown when argument is null"); + var result = builder.AppendXmlDocException("ArgumentNullException", "Thrown when argument is null").ToString(); - _ = await Assert.That(result).IsEqualTo(builder); _ = await Assert - .That(builder.ToString()) + .That(result) .IsEqualTo( "/// Thrown when argument is null" + Environment.NewLine @@ -671,10 +564,9 @@ public async Task AppendXmlDocException_WithNullExceptionType_Should_NotAppendAn { var builder = new CSharpCodeBuilder(); - var result = builder.AppendXmlDocException(null, "Exception description"); + var result = builder.AppendXmlDocException(null, "Exception description").ToString(); - _ = await Assert.That(result).IsEqualTo(builder); - _ = await Assert.That(builder.ToString()).IsEqualTo(string.Empty); + _ = await Assert.That(result).IsEqualTo(string.Empty); } [Test] @@ -682,10 +574,9 @@ public async Task AppendXmlDocException_WithEmptyExceptionType_Should_NotAppendA { var builder = new CSharpCodeBuilder(); - var result = builder.AppendXmlDocException("", "Exception description"); + var result = builder.AppendXmlDocException("", "Exception description").ToString(); - _ = await Assert.That(result).IsEqualTo(builder); - _ = await Assert.That(builder.ToString()).IsEqualTo(string.Empty); + _ = await Assert.That(result).IsEqualTo(string.Empty); } [Test] @@ -693,10 +584,9 @@ public async Task AppendXmlDocException_WithNullDescription_Should_NotAppendAnyt { var builder = new CSharpCodeBuilder(); - var result = builder.AppendXmlDocException("ArgumentNullException", null); + var result = builder.AppendXmlDocException("ArgumentNullException", null).ToString(); - _ = await Assert.That(result).IsEqualTo(builder); - _ = await Assert.That(builder.ToString()).IsEqualTo(string.Empty); + _ = await Assert.That(result).IsEqualTo(string.Empty); } [Test] @@ -704,21 +594,9 @@ public async Task AppendXmlDocException_WithEmptyDescription_Should_NotAppendAny { var builder = new CSharpCodeBuilder(); - var result = builder.AppendXmlDocException("ArgumentNullException", ""); + var result = builder.AppendXmlDocException("ArgumentNullException", "").ToString(); - _ = await Assert.That(result).IsEqualTo(builder); - _ = await Assert.That(builder.ToString()).IsEqualTo(string.Empty); - } - - [Test] - public async Task AppendXmlDocException_WithBothNullParameters_Should_NotAppendAnything() - { - var builder = new CSharpCodeBuilder(); - - var result = builder.AppendXmlDocException(null, null); - - _ = await Assert.That(result).IsEqualTo(builder); - _ = await Assert.That(builder.ToString()).IsEqualTo(string.Empty); + _ = await Assert.That(result).IsEqualTo(string.Empty); } [Test] @@ -726,11 +604,12 @@ public async Task AppendXmlDocException_WithGenericExceptionType_Should_AppendCo { var builder = new CSharpCodeBuilder(); - var result = builder.AppendXmlDocException("InvalidOperationException", "Thrown when the operation is invalid"); + var result = builder + .AppendXmlDocException("Thrown when the operation is invalid") + .ToString(); - _ = await Assert.That(result).IsEqualTo(builder); _ = await Assert - .That(builder.ToString()) + .That(result) .IsEqualTo( "/// Thrown when the operation is invalid" + Environment.NewLine @@ -742,14 +621,12 @@ public async Task AppendXmlDocException_WithFullyQualifiedType_Should_AppendCorr { var builder = new CSharpCodeBuilder(); - var result = builder.AppendXmlDocException( - "System.ArgumentOutOfRangeException", - "Thrown when the value is out of range" - ); + var result = builder + .AppendXmlDocException("System.ArgumentOutOfRangeException", "Thrown when the value is out of range") + .ToString(); - _ = await Assert.That(result).IsEqualTo(builder); _ = await Assert - .That(builder.ToString()) + .That(result) .IsEqualTo( "/// Thrown when the value is out of range" + Environment.NewLine @@ -767,7 +644,7 @@ public async Task AppendXmlDocExceptions_WithValidExceptions_Should_AppendAllExc ("ArgumentOutOfRangeException", "Thrown when value is out of range"), }; - var result = builder.AppendXmlDocExceptions(exceptions); + var result = builder.AppendXmlDocExceptions(exceptions).ToString(); var expected = "/// Thrown when argument is null" @@ -777,8 +654,7 @@ public async Task AppendXmlDocExceptions_WithValidExceptions_Should_AppendAllExc + "/// Thrown when value is out of range" + Environment.NewLine; - _ = await Assert.That(result).IsEqualTo(builder); - _ = await Assert.That(builder.ToString()).IsEqualTo(expected); + _ = await Assert.That(result).IsEqualTo(expected); } [Test] @@ -790,14 +666,13 @@ public async Task AppendXmlDocExceptions_WithSingleException_Should_AppendSingle ("ArgumentNullException", "Thrown when argument is null"), }; - var result = builder.AppendXmlDocExceptions(exceptions); + var result = builder.AppendXmlDocExceptions(exceptions).ToString(); var expected = "/// Thrown when argument is null" + Environment.NewLine; - _ = await Assert.That(result).IsEqualTo(builder); - _ = await Assert.That(builder.ToString()).IsEqualTo(expected); + _ = await Assert.That(result).IsEqualTo(expected); } [Test] @@ -805,10 +680,9 @@ public async Task AppendXmlDocExceptions_WithNullCollection_Should_NotAppendAnyt { var builder = new CSharpCodeBuilder(); - var result = builder.AppendXmlDocExceptions(null); + var result = builder.AppendXmlDocExceptions(null).ToString(); - _ = await Assert.That(result).IsEqualTo(builder); - _ = await Assert.That(builder.ToString()).IsEqualTo(string.Empty); + _ = await Assert.That(result).IsEqualTo(string.Empty); } [Test] @@ -817,10 +691,9 @@ public async Task AppendXmlDocExceptions_WithEmptyCollection_Should_NotAppendAny var builder = new CSharpCodeBuilder(); var exceptions = Array.Empty<(string Type, string Description)>(); - var result = builder.AppendXmlDocExceptions(exceptions); + var result = builder.AppendXmlDocExceptions(exceptions).ToString(); - _ = await Assert.That(result).IsEqualTo(builder); - _ = await Assert.That(builder.ToString()).IsEqualTo(string.Empty); + _ = await Assert.That(result).IsEqualTo(string.Empty); } [Test] @@ -837,7 +710,7 @@ public async Task AppendXmlDocExceptions_WithInvalidExceptions_Should_SkipInvali ("ValidException", "Another valid exception"), }; - var result = builder.AppendXmlDocExceptions(exceptions); + var result = builder.AppendXmlDocExceptions(exceptions).ToString(); var expected = "/// Valid exception" @@ -845,26 +718,7 @@ public async Task AppendXmlDocExceptions_WithInvalidExceptions_Should_SkipInvali + "/// Another valid exception" + Environment.NewLine; - _ = await Assert.That(result).IsEqualTo(builder); - _ = await Assert.That(builder.ToString()).IsEqualTo(expected); - } - - [Test] - public async Task AppendXmlDocExceptions_WithAllInvalidExceptions_Should_NotAppendAnything() - { - var builder = new CSharpCodeBuilder(); - var exceptions = new List<(string Type, string Description)> - { - (null!, "Invalid - null type"), - ("", "Invalid - empty type"), - ("SomeException", null!), - ("AnotherException", ""), - }; - - var result = builder.AppendXmlDocExceptions(exceptions); - - _ = await Assert.That(result).IsEqualTo(builder); - _ = await Assert.That(builder.ToString()).IsEqualTo(string.Empty); + _ = await Assert.That(result).IsEqualTo(expected); } [Test] @@ -880,7 +734,8 @@ public async Task AppendXmlDocExceptions_Should_SupportMethodChaining() var result = builder .AppendXmlDocSummary("Method summary") .AppendXmlDocExceptions(exceptions) - .AppendXmlDocReturns("Return value"); + .AppendXmlDocReturns("Return value") + .ToString(); var expected = "/// " @@ -896,8 +751,7 @@ public async Task AppendXmlDocExceptions_Should_SupportMethodChaining() + "/// Return value" + Environment.NewLine; - _ = await Assert.That(result).IsEqualTo(builder); - _ = await Assert.That(builder.ToString()).IsEqualTo(expected); + _ = await Assert.That(result).IsEqualTo(expected); } [Test] @@ -911,7 +765,7 @@ public async Task AppendXmlDocExceptions_WithIndentation_Should_RespectIndentati ("InvalidOperationException", "Thrown when operation is invalid"), }; - var result = builder.AppendXmlDocExceptions(exceptions); + var result = builder.AppendXmlDocExceptions(exceptions).ToString(); var expected = " /// Thrown when argument is null" @@ -919,8 +773,7 @@ public async Task AppendXmlDocExceptions_WithIndentation_Should_RespectIndentati + " /// Thrown when operation is invalid" + Environment.NewLine; - _ = await Assert.That(result).IsEqualTo(builder); - _ = await Assert.That(builder.ToString()).IsEqualTo(expected); + _ = await Assert.That(result).IsEqualTo(expected); } [Test] @@ -928,17 +781,167 @@ public async Task AppendXmlDocException_WithSpecialCharactersInDescription_Shoul { var builder = new CSharpCodeBuilder(); - var result = builder.AppendXmlDocException( - "ArgumentException", - "Thrown when value contains <, >, &, or \" characters" - ); + var result = builder + .AppendXmlDocException("ArgumentException", "Thrown when value contains <, >, &, or \" characters") + .ToString(); - _ = await Assert.That(result).IsEqualTo(builder); _ = await Assert - .That(builder.ToString()) + .That(result) .IsEqualTo( "/// Thrown when value contains <, >, &, or \" characters" + Environment.NewLine ); } + + [Test] + public async Task AppendXmlDocTypeParams_WithMultipleParameters_Should_AppendAllTypeParamElements() + { + var builder = new CSharpCodeBuilder(); + var typeParameters = new List<(string, string)> + { + ("T", "First type parameter"), + ("U", "Second type parameter"), + }; + + var result = builder.AppendXmlDocTypeParams(typeParameters).ToString(); + + var expected = + "/// First type parameter" + + Environment.NewLine + + "/// Second type parameter" + + Environment.NewLine; + + _ = await Assert.That(result).IsEqualTo(expected); + } + + [Test] + public async Task AppendXmlDocTypeParams_WithSingleParameter_Should_AppendSingleTypeParamElement() + { + var builder = new CSharpCodeBuilder(); + var typeParameters = new List<(string, string)> { ("T", "Generic type parameter") }; + + var result = builder.AppendXmlDocTypeParams(typeParameters).ToString(); + + var expected = "/// Generic type parameter" + Environment.NewLine; + + _ = await Assert.That(result).IsEqualTo(expected); + } + + [Test] + public async Task AppendXmlDocTypeParams_WithNullCollection_Should_NotAppendAnything() + { + var builder = new CSharpCodeBuilder(); + + var result = builder.AppendXmlDocTypeParams(null).ToString(); + + _ = await Assert.That(result).IsEqualTo(string.Empty); + } + + [Test] + public async Task AppendXmlDocTypeParams_WithEmptyCollection_Should_NotAppendAnything() + { + var builder = new CSharpCodeBuilder(); + var typeParameters = Array.Empty<(string, string)>(); + + var result = builder.AppendXmlDocTypeParams(typeParameters).ToString(); + + _ = await Assert.That(result).IsEqualTo(string.Empty); + } + + [Test] + public async Task AppendXmlDocTypeParams_WithInvalidEntries_Should_SkipInvalidEntries() + { + var builder = new CSharpCodeBuilder(); + var typeParameters = new List<(string, string)> + { + ("T", "Valid type parameter"), + (null!, "Invalid - null name"), + ("", "Invalid - empty name"), + ("U", null!), + ("V", ""), + ("W", "Another valid type parameter"), + }; + + var result = builder.AppendXmlDocTypeParams(typeParameters).ToString(); + + var expected = + "/// Valid type parameter" + + Environment.NewLine + + "/// Another valid type parameter" + + Environment.NewLine; + + _ = await Assert.That(result).IsEqualTo(expected); + } + + [Test] + public async Task AppendXmlDocTypeParams_WithAllInvalidEntries_Should_NotAppendAnything() + { + var builder = new CSharpCodeBuilder(); + var typeParameters = new List<(string, string)> + { + (null!, "Invalid - null name"), + ("", "Invalid - empty name"), + ("T", null!), + ("U", ""), + }; + + var result = builder.AppendXmlDocTypeParams(typeParameters).ToString(); + + _ = await Assert.That(result).IsEqualTo(string.Empty); + } + + [Test] + public async Task AppendXmlDocTypeParams_Should_SupportMethodChaining() + { + var builder = new CSharpCodeBuilder(); + var typeParameters = new List<(string, string)> + { + ("T", "First type parameter"), + ("U", "Second type parameter"), + }; + + var result = builder + .AppendXmlDocSummary("Generic class") + .AppendXmlDocTypeParams(typeParameters) + .AppendXmlDocReturns("Return value") + .ToString(); + + var expected = + "/// " + + Environment.NewLine + + "/// Generic class" + + Environment.NewLine + + "/// " + + Environment.NewLine + + "/// First type parameter" + + Environment.NewLine + + "/// Second type parameter" + + Environment.NewLine + + "/// Return value" + + Environment.NewLine; + + _ = await Assert.That(result).IsEqualTo(expected); + } + + [Test] + public async Task AppendXmlDocTypeParams_WithIndentation_Should_RespectIndentation() + { + var builder = new CSharpCodeBuilder(); + builder.IncrementIndent(); + var typeParameters = new List<(string, string)> + { + ("T", "First type parameter"), + ("U", "Second type parameter"), + }; + + var result = builder.AppendXmlDocTypeParams(typeParameters).ToString(); + + var expected = + " /// First type parameter" + + Environment.NewLine + + " /// Second type parameter" + + Environment.NewLine; + + _ = await Assert.That(result).IsEqualTo(expected); + } }