From d5f2edceef6f114a1ecd5b2f3cc1f6b079db4f2b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 25 Aug 2025 07:57:32 +0000 Subject: [PATCH 1/7] chore: Initial plan From a1542560d10bff436de97191325f24d54f2eb6a0 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 25 Aug 2025 08:02:53 +0000 Subject: [PATCH 2/7] chore: Initial plan for Scope method implementation Co-authored-by: samtrion <3283596+samtrion@users.noreply.github.com> --- Directory.Build.props | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index 2d8f37c..00a5076 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -7,13 +7,13 @@ $(RepositoryUrl)/releases $(PackageTags);codegeneration;sourcegenrator 2024 - <_DefaultTargetFrameworks>net8.0;net9.0;net10.0 + <_DefaultTargetFrameworks>net8.0 <_ProjectTargetFrameworks>netstandard2.0;netstandard2.1;$(_DefaultTargetFrameworks) <_TestTargetFrameworks>$(_DefaultTargetFrameworks) false - net9.0 + net8.0 From cafbee40a4ad7945e857cb07a1f706bd860c2115 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 25 Aug 2025 08:12:47 +0000 Subject: [PATCH 3/7] chore: Implement Scope method for automatic indentation management with comprehensive tests Co-authored-by: samtrion <3283596+samtrion@users.noreply.github.com> --- .../CSharpCodeBuilder.Scope.cs | 94 ++++++ .../CSharpCodeBuilderTests.Append.cs | 2 +- .../CSharpCodeBuilderTests.Scope.cs | 274 ++++++++++++++++++ 3 files changed, 369 insertions(+), 1 deletion(-) create mode 100644 src/NetEvolve.CodeBuilder/CSharpCodeBuilder.Scope.cs create mode 100644 tests/NetEvolve.CodeBuilder.Tests.Unit/CSharpCodeBuilderTests.Scope.cs diff --git a/src/NetEvolve.CodeBuilder/CSharpCodeBuilder.Scope.cs b/src/NetEvolve.CodeBuilder/CSharpCodeBuilder.Scope.cs new file mode 100644 index 0000000..c692875 --- /dev/null +++ b/src/NetEvolve.CodeBuilder/CSharpCodeBuilder.Scope.cs @@ -0,0 +1,94 @@ +namespace NetEvolve.CodeBuilder; + +using System; + +/// +/// 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. +/// +public 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.IncrementIndent(); + } + + /// + /// Decrements the indentation level when the scope is disposed. + /// + /// + /// This method is called automatically when the 'using' statement block ends. + /// + public void Dispose() + { + _builder?.DecrementIndent(); + } + + /// + /// 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 left, ScopeHandler right) => 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 left, ScopeHandler right) => true; +} + +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 ScopeHandler Scope() => new(this); +} diff --git a/tests/NetEvolve.CodeBuilder.Tests.Unit/CSharpCodeBuilderTests.Append.cs b/tests/NetEvolve.CodeBuilder.Tests.Unit/CSharpCodeBuilderTests.Append.cs index ce48611..02db166 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(""); } diff --git a/tests/NetEvolve.CodeBuilder.Tests.Unit/CSharpCodeBuilderTests.Scope.cs b/tests/NetEvolve.CodeBuilder.Tests.Unit/CSharpCodeBuilderTests.Scope.cs new file mode 100644 index 0000000..e693569 --- /dev/null +++ b/tests/NetEvolve.CodeBuilder.Tests.Unit/CSharpCodeBuilderTests.Scope.cs @@ -0,0 +1,274 @@ +namespace NetEvolve.CodeBuilder.Tests.Unit; + +using System; + +public partial class CSharpCodeBuilderTests +{ + [Test] + public async Task Scope_Should_Increment_Indentation_On_Creation() + { + var builder = new CSharpCodeBuilder(); + + using (builder.Scope()) + { + _ = builder.AppendLine().Append("test"); + } + + var expected = Environment.NewLine + " test"; + _ = await Assert.That(builder.ToString()).IsEqualTo(expected); + } + + [Test] + public async Task Scope_Should_Decrement_Indentation_On_Disposal() + { + var builder = new CSharpCodeBuilder(); + + using (builder.Scope()) + { + _ = builder.AppendLine().Append("indented"); + } + _ = builder.AppendLine().Append("normal"); + + var expected = Environment.NewLine + " indented" + Environment.NewLine + "normal"; + _ = await Assert.That(builder.ToString()).IsEqualTo(expected); + } + + [Test] + public async Task Scope_Should_Support_Nested_Scopes() + { + var builder = new CSharpCodeBuilder(); + + using (builder.Scope()) + { + _ = builder.AppendLine().Append("level 1"); + using (builder.Scope()) + { + _ = builder.AppendLine().Append("level 2"); + using (builder.Scope()) + { + _ = builder.AppendLine().Append("level 3"); + } + _ = builder.AppendLine().Append("back to level 2"); + } + _ = builder.AppendLine().Append("back to level 1"); + } + _ = builder.AppendLine().Append("level 0"); + + var expected = + Environment.NewLine + + " level 1" + + Environment.NewLine + + " level 2" + + Environment.NewLine + + " level 3" + + Environment.NewLine + + " back to level 2" + + Environment.NewLine + + " back to level 1" + + Environment.NewLine + + "level 0"; + + _ = await Assert.That(builder.ToString()).IsEqualTo(expected); + } + + [Test] + public async Task Scope_Should_Work_With_Tabs() + { + var builder = new CSharpCodeBuilder { UseTabs = true }; + + using (builder.Scope()) + { + _ = builder.AppendLine().Append("test"); + } + + var expected = Environment.NewLine + "\ttest"; + _ = await Assert.That(builder.ToString()).IsEqualTo(expected); + } + + [Test] + public async Task Scope_Should_Handle_Multiple_Sequential_Scopes() + { + var builder = new CSharpCodeBuilder(); + + using (builder.Scope()) + { + _ = builder.AppendLine().Append("first scope"); + } + + using (builder.Scope()) + { + _ = builder.AppendLine().Append("second scope"); + } + + _ = builder.AppendLine().Append("no scope"); + + var expected = + Environment.NewLine + + " first scope" + + Environment.NewLine + + " second scope" + + Environment.NewLine + + "no scope"; + + _ = await Assert.That(builder.ToString()).IsEqualTo(expected); + } + + [Test] + public async Task Scope_Should_Handle_Empty_Scope() + { + var builder = new CSharpCodeBuilder(); + + using (builder.Scope()) + { + // Empty scope - should not affect builder content + } + + _ = builder.AppendLine().Append("test"); + + var expected = Environment.NewLine + "test"; + _ = await Assert.That(builder.ToString()).IsEqualTo(expected); + } + + [Test] + public async Task Scope_Should_Handle_Exception_Within_Scope() + { + var builder = new CSharpCodeBuilder(); + + try + { + using (builder.Scope()) + { + _ = builder.AppendLine().Append("before exception"); + throw new InvalidOperationException("Test exception"); +#pragma warning disable CS0162 // Unreachable code detected + _ = builder.AppendLine().Append("after exception"); +#pragma warning restore CS0162 // Unreachable code detected + } + } + catch (InvalidOperationException) + { + // Expected exception - indentation should still be properly decremented + } + + _ = builder.AppendLine().Append("after scope"); + + var expected = Environment.NewLine + " before exception" + Environment.NewLine + "after scope"; + _ = await Assert.That(builder.ToString()).IsEqualTo(expected); + } + + [Test] + public async Task Scope_Should_Work_With_Existing_Indentation() + { + var builder = new CSharpCodeBuilder(); + + // Manually add some indentation first + builder.IncrementIndent(); + _ = builder.AppendLine().Append("existing indent"); + + using (builder.Scope()) + { + _ = builder.AppendLine().Append("scope indent"); + } + + _ = builder.AppendLine().Append("back to existing"); + + var expected = + Environment.NewLine + + " existing indent" + + Environment.NewLine + + " scope indent" + + Environment.NewLine + + " back to existing"; + + _ = await Assert.That(builder.ToString()).IsEqualTo(expected); + } + + [Test] + public async Task Scope_Should_Work_With_Complex_Code_Generation() + { + var builder = new CSharpCodeBuilder(); + + _ = builder.AppendLine("public class TestClass"); + using (builder.Scope()) + { + _ = builder.Append("public void TestMethod()").AppendLine(); + using (builder.Scope()) + { + _ = builder.Append("if (true)").AppendLine(); + using (builder.Scope()) + { + _ = builder.Append("Console.WriteLine(\"Hello, World!\");"); + } + } + } + + var expected = + "public class TestClass" + + Environment.NewLine + + " public void TestMethod()" + + Environment.NewLine + + " if (true)" + + Environment.NewLine + + " Console.WriteLine(\"Hello, World!\");"; + + _ = await Assert.That(builder.ToString()).IsEqualTo(expected); + } + + [Test] + public async Task Scope_Should_Handle_Deep_Nesting() + { + var builder = new CSharpCodeBuilder(); + const int nestingLevels = 5; + + // Create deep nesting + var scopes = new ScopeHandler[nestingLevels]; + for (int i = 0; i < nestingLevels; i++) + { + scopes[i] = builder.Scope(); + _ = builder.AppendLine().Append($"Level {i + 1}"); + } + + // Dispose scopes in reverse order (as would happen with nested using statements) + for (int i = nestingLevels - 1; i >= 0; i--) + { + scopes[i].Dispose(); + } + + _ = builder.AppendLine().Append("Back to level 0"); + + var expected = + Environment.NewLine + + " Level 1" + + Environment.NewLine + + " Level 2" + + Environment.NewLine + + " Level 3" + + Environment.NewLine + + " Level 4" + + Environment.NewLine + + " Level 5" + + Environment.NewLine + + "Back to level 0"; + + _ = await Assert.That(builder.ToString()).IsEqualTo(expected); + } + + [Test] + public async Task Scope_Disposal_Should_Be_Safe_To_Call_Multiple_Times() + { + var builder = new CSharpCodeBuilder(); + + var scope = builder.Scope(); + _ = builder.AppendLine().Append("test"); + + // Dispose multiple times should be safe + scope.Dispose(); + scope.Dispose(); + scope.Dispose(); + + _ = builder.AppendLine().Append("after"); + + var expected = Environment.NewLine + " test" + Environment.NewLine + "after"; + _ = await Assert.That(builder.ToString()).IsEqualTo(expected); + } +} From fd8098166e779da51a6fbdf715cc057bf9852161 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 25 Aug 2025 08:14:50 +0000 Subject: [PATCH 4/7] chore: Add example usage test and restore original build configuration Co-authored-by: samtrion <3283596+samtrion@users.noreply.github.com> --- Directory.Build.props | 4 ++-- .../CSharpCodeBuilderTests.Scope.cs | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index 00a5076..2d8f37c 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -7,13 +7,13 @@ $(RepositoryUrl)/releases $(PackageTags);codegeneration;sourcegenrator 2024 - <_DefaultTargetFrameworks>net8.0 + <_DefaultTargetFrameworks>net8.0;net9.0;net10.0 <_ProjectTargetFrameworks>netstandard2.0;netstandard2.1;$(_DefaultTargetFrameworks) <_TestTargetFrameworks>$(_DefaultTargetFrameworks) false - net8.0 + net9.0 diff --git a/tests/NetEvolve.CodeBuilder.Tests.Unit/CSharpCodeBuilderTests.Scope.cs b/tests/NetEvolve.CodeBuilder.Tests.Unit/CSharpCodeBuilderTests.Scope.cs index e693569..46355af 100644 --- a/tests/NetEvolve.CodeBuilder.Tests.Unit/CSharpCodeBuilderTests.Scope.cs +++ b/tests/NetEvolve.CodeBuilder.Tests.Unit/CSharpCodeBuilderTests.Scope.cs @@ -271,4 +271,23 @@ public async Task Scope_Disposal_Should_Be_Safe_To_Call_Multiple_Times() var expected = Environment.NewLine + " test" + Environment.NewLine + "after"; _ = await Assert.That(builder.ToString()).IsEqualTo(expected); } + + [Test] + public async Task Scope_Example_Usage_Should_Generate_Proper_Code() + { + // This test shows the typical usage pattern mentioned in the issue + var builder = new CSharpCodeBuilder(); + + _ = builder.AppendLine("if (condition)"); + using (builder.Scope()) + { + _ = builder.AppendLine("return true;"); + } + _ = builder.Append("return false;"); + + var expected = + "if (condition)" + Environment.NewLine + " return true;" + Environment.NewLine + "return false;"; + + _ = await Assert.That(builder.ToString()).IsEqualTo(expected); + } } From dbb65a4392dffc39c79089b9371bd5d543717488 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20St=C3=BChmer?= Date: Tue, 26 Aug 2025 11:03:41 +0200 Subject: [PATCH 5/7] chore: Update the implementation --- .../CSharpCodeBuilder.Append.cs | 2 +- .../CSharpCodeBuilder.Documentation.cs | 14 +- .../CSharpCodeBuilder.Scope.cs | 137 ++++---- .../CSharpCodeBuilderTests.Formatting.cs | 28 +- ...erTests.Scopes_in_wild_nature.verified.txt | 15 + .../CSharpCodeBuilderTests.Documentation.cs | 2 +- .../CSharpCodeBuilderTests.Scope.cs | 293 ------------------ 7 files changed, 124 insertions(+), 367 deletions(-) create mode 100644 tests/NetEvolve.CodeBuilder.Tests.Integration/_snapshots/CSharpCodeBuilderTests.Scopes_in_wild_nature.verified.txt delete mode 100644 tests/NetEvolve.CodeBuilder.Tests.Unit/CSharpCodeBuilderTests.Scope.cs 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..e5dbd0a 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. /// diff --git a/src/NetEvolve.CodeBuilder/CSharpCodeBuilder.Scope.cs b/src/NetEvolve.CodeBuilder/CSharpCodeBuilder.Scope.cs index c692875..06cde62 100644 --- a/src/NetEvolve.CodeBuilder/CSharpCodeBuilder.Scope.cs +++ b/src/NetEvolve.CodeBuilder/CSharpCodeBuilder.Scope.cs @@ -2,75 +2,6 @@ using System; -/// -/// 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. -/// -public 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.IncrementIndent(); - } - - /// - /// Decrements the indentation level when the scope is disposed. - /// - /// - /// This method is called automatically when the 'using' statement block ends. - /// - public void Dispose() - { - _builder?.DecrementIndent(); - } - - /// - /// 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 left, ScopeHandler right) => 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 left, ScopeHandler right) => true; -} - public partial record CSharpCodeBuilder { /// @@ -90,5 +21,71 @@ public partial record CSharpCodeBuilder /// } /// /// - public ScopeHandler Scope() => new(this); + 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.Documentation.cs b/tests/NetEvolve.CodeBuilder.Tests.Unit/CSharpCodeBuilderTests.Documentation.cs index bbcf1bf..cda4737 100644 --- a/tests/NetEvolve.CodeBuilder.Tests.Unit/CSharpCodeBuilderTests.Documentation.cs +++ b/tests/NetEvolve.CodeBuilder.Tests.Unit/CSharpCodeBuilderTests.Documentation.cs @@ -726,7 +726,7 @@ 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"); _ = await Assert.That(result).IsEqualTo(builder); _ = await Assert diff --git a/tests/NetEvolve.CodeBuilder.Tests.Unit/CSharpCodeBuilderTests.Scope.cs b/tests/NetEvolve.CodeBuilder.Tests.Unit/CSharpCodeBuilderTests.Scope.cs deleted file mode 100644 index 46355af..0000000 --- a/tests/NetEvolve.CodeBuilder.Tests.Unit/CSharpCodeBuilderTests.Scope.cs +++ /dev/null @@ -1,293 +0,0 @@ -namespace NetEvolve.CodeBuilder.Tests.Unit; - -using System; - -public partial class CSharpCodeBuilderTests -{ - [Test] - public async Task Scope_Should_Increment_Indentation_On_Creation() - { - var builder = new CSharpCodeBuilder(); - - using (builder.Scope()) - { - _ = builder.AppendLine().Append("test"); - } - - var expected = Environment.NewLine + " test"; - _ = await Assert.That(builder.ToString()).IsEqualTo(expected); - } - - [Test] - public async Task Scope_Should_Decrement_Indentation_On_Disposal() - { - var builder = new CSharpCodeBuilder(); - - using (builder.Scope()) - { - _ = builder.AppendLine().Append("indented"); - } - _ = builder.AppendLine().Append("normal"); - - var expected = Environment.NewLine + " indented" + Environment.NewLine + "normal"; - _ = await Assert.That(builder.ToString()).IsEqualTo(expected); - } - - [Test] - public async Task Scope_Should_Support_Nested_Scopes() - { - var builder = new CSharpCodeBuilder(); - - using (builder.Scope()) - { - _ = builder.AppendLine().Append("level 1"); - using (builder.Scope()) - { - _ = builder.AppendLine().Append("level 2"); - using (builder.Scope()) - { - _ = builder.AppendLine().Append("level 3"); - } - _ = builder.AppendLine().Append("back to level 2"); - } - _ = builder.AppendLine().Append("back to level 1"); - } - _ = builder.AppendLine().Append("level 0"); - - var expected = - Environment.NewLine - + " level 1" - + Environment.NewLine - + " level 2" - + Environment.NewLine - + " level 3" - + Environment.NewLine - + " back to level 2" - + Environment.NewLine - + " back to level 1" - + Environment.NewLine - + "level 0"; - - _ = await Assert.That(builder.ToString()).IsEqualTo(expected); - } - - [Test] - public async Task Scope_Should_Work_With_Tabs() - { - var builder = new CSharpCodeBuilder { UseTabs = true }; - - using (builder.Scope()) - { - _ = builder.AppendLine().Append("test"); - } - - var expected = Environment.NewLine + "\ttest"; - _ = await Assert.That(builder.ToString()).IsEqualTo(expected); - } - - [Test] - public async Task Scope_Should_Handle_Multiple_Sequential_Scopes() - { - var builder = new CSharpCodeBuilder(); - - using (builder.Scope()) - { - _ = builder.AppendLine().Append("first scope"); - } - - using (builder.Scope()) - { - _ = builder.AppendLine().Append("second scope"); - } - - _ = builder.AppendLine().Append("no scope"); - - var expected = - Environment.NewLine - + " first scope" - + Environment.NewLine - + " second scope" - + Environment.NewLine - + "no scope"; - - _ = await Assert.That(builder.ToString()).IsEqualTo(expected); - } - - [Test] - public async Task Scope_Should_Handle_Empty_Scope() - { - var builder = new CSharpCodeBuilder(); - - using (builder.Scope()) - { - // Empty scope - should not affect builder content - } - - _ = builder.AppendLine().Append("test"); - - var expected = Environment.NewLine + "test"; - _ = await Assert.That(builder.ToString()).IsEqualTo(expected); - } - - [Test] - public async Task Scope_Should_Handle_Exception_Within_Scope() - { - var builder = new CSharpCodeBuilder(); - - try - { - using (builder.Scope()) - { - _ = builder.AppendLine().Append("before exception"); - throw new InvalidOperationException("Test exception"); -#pragma warning disable CS0162 // Unreachable code detected - _ = builder.AppendLine().Append("after exception"); -#pragma warning restore CS0162 // Unreachable code detected - } - } - catch (InvalidOperationException) - { - // Expected exception - indentation should still be properly decremented - } - - _ = builder.AppendLine().Append("after scope"); - - var expected = Environment.NewLine + " before exception" + Environment.NewLine + "after scope"; - _ = await Assert.That(builder.ToString()).IsEqualTo(expected); - } - - [Test] - public async Task Scope_Should_Work_With_Existing_Indentation() - { - var builder = new CSharpCodeBuilder(); - - // Manually add some indentation first - builder.IncrementIndent(); - _ = builder.AppendLine().Append("existing indent"); - - using (builder.Scope()) - { - _ = builder.AppendLine().Append("scope indent"); - } - - _ = builder.AppendLine().Append("back to existing"); - - var expected = - Environment.NewLine - + " existing indent" - + Environment.NewLine - + " scope indent" - + Environment.NewLine - + " back to existing"; - - _ = await Assert.That(builder.ToString()).IsEqualTo(expected); - } - - [Test] - public async Task Scope_Should_Work_With_Complex_Code_Generation() - { - var builder = new CSharpCodeBuilder(); - - _ = builder.AppendLine("public class TestClass"); - using (builder.Scope()) - { - _ = builder.Append("public void TestMethod()").AppendLine(); - using (builder.Scope()) - { - _ = builder.Append("if (true)").AppendLine(); - using (builder.Scope()) - { - _ = builder.Append("Console.WriteLine(\"Hello, World!\");"); - } - } - } - - var expected = - "public class TestClass" - + Environment.NewLine - + " public void TestMethod()" - + Environment.NewLine - + " if (true)" - + Environment.NewLine - + " Console.WriteLine(\"Hello, World!\");"; - - _ = await Assert.That(builder.ToString()).IsEqualTo(expected); - } - - [Test] - public async Task Scope_Should_Handle_Deep_Nesting() - { - var builder = new CSharpCodeBuilder(); - const int nestingLevels = 5; - - // Create deep nesting - var scopes = new ScopeHandler[nestingLevels]; - for (int i = 0; i < nestingLevels; i++) - { - scopes[i] = builder.Scope(); - _ = builder.AppendLine().Append($"Level {i + 1}"); - } - - // Dispose scopes in reverse order (as would happen with nested using statements) - for (int i = nestingLevels - 1; i >= 0; i--) - { - scopes[i].Dispose(); - } - - _ = builder.AppendLine().Append("Back to level 0"); - - var expected = - Environment.NewLine - + " Level 1" - + Environment.NewLine - + " Level 2" - + Environment.NewLine - + " Level 3" - + Environment.NewLine - + " Level 4" - + Environment.NewLine - + " Level 5" - + Environment.NewLine - + "Back to level 0"; - - _ = await Assert.That(builder.ToString()).IsEqualTo(expected); - } - - [Test] - public async Task Scope_Disposal_Should_Be_Safe_To_Call_Multiple_Times() - { - var builder = new CSharpCodeBuilder(); - - var scope = builder.Scope(); - _ = builder.AppendLine().Append("test"); - - // Dispose multiple times should be safe - scope.Dispose(); - scope.Dispose(); - scope.Dispose(); - - _ = builder.AppendLine().Append("after"); - - var expected = Environment.NewLine + " test" + Environment.NewLine + "after"; - _ = await Assert.That(builder.ToString()).IsEqualTo(expected); - } - - [Test] - public async Task Scope_Example_Usage_Should_Generate_Proper_Code() - { - // This test shows the typical usage pattern mentioned in the issue - var builder = new CSharpCodeBuilder(); - - _ = builder.AppendLine("if (condition)"); - using (builder.Scope()) - { - _ = builder.AppendLine("return true;"); - } - _ = builder.Append("return false;"); - - var expected = - "if (condition)" + Environment.NewLine + " return true;" + Environment.NewLine + "return false;"; - - _ = await Assert.That(builder.ToString()).IsEqualTo(expected); - } -} From e67af266d453139a65e481da8ddc6c7e05030913 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20St=C3=BChmer?= Date: Tue, 26 Aug 2025 15:19:02 +0200 Subject: [PATCH 6/7] chore: Exteded `AppendXmlDocSee` and `AppendXmlDocSeeAlso` with `href` --- .../CSharpCodeBuilder.Documentation.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/NetEvolve.CodeBuilder/CSharpCodeBuilder.Documentation.cs b/src/NetEvolve.CodeBuilder/CSharpCodeBuilder.Documentation.cs index e5dbd0a..da671bd 100644 --- a/src/NetEvolve.CodeBuilder/CSharpCodeBuilder.Documentation.cs +++ b/src/NetEvolve.CodeBuilder/CSharpCodeBuilder.Documentation.cs @@ -258,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($"/// "); } /// From adfe30b3c02da515b12de31333f2b5affc0bf78d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20St=C3=BChmer?= Date: Wed, 27 Aug 2025 11:07:19 +0200 Subject: [PATCH 7/7] test: Updated tests --- .../CSharpCodeBuilder.Documentation.cs | 12 +- .../CSharpCodeBuilderTests.Append.cs | 180 +++++++ .../CSharpCodeBuilderTests.Documentation.cs | 477 +++++++++--------- 3 files changed, 426 insertions(+), 243 deletions(-) diff --git a/src/NetEvolve.CodeBuilder/CSharpCodeBuilder.Documentation.cs b/src/NetEvolve.CodeBuilder/CSharpCodeBuilder.Documentation.cs index da671bd..c889591 100644 --- a/src/NetEvolve.CodeBuilder/CSharpCodeBuilder.Documentation.cs +++ b/src/NetEvolve.CodeBuilder/CSharpCodeBuilder.Documentation.cs @@ -258,34 +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. + /// 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, bool isHRef = false) + 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. + /// 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, bool isHRef = false) + public CSharpCodeBuilder AppendXmlDocSeeAlso(string? cref, bool isHref = false) { if (string.IsNullOrEmpty(cref)) { return this; } - return EnsureNewLineForXmlDoc().AppendLine($"/// "); + return EnsureNewLineForXmlDoc().AppendLine($"/// "); } /// diff --git a/tests/NetEvolve.CodeBuilder.Tests.Unit/CSharpCodeBuilderTests.Append.cs b/tests/NetEvolve.CodeBuilder.Tests.Unit/CSharpCodeBuilderTests.Append.cs index 02db166..282770b 100644 --- a/tests/NetEvolve.CodeBuilder.Tests.Unit/CSharpCodeBuilderTests.Append.cs +++ b/tests/NetEvolve.CodeBuilder.Tests.Unit/CSharpCodeBuilderTests.Append.cs @@ -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 cda4737..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("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); + } }