Skip to content

Commit cec2835

Browse files
Copilotdex3r
andauthored
Improve test coverage: edge cases + fix bool/string switch key formatting (#46)
* Initial plan * test: add edge case tests and fix bool/string switch key formatting Co-authored-by: dex3r <3155725+dex3r@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: dex3r <3155725+dex3r@users.noreply.github.com>
1 parent bd67931 commit cec2835

4 files changed

Lines changed: 294 additions & 3 deletions

File tree

MattSourceGenHelpers.GeneratorTests/GeneratorDiagnosticsTests.cs

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -419,4 +419,68 @@ static IMethodImplementationGenerator GetValue_Generator() =>
419419
Assert.That(diagnostics.Value.Where(d => d.Severity == DiagnosticSeverity.Error), Is.Empty,
420420
"Fluent generator with a CompilationReference for abstractions should produce no error diagnostics");
421421
}
422+
423+
// -----------------------------------------------------------------------
424+
// MSGH001 – targeting an existing but non-partial method
425+
// -----------------------------------------------------------------------
426+
427+
[Test]
428+
public void GeneratesMethod_TargetingNonPartialMethod_EmitsMSGH001()
429+
{
430+
string source = """
431+
using MattSourceGenHelpers.Abstractions;
432+
433+
namespace TestNamespace;
434+
435+
public partial class MyClass
436+
{
437+
public string NonPartialMethod() => "hello";
438+
439+
[GeneratesMethod("NonPartialMethod")]
440+
private static string MyGenerator() => "world";
441+
}
442+
""";
443+
444+
ImmutableArray<Diagnostic> diagnostics = GeneratorTestHelper.GetGeneratorOnlyDiagnostics(source);
445+
446+
Diagnostic? msgh001 = diagnostics.FirstOrDefault(d => d.Id == "MSGH001");
447+
Assert.That(msgh001, Is.Not.Null,
448+
"Expected MSGH001 when targeting an existing but non-partial method");
449+
Assert.That(msgh001!.GetMessage(), Does.Contain("NonPartialMethod"),
450+
"Error message should mention the non-partial method name");
451+
}
452+
453+
// -----------------------------------------------------------------------
454+
// Bool switch parameter – valid configuration
455+
// -----------------------------------------------------------------------
456+
457+
[Test]
458+
public void GeneratesMethod_SwitchCaseWithBoolParameter_ValidConfiguration_ProducesNoDiagnosticErrors()
459+
{
460+
string source = """
461+
using MattSourceGenHelpers.Abstractions;
462+
using System;
463+
464+
namespace TestNamespace;
465+
466+
public static partial class MyClass
467+
{
468+
public static partial string GetLabel(bool flag);
469+
470+
[GeneratesMethod(nameof(GetLabel))]
471+
[SwitchCase(arg1: true)]
472+
[SwitchCase(arg1: false)]
473+
private static string GetLabel_Generator(bool flag) => flag ? "Yes" : "No";
474+
475+
[GeneratesMethod(nameof(GetLabel))]
476+
[SwitchDefault]
477+
private static Func<bool, string> GetLabel_Default() => _ => "Unknown";
478+
}
479+
""";
480+
481+
ImmutableArray<Diagnostic> diagnostics = GeneratorTestHelper.GetGeneratorOnlyDiagnostics(source);
482+
483+
Assert.That(diagnostics.Where(d => d.Severity == DiagnosticSeverity.Error), Is.Empty,
484+
"Valid bool switch case generator should produce no error diagnostics");
485+
}
422486
}

MattSourceGenHelpers.Generators/GeneratesMethodPatternSourceBuilder.cs

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -241,9 +241,7 @@ private static string GenerateSwitchMethodSource(
241241
ITypeSymbol? parameterType = partialMethod.Parameters.Length > 0 ? partialMethod.Parameters[0].Type : null;
242242
foreach ((object key, string value) in cases)
243243
{
244-
string formattedKey = parameterType?.TypeKind == TypeKind.Enum
245-
? $"{parameterType.ToDisplayString()}.{key}"
246-
: key.ToString()!;
244+
string formattedKey = FormatKeyAsCSharpLiteral(key, parameterType);
247245
builder.AppendLine($" case {formattedKey}: return {value};");
248246
}
249247

@@ -318,4 +316,20 @@ internal static string FormatValueAsCSharpLiteral(string? value, ITypeSymbol ret
318316
_ => value
319317
};
320318
}
319+
320+
private static string FormatKeyAsCSharpLiteral(object key, ITypeSymbol? parameterType)
321+
{
322+
if (parameterType?.TypeKind == TypeKind.Enum)
323+
{
324+
return $"{parameterType.ToDisplayString()}.{key}";
325+
}
326+
327+
return key switch
328+
{
329+
bool b => b ? "true" : "false",
330+
// SyntaxFactory.Literal handles escaping and quoting (e.g. "hello" → "\"hello\"")
331+
string s => SyntaxFactory.Literal(s).Text,
332+
_ => key.ToString()!
333+
};
334+
}
321335
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
using MattSourceGenHelpers.Abstractions;
2+
// ReSharper disable ConvertClosureToMethodGroup
3+
4+
namespace MattSourceGenHelpers.Tests;
5+
6+
[TestFixture]
7+
public class BoolSwitchKeyTests
8+
{
9+
[TestCase(true, "Yes")]
10+
[TestCase(false, "No")]
11+
public void BoolSwitchKey_ProducesExpectedRuntimeOutput(bool flag, string expected)
12+
{
13+
string result = TestBoolSwitchClass.GetBoolLabel(flag);
14+
Assert.That(result, Is.EqualTo(expected));
15+
}
16+
17+
[Test]
18+
public void BoolSwitchKey_ProducesExpectedGeneratedCode()
19+
{
20+
string generatedCode = GeneratedCodeTestHelper.ReadGeneratedCode("TestBoolSwitchClass_GetBoolLabel.g.cs");
21+
string expectedCode = """
22+
namespace MattSourceGenHelpers.Tests;
23+
24+
static partial class TestBoolSwitchClass
25+
{
26+
public static partial string GetBoolLabel(bool flag)
27+
{
28+
switch (flag)
29+
{
30+
case true: return "Yes";
31+
case false: return "No";
32+
default: return "Unknown";
33+
}
34+
}
35+
}
36+
""".ReplaceLineEndings("\n").TrimEnd();
37+
38+
Assert.That(generatedCode, Is.EqualTo(expectedCode));
39+
}
40+
}
41+
42+
public static partial class TestBoolSwitchClass
43+
{
44+
public static partial string GetBoolLabel(bool flag);
45+
46+
[GeneratesMethod(nameof(GetBoolLabel))]
47+
[SwitchCase(arg1: true)]
48+
[SwitchCase(arg1: false)]
49+
static string GetBoolLabel_Generator(bool flag) => flag ? "Yes" : "No";
50+
51+
[GeneratesMethod(nameof(GetBoolLabel))]
52+
[SwitchDefault]
53+
static Func<bool, string> GetBoolLabel_Default() => _ => "Unknown";
54+
}
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
using MattSourceGenHelpers.Abstractions;
2+
3+
namespace MattSourceGenHelpers.Tests;
4+
5+
[TestFixture]
6+
public class EdgeCaseSimplePatternTests
7+
{
8+
// -----------------------------------------------------------------------
9+
// Void return type
10+
// -----------------------------------------------------------------------
11+
12+
[Test]
13+
public void VoidReturnGenerator_MethodIsCallable()
14+
{
15+
TestVoidClass instance = new TestVoidClass();
16+
Assert.DoesNotThrow(() => instance.DoSomething());
17+
}
18+
19+
[Test]
20+
public void VoidReturnGenerator_ProducesExpectedGeneratedCode()
21+
{
22+
string generatedCode = GeneratedCodeTestHelper.ReadGeneratedCode("TestVoidClass_DoSomething.g.cs");
23+
string expectedCode = """
24+
namespace MattSourceGenHelpers.Tests;
25+
26+
partial class TestVoidClass
27+
{
28+
public partial void DoSomething()
29+
{
30+
}
31+
}
32+
""".ReplaceLineEndings("\n").TrimEnd();
33+
34+
Assert.That(generatedCode, Is.EqualTo(expectedCode));
35+
}
36+
37+
// -----------------------------------------------------------------------
38+
// Bool return type
39+
// -----------------------------------------------------------------------
40+
41+
[Test]
42+
public void BoolReturnGenerator_ProducesExpectedRuntimeOutput()
43+
{
44+
bool result = TestBoolReturnClass.IsEnabled();
45+
Assert.That(result, Is.True);
46+
}
47+
48+
[Test]
49+
public void BoolReturnGenerator_ProducesExpectedGeneratedCode()
50+
{
51+
string generatedCode = GeneratedCodeTestHelper.ReadGeneratedCode("TestBoolReturnClass_IsEnabled.g.cs");
52+
string expectedCode = """
53+
namespace MattSourceGenHelpers.Tests;
54+
55+
static partial class TestBoolReturnClass
56+
{
57+
public static partial bool IsEnabled()
58+
{
59+
return true;
60+
}
61+
}
62+
""".ReplaceLineEndings("\n").TrimEnd();
63+
64+
Assert.That(generatedCode, Is.EqualTo(expectedCode));
65+
}
66+
67+
// -----------------------------------------------------------------------
68+
// Char return type
69+
// -----------------------------------------------------------------------
70+
71+
[Test]
72+
public void CharReturnGenerator_ProducesExpectedRuntimeOutput()
73+
{
74+
char result = TestCharReturnClass.GetSymbol();
75+
Assert.That(result, Is.EqualTo('A'));
76+
}
77+
78+
[Test]
79+
public void CharReturnGenerator_ProducesExpectedGeneratedCode()
80+
{
81+
string generatedCode = GeneratedCodeTestHelper.ReadGeneratedCode("TestCharReturnClass_GetSymbol.g.cs");
82+
string expectedCode = """
83+
namespace MattSourceGenHelpers.Tests;
84+
85+
static partial class TestCharReturnClass
86+
{
87+
public static partial char GetSymbol()
88+
{
89+
return 'A';
90+
}
91+
}
92+
""".ReplaceLineEndings("\n").TrimEnd();
93+
94+
Assert.That(generatedCode, Is.EqualTo(expectedCode));
95+
}
96+
97+
// -----------------------------------------------------------------------
98+
// Internal method accessibility
99+
// -----------------------------------------------------------------------
100+
101+
[Test]
102+
public void InternalMethodGenerator_ProducesExpectedRuntimeOutput()
103+
{
104+
TestInternalMethodClass instance = new TestInternalMethodClass();
105+
string result = instance.GetValue();
106+
Assert.That(result, Is.EqualTo("internal_value"));
107+
}
108+
109+
[Test]
110+
public void InternalMethodGenerator_ProducesExpectedGeneratedCode()
111+
{
112+
string generatedCode = GeneratedCodeTestHelper.ReadGeneratedCode("TestInternalMethodClass_GetValue.g.cs");
113+
string expectedCode = """
114+
namespace MattSourceGenHelpers.Tests;
115+
116+
partial class TestInternalMethodClass
117+
{
118+
internal partial string GetValue()
119+
{
120+
return "internal_value";
121+
}
122+
}
123+
""".ReplaceLineEndings("\n").TrimEnd();
124+
125+
Assert.That(generatedCode, Is.EqualTo(expectedCode));
126+
}
127+
}
128+
129+
public partial class TestVoidClass
130+
{
131+
public partial void DoSomething();
132+
133+
[GeneratesMethod(nameof(DoSomething))]
134+
private static void DoSomething_Generator() { }
135+
}
136+
137+
public static partial class TestBoolReturnClass
138+
{
139+
public static partial bool IsEnabled();
140+
141+
[GeneratesMethod(nameof(IsEnabled))]
142+
private static bool IsEnabled_Generator() => true;
143+
}
144+
145+
public static partial class TestCharReturnClass
146+
{
147+
public static partial char GetSymbol();
148+
149+
[GeneratesMethod(nameof(GetSymbol))]
150+
private static char GetSymbol_Generator() => 'A';
151+
}
152+
153+
public partial class TestInternalMethodClass
154+
{
155+
internal partial string GetValue();
156+
157+
[GeneratesMethod(nameof(GetValue))]
158+
private static string GetValue_Generator() => "internal_value";
159+
}

0 commit comments

Comments
 (0)