Skip to content

Commit 25d74db

Browse files
author
mattana
committed
feat: Enhance TypeHelpers and add extensive unit tests for ActorVisitor and BlockGraph functionalities
1 parent 1d17193 commit 25d74db

6 files changed

Lines changed: 765 additions & 5 deletions

File tree

ActorSrcGen/Helpers/TypeHelpers.cs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,16 @@ public static string RenderTypename(this GenericNameSyntax? ts, Compilation comp
7373
if (ts is null)
7474
return "";
7575
var x = ts.ToSymbol(compilation);
76-
if (stripTask && x is not null && x is INamedTypeSymbol nts && nts.Name == "Task")
76+
if (x is null)
77+
{
78+
if (stripTask && string.Equals(ts.Identifier.Text, "Task", StringComparison.Ordinal) && ts.TypeArgumentList.Arguments.Count > 0)
79+
{
80+
return ts.TypeArgumentList.Arguments[0].ToString();
81+
}
82+
return ts.ToString();
83+
}
84+
85+
if (stripTask && x is INamedTypeSymbol nts && nts.Name == "Task")
7786
{
7887
return nts.TypeArguments[0].ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat);
7988
}

specs/001-generator-reliability-hardening/tasks.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -327,20 +327,20 @@ Phase 6: POLISH & CROSS-CUTTING (depends on all stories)
327327

328328
### Expand Unit Test Suite
329329

330-
- [ ] T056 [US3] Create comprehensive `tests/ActorSrcGen.Tests/Unit/RoslynExtensionTests.cs`:
330+
- [X] T056 [US3] Create comprehensive `tests/ActorSrcGen.Tests/Unit/RoslynExtensionTests.cs`:
331331
- Test all extensions in RoslynExtensions.cs
332332
- Test all extensions in DomainRoslynExtensions.cs
333333
- 10+ tests total covering all code paths
334-
- [ ] T057 [US3] Create `tests/ActorSrcGen.Tests/Unit/TypeHelperTests.cs`:
334+
- [X] T057 [US3] Create `tests/ActorSrcGen.Tests/Unit/TypeHelperTests.cs`:
335335
- Test type name rendering for all scenarios
336336
- Test ImmutableArray<T> rendering
337337
- 8+ tests total
338-
- [ ] T058 [US3] Expand ActorVisitorTests with edge cases:
338+
- [X] T058 [US3] Expand ActorVisitorTests with edge cases:
339339
- Actor with no methods
340340
- Actor with only Step methods (no FirstStep)
341341
- Actor with conflicting attributes
342342
- 5 additional tests
343-
- [ ] T059 [US3] Create `tests/ActorSrcGen.Tests/Unit/BlockGraphConstructionTests.cs`:
343+
- [X] T059 [US3] Create `tests/ActorSrcGen.Tests/Unit/BlockGraphConstructionTests.cs`:
344344
- Test BlockGraph from various actor patterns
345345
- Test block linking logic
346346
- Test cycle detection (if applicable)

tests/ActorSrcGen.Tests/Unit/ActorVisitorTests.cs

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,4 +107,76 @@ public partial class Multi
107107
var blockB = actor.StepNodes.First(b => b.Method.Name == "B");
108108
Assert.Contains(actor.StepNodes.First(b => b.Method.Name == "C").Id, blockB.NextBlocks);
109109
}
110+
111+
[Fact]
112+
public void VisitActor_WithOnlyStep_NoEntry_ReturnsASG0002()
113+
{
114+
var source = """
115+
using ActorSrcGen;
116+
public partial class NoEntry
117+
{
118+
[Step]
119+
public string Step1(string input) => input;
120+
}
121+
""";
122+
123+
var sas = CreateActor(source);
124+
var visitor = new ActorVisitor();
125+
126+
var result = visitor.VisitActor(sas);
127+
128+
Assert.Empty(result.Actors);
129+
Assert.Contains(result.Diagnostics, d => d.Id == "ASG0002");
130+
}
131+
132+
[Fact]
133+
public void VisitActor_WithDuplicateInputTypes_ReturnsASG0001()
134+
{
135+
var source = """
136+
using ActorSrcGen;
137+
public partial class DuplicateInputs
138+
{
139+
[FirstStep]
140+
public string A(string input) => input;
141+
142+
[FirstStep]
143+
public string B(string input) => input + "b";
144+
145+
[LastStep]
146+
public string End(string input) => input;
147+
}
148+
""";
149+
150+
var sas = CreateActor(source);
151+
var visitor = new ActorVisitor();
152+
153+
var result = visitor.VisitActor(sas);
154+
155+
Assert.Single(result.Actors);
156+
Assert.Contains(result.Diagnostics, d => d.Id == "ASG0001");
157+
}
158+
159+
[Fact]
160+
public void VisitActor_WithOnlyIngest_NoSteps_ReturnsDiagnostics()
161+
{
162+
var source = """
163+
using System.Threading.Tasks;
164+
using ActorSrcGen;
165+
public partial class OnlyIngest
166+
{
167+
[Ingest]
168+
public static Task<string> PullAsync() => Task.FromResult("x");
169+
}
170+
""";
171+
172+
var sas = CreateActor(source);
173+
var visitor = new ActorVisitor();
174+
175+
var result = visitor.VisitActor(sas);
176+
177+
Assert.Empty(result.Actors);
178+
Assert.Equal(2, result.Diagnostics.Length);
179+
Assert.Contains(result.Diagnostics, d => d.Id == "ASG0001");
180+
Assert.Contains(result.Diagnostics, d => d.Id == "ASG0002");
181+
}
110182
}
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
using ActorSrcGen.Helpers;
2+
using ActorSrcGen.Model;
3+
using ActorSrcGen.Tests.Helpers;
4+
using Microsoft.CodeAnalysis;
5+
using Microsoft.CodeAnalysis.CSharp.Syntax;
6+
7+
namespace ActorSrcGen.Tests.Unit;
8+
9+
public class BlockGraphConstructionTests
10+
{
11+
private static ActorNode Visit(string source)
12+
{
13+
var compilation = CompilationHelper.CreateCompilation(source);
14+
var tree = compilation.SyntaxTrees.Single();
15+
var model = compilation.GetSemanticModel(tree);
16+
var classSyntax = tree.GetRoot().DescendantNodes().OfType<ClassDeclarationSyntax>().First();
17+
var classSymbol = (INamedTypeSymbol)model.GetDeclaredSymbol(classSyntax)!;
18+
var sas = new SyntaxAndSymbol(classSyntax, classSymbol, model);
19+
var visitor = new ActorVisitor();
20+
var result = visitor.VisitActor(sas);
21+
Assert.Single(result.Actors);
22+
return result.Actors[0];
23+
}
24+
25+
[Fact]
26+
public void WireBlocks_LinearChain_LinksInOrder()
27+
{
28+
var source = """
29+
using ActorSrcGen;
30+
31+
[Actor]
32+
public partial class Linear
33+
{
34+
[FirstStep]
35+
[NextStep(nameof(Middle))]
36+
public int Start(string input) => input.Length;
37+
38+
[Step]
39+
[NextStep(nameof(Finish))]
40+
public int Middle(int value) => value + 1;
41+
42+
[LastStep]
43+
public int Finish(int value) => value;
44+
}
45+
""";
46+
47+
var actor = Visit(source);
48+
var start = actor.StepNodes.First(b => b.Method.Name == "Start");
49+
var middle = actor.StepNodes.First(b => b.Method.Name == "Middle");
50+
var finish = actor.StepNodes.First(b => b.Method.Name == "Finish");
51+
52+
Assert.Single(start.NextBlocks);
53+
Assert.Equal(middle.Id, start.NextBlocks[0]);
54+
Assert.Single(middle.NextBlocks);
55+
Assert.Equal(finish.Id, middle.NextBlocks[0]);
56+
Assert.Empty(finish.NextBlocks);
57+
}
58+
59+
[Fact]
60+
public void WireBlocks_FanOut_SortsAndDeduplicates()
61+
{
62+
var source = """
63+
using ActorSrcGen;
64+
65+
[Actor]
66+
public partial class FanOut
67+
{
68+
[FirstStep]
69+
[NextStep(nameof(BranchA))]
70+
[NextStep(nameof(BranchB))]
71+
[NextStep(nameof(BranchA))]
72+
public string Start(string input) => input;
73+
74+
[Step]
75+
[NextStep(nameof(End))]
76+
public string BranchA(string input) => input + "a";
77+
78+
[Step]
79+
[NextStep(nameof(End))]
80+
public string BranchB(string input) => input + "b";
81+
82+
[LastStep]
83+
public string End(string input) => input;
84+
}
85+
""";
86+
87+
var actor = Visit(source);
88+
var start = actor.StepNodes.First(b => b.Method.Name == "Start");
89+
90+
Assert.Equal(2, start.NextBlocks.Length);
91+
Assert.Equal(start.NextBlocks.OrderBy(i => i).ToArray(), start.NextBlocks.ToArray());
92+
Assert.Equal(actor.StepNodes.First(b => b.Method.Name == "BranchA").Id, start.NextBlocks[0]);
93+
Assert.Equal(actor.StepNodes.First(b => b.Method.Name == "BranchB").Id, start.NextBlocks[1]);
94+
}
95+
96+
[Fact]
97+
public void WireBlocks_MissingTarget_IgnoresUnknown()
98+
{
99+
var source = """
100+
using ActorSrcGen;
101+
102+
[Actor]
103+
public partial class MissingTarget
104+
{
105+
[FirstStep]
106+
[NextStep("Missing")]
107+
public int Start(string input) => input.Length;
108+
}
109+
""";
110+
111+
var actor = Visit(source);
112+
var start = actor.StepNodes.First();
113+
114+
Assert.Empty(start.NextBlocks);
115+
}
116+
117+
[Fact]
118+
public void ResolveNodeType_TransformManyForCollections()
119+
{
120+
var source = """
121+
using System.Collections.Generic;
122+
using ActorSrcGen;
123+
124+
[Actor]
125+
public partial class Collections
126+
{
127+
[FirstStep]
128+
public IEnumerable<string> Start(string input)
129+
{
130+
yield return input;
131+
}
132+
}
133+
""";
134+
135+
var actor = Visit(source);
136+
var node = actor.StepNodes.First();
137+
138+
Assert.Equal(NodeType.TransformMany, node.NodeType);
139+
Assert.True(node.IsReturnTypeCollection);
140+
}
141+
}

0 commit comments

Comments
 (0)