-
Notifications
You must be signed in to change notification settings - Fork 0
Improve test coverage: edge cases + fix bool/string switch key formatting #46
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -241,9 +241,7 @@ private static string GenerateSwitchMethodSource( | |
| ITypeSymbol? parameterType = partialMethod.Parameters.Length > 0 ? partialMethod.Parameters[0].Type : null; | ||
| foreach ((object key, string value) in cases) | ||
| { | ||
| string formattedKey = parameterType?.TypeKind == TypeKind.Enum | ||
| ? $"{parameterType.ToDisplayString()}.{key}" | ||
| : key.ToString()!; | ||
| string formattedKey = FormatKeyAsCSharpLiteral(key, parameterType); | ||
| builder.AppendLine($" case {formattedKey}: return {value};"); | ||
| } | ||
|
|
||
|
|
@@ -318,4 +316,20 @@ internal static string FormatValueAsCSharpLiteral(string? value, ITypeSymbol ret | |
| _ => value | ||
| }; | ||
| } | ||
|
|
||
| private static string FormatKeyAsCSharpLiteral(object key, ITypeSymbol? parameterType) | ||
| { | ||
| if (parameterType?.TypeKind == TypeKind.Enum) | ||
| { | ||
| return $"{parameterType.ToDisplayString()}.{key}"; | ||
| } | ||
|
|
||
| return key switch | ||
| { | ||
| bool b => b ? "true" : "false", | ||
| // SyntaxFactory.Literal handles escaping and quoting (e.g. "hello" → "\"hello\"") | ||
| string s => SyntaxFactory.Literal(s).Text, | ||
| _ => key.ToString()! | ||
|
Comment on lines
+329
to
+332
|
||
| }; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,54 @@ | ||
| using MattSourceGenHelpers.Abstractions; | ||
| // ReSharper disable ConvertClosureToMethodGroup | ||
|
|
||
| namespace MattSourceGenHelpers.Tests; | ||
|
|
||
| [TestFixture] | ||
| public class BoolSwitchKeyTests | ||
| { | ||
| [TestCase(true, "Yes")] | ||
| [TestCase(false, "No")] | ||
| public void BoolSwitchKey_ProducesExpectedRuntimeOutput(bool flag, string expected) | ||
| { | ||
| string result = TestBoolSwitchClass.GetBoolLabel(flag); | ||
| Assert.That(result, Is.EqualTo(expected)); | ||
| } | ||
|
|
||
| [Test] | ||
| public void BoolSwitchKey_ProducesExpectedGeneratedCode() | ||
| { | ||
| string generatedCode = GeneratedCodeTestHelper.ReadGeneratedCode("TestBoolSwitchClass_GetBoolLabel.g.cs"); | ||
| string expectedCode = """ | ||
| namespace MattSourceGenHelpers.Tests; | ||
|
|
||
| static partial class TestBoolSwitchClass | ||
| { | ||
| public static partial string GetBoolLabel(bool flag) | ||
| { | ||
| switch (flag) | ||
| { | ||
| case true: return "Yes"; | ||
| case false: return "No"; | ||
| default: return "Unknown"; | ||
| } | ||
| } | ||
| } | ||
| """.ReplaceLineEndings("\n").TrimEnd(); | ||
|
|
||
| Assert.That(generatedCode, Is.EqualTo(expectedCode)); | ||
| } | ||
| } | ||
|
|
||
| public static partial class TestBoolSwitchClass | ||
| { | ||
| public static partial string GetBoolLabel(bool flag); | ||
|
|
||
| [GeneratesMethod(nameof(GetBoolLabel))] | ||
| [SwitchCase(arg1: true)] | ||
| [SwitchCase(arg1: false)] | ||
| static string GetBoolLabel_Generator(bool flag) => flag ? "Yes" : "No"; | ||
|
|
||
| [GeneratesMethod(nameof(GetBoolLabel))] | ||
| [SwitchDefault] | ||
| static Func<bool, string> GetBoolLabel_Default() => _ => "Unknown"; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,159 @@ | ||
| using MattSourceGenHelpers.Abstractions; | ||
|
|
||
| namespace MattSourceGenHelpers.Tests; | ||
|
|
||
| [TestFixture] | ||
| public class EdgeCaseSimplePatternTests | ||
| { | ||
| // ----------------------------------------------------------------------- | ||
| // Void return type | ||
| // ----------------------------------------------------------------------- | ||
|
|
||
| [Test] | ||
| public void VoidReturnGenerator_MethodIsCallable() | ||
| { | ||
| TestVoidClass instance = new TestVoidClass(); | ||
| Assert.DoesNotThrow(() => instance.DoSomething()); | ||
| } | ||
|
|
||
| [Test] | ||
| public void VoidReturnGenerator_ProducesExpectedGeneratedCode() | ||
| { | ||
| string generatedCode = GeneratedCodeTestHelper.ReadGeneratedCode("TestVoidClass_DoSomething.g.cs"); | ||
| string expectedCode = """ | ||
| namespace MattSourceGenHelpers.Tests; | ||
|
|
||
| partial class TestVoidClass | ||
| { | ||
| public partial void DoSomething() | ||
| { | ||
| } | ||
| } | ||
| """.ReplaceLineEndings("\n").TrimEnd(); | ||
|
|
||
| Assert.That(generatedCode, Is.EqualTo(expectedCode)); | ||
| } | ||
|
|
||
| // ----------------------------------------------------------------------- | ||
| // Bool return type | ||
| // ----------------------------------------------------------------------- | ||
|
|
||
| [Test] | ||
| public void BoolReturnGenerator_ProducesExpectedRuntimeOutput() | ||
| { | ||
| bool result = TestBoolReturnClass.IsEnabled(); | ||
| Assert.That(result, Is.True); | ||
| } | ||
|
|
||
| [Test] | ||
| public void BoolReturnGenerator_ProducesExpectedGeneratedCode() | ||
| { | ||
| string generatedCode = GeneratedCodeTestHelper.ReadGeneratedCode("TestBoolReturnClass_IsEnabled.g.cs"); | ||
| string expectedCode = """ | ||
| namespace MattSourceGenHelpers.Tests; | ||
|
|
||
| static partial class TestBoolReturnClass | ||
| { | ||
| public static partial bool IsEnabled() | ||
| { | ||
| return true; | ||
| } | ||
| } | ||
| """.ReplaceLineEndings("\n").TrimEnd(); | ||
|
|
||
| Assert.That(generatedCode, Is.EqualTo(expectedCode)); | ||
| } | ||
|
|
||
| // ----------------------------------------------------------------------- | ||
| // Char return type | ||
| // ----------------------------------------------------------------------- | ||
|
|
||
| [Test] | ||
| public void CharReturnGenerator_ProducesExpectedRuntimeOutput() | ||
| { | ||
| char result = TestCharReturnClass.GetSymbol(); | ||
| Assert.That(result, Is.EqualTo('A')); | ||
| } | ||
|
|
||
| [Test] | ||
| public void CharReturnGenerator_ProducesExpectedGeneratedCode() | ||
| { | ||
| string generatedCode = GeneratedCodeTestHelper.ReadGeneratedCode("TestCharReturnClass_GetSymbol.g.cs"); | ||
| string expectedCode = """ | ||
| namespace MattSourceGenHelpers.Tests; | ||
|
|
||
| static partial class TestCharReturnClass | ||
| { | ||
| public static partial char GetSymbol() | ||
| { | ||
| return 'A'; | ||
| } | ||
| } | ||
| """.ReplaceLineEndings("\n").TrimEnd(); | ||
|
|
||
| Assert.That(generatedCode, Is.EqualTo(expectedCode)); | ||
| } | ||
|
|
||
| // ----------------------------------------------------------------------- | ||
| // Internal method accessibility | ||
| // ----------------------------------------------------------------------- | ||
|
|
||
| [Test] | ||
| public void InternalMethodGenerator_ProducesExpectedRuntimeOutput() | ||
| { | ||
| TestInternalMethodClass instance = new TestInternalMethodClass(); | ||
| string result = instance.GetValue(); | ||
| Assert.That(result, Is.EqualTo("internal_value")); | ||
| } | ||
|
|
||
| [Test] | ||
| public void InternalMethodGenerator_ProducesExpectedGeneratedCode() | ||
| { | ||
| string generatedCode = GeneratedCodeTestHelper.ReadGeneratedCode("TestInternalMethodClass_GetValue.g.cs"); | ||
| string expectedCode = """ | ||
| namespace MattSourceGenHelpers.Tests; | ||
|
|
||
| partial class TestInternalMethodClass | ||
| { | ||
| internal partial string GetValue() | ||
| { | ||
| return "internal_value"; | ||
| } | ||
| } | ||
| """.ReplaceLineEndings("\n").TrimEnd(); | ||
|
|
||
| Assert.That(generatedCode, Is.EqualTo(expectedCode)); | ||
| } | ||
| } | ||
|
|
||
| public partial class TestVoidClass | ||
| { | ||
| public partial void DoSomething(); | ||
|
|
||
| [GeneratesMethod(nameof(DoSomething))] | ||
| private static void DoSomething_Generator() { } | ||
| } | ||
|
|
||
| public static partial class TestBoolReturnClass | ||
| { | ||
| public static partial bool IsEnabled(); | ||
|
|
||
| [GeneratesMethod(nameof(IsEnabled))] | ||
| private static bool IsEnabled_Generator() => true; | ||
| } | ||
|
|
||
| public static partial class TestCharReturnClass | ||
| { | ||
| public static partial char GetSymbol(); | ||
|
|
||
| [GeneratesMethod(nameof(GetSymbol))] | ||
| private static char GetSymbol_Generator() => 'A'; | ||
| } | ||
|
|
||
| public partial class TestInternalMethodClass | ||
| { | ||
| internal partial string GetValue(); | ||
|
|
||
| [GeneratesMethod(nameof(GetValue))] | ||
| private static string GetValue_Generator() => "internal_value"; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FormatKeyAsCSharpLiteralfixesbool/string, butcharswitch keys will still generate invalid C# (e.g.,[SwitchCase(arg1: 'A')]will end up ascase A:because the fallback useskey.ToString()). Consider handlingchar(and potentially other primitive literal types) similarly toFormatValueAsCSharpLiteral, e.g., useSyntaxFactory.Literal(c).Textwhenkey is charor whenparameterType.SpecialType == System_Char.