Skip to content

Commit b1bce13

Browse files
Add snapshot tests and fix empty line generation (#8)
1 parent 32691e9 commit b1bce13

11 files changed

+459
-36
lines changed

.gitignore

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,4 +55,10 @@ coverage/
5555
# NUnit
5656
*.VisualState.xml
5757
TestResult.xml
58-
nunit-*.xml
58+
nunit-*.xml
59+
60+
# Verify snapshots (received files)
61+
*.received.*
62+
63+
# User-specific files
64+
*.user

src/Directory.Packages.props

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
</PropertyGroup>
55
<ItemGroup>
66
<!-- Analyzers -->
7+
<PackageVersion Include="Endpointer" Version="0.1.0" />
78
<PackageVersion Include="Meziantou.Analyzer" Version="2.0.285" />
89
<PackageVersion Include="SonarAnalyzer.CSharp" Version="10.18.0.131500" />
910
<PackageVersion Include="Roslynator.Analyzers" Version="4.15.0" />
@@ -19,5 +20,8 @@
1920
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="5.0.0" />
2021
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Testing" Version="10.0.2" />
2122
<PackageVersion Include="Microsoft.Extensions.TimeProvider.Testing" Version="10.2.0" />
23+
<PackageVersion Include="Basic.Reference.Assemblies.Net80" Version="1.8.4" />
24+
<PackageVersion Include="Verify.SourceGenerators" Version="2.5.0" />
25+
<PackageVersion Include="Verify.TUnit" Version="31.4.3" />
2226
</ItemGroup>
2327
</Project>

src/Endpointer.Generator.Tests/Endpointer.Generator.Tests.csproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@
99
<PackageReference Include="TUnit" />
1010
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Analyzer.Testing" />
1111
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" />
12+
<PackageReference Include="Basic.Reference.Assemblies.Net80" />
13+
<PackageReference Include="Verify.SourceGenerators" />
14+
<PackageReference Include="Verify.TUnit" />
1215
</ItemGroup>
1316

1417
<ItemGroup>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
[
2+
// <auto-generated/>
3+
#nullable enable
4+
5+
using Microsoft.AspNetCore.Routing;
6+
using Microsoft.Extensions.DependencyInjection;
7+
8+
namespace Endpointer;
9+
10+
public static class EndpointerExtensions
11+
{
12+
public static IServiceCollection AddEndpointer(this IServiceCollection services)
13+
{
14+
services.AddScoped<App.Users.GetUserEndpoint>();
15+
services.AddScoped<App.Users.CreateUserEndpoint>();
16+
services.AddScoped<App.Orders.GetOrderEndpoint>();
17+
18+
return services;
19+
}
20+
21+
public static IEndpointRouteBuilder MapEndpointer(this IEndpointRouteBuilder endpoints)
22+
{
23+
// App.Users.GetUserEndpoint
24+
new App.Users.GetUserEndpoint.Endpoint().MapEndpoint(endpoints);
25+
26+
// App.Users.CreateUserEndpoint
27+
new App.Users.CreateUserEndpoint.Endpoint().MapEndpoint(endpoints);
28+
29+
// App.Orders.GetOrderEndpoint
30+
new App.Orders.GetOrderEndpoint.Endpoint().MapEndpoint(endpoints);
31+
32+
return endpoints;
33+
}
34+
}
35+
36+
]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
[
2+
// <auto-generated/>
3+
#nullable enable
4+
5+
using Microsoft.AspNetCore.Routing;
6+
using Microsoft.Extensions.DependencyInjection;
7+
8+
namespace Endpointer;
9+
10+
public static class EndpointerExtensions
11+
{
12+
public static IServiceCollection AddEndpointer(this IServiceCollection services)
13+
{
14+
return services;
15+
}
16+
17+
public static IEndpointRouteBuilder MapEndpointer(this IEndpointRouteBuilder endpoints)
18+
{
19+
return endpoints;
20+
}
21+
}
22+
23+
]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
[
2+
// <auto-generated/>
3+
#nullable enable
4+
5+
using Microsoft.AspNetCore.Routing;
6+
using Microsoft.Extensions.DependencyInjection;
7+
8+
namespace Endpointer;
9+
10+
public static class EndpointerExtensions
11+
{
12+
public static IServiceCollection AddEndpointer(this IServiceCollection services)
13+
{
14+
services.AddScoped<TestApp.GetTimeEndpoint>();
15+
16+
return services;
17+
}
18+
19+
public static IEndpointRouteBuilder MapEndpointer(this IEndpointRouteBuilder endpoints)
20+
{
21+
// TestApp.GetTimeEndpoint
22+
new TestApp.GetTimeEndpoint.Endpoint().MapEndpoint(endpoints);
23+
24+
return endpoints;
25+
}
26+
}
27+
28+
]
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
[
2+
// <auto-generated/>
3+
#nullable enable
4+
5+
using Microsoft.AspNetCore.Routing;
6+
7+
namespace Endpointer;
8+
9+
/// <summary>
10+
/// Marker interface for endpoint classes to be discovered by the source generator.
11+
/// </summary>
12+
public interface IEndpoint
13+
{
14+
void MapEndpoint(IEndpointRouteBuilder endpoints);
15+
}
16+
]
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
using Basic.Reference.Assemblies;
2+
using Microsoft.CodeAnalysis;
3+
using Microsoft.CodeAnalysis.CSharp;
4+
5+
namespace Endpointer.Generator.Tests;
6+
7+
public class EndpointerGeneratorSnapshotTests
8+
{
9+
// Stub for ASP.NET Core types needed in test compilation
10+
private const string AspNetCoreStubs = """
11+
namespace Microsoft.AspNetCore.Routing
12+
{
13+
public interface IEndpointRouteBuilder { }
14+
}
15+
namespace Microsoft.Extensions.DependencyInjection
16+
{
17+
public interface IServiceCollection { }
18+
19+
public static class ServiceCollectionExtensions
20+
{
21+
public static IServiceCollection AddScoped<T>(this IServiceCollection services) => services;
22+
}
23+
}
24+
""";
25+
26+
[Test]
27+
public Task IEndpointInterface_MatchesSnapshot()
28+
{
29+
var (result, _) = RunGenerator("");
30+
31+
var generatedSources = result.Results[0].GeneratedSources
32+
.Where(s => string.Equals(s.HintName, "IEndpoint.g.cs", StringComparison.Ordinal))
33+
.Select(s => s.SourceText.ToString());
34+
35+
return Verify(generatedSources);
36+
}
37+
38+
[Test]
39+
public Task EndpointerRegistration_WithNoEndpoints_MatchesSnapshot()
40+
{
41+
var (result, _) = RunGenerator("");
42+
43+
var generatedSources = result.Results[0].GeneratedSources
44+
.Where(s => string.Equals(s.HintName, "EndpointerRegistration.g.cs", StringComparison.Ordinal))
45+
.Select(s => s.SourceText.ToString());
46+
47+
return Verify(generatedSources);
48+
}
49+
50+
[Test]
51+
public Task EndpointerRegistration_WithSingleEndpoint_MatchesSnapshot()
52+
{
53+
const string source = """
54+
using Endpointer;
55+
using Microsoft.AspNetCore.Routing;
56+
57+
namespace TestApp;
58+
59+
public class GetTimeEndpoint
60+
{
61+
public class Endpoint : IEndpoint
62+
{
63+
public void MapEndpoint(IEndpointRouteBuilder endpoints) { }
64+
}
65+
}
66+
""";
67+
68+
var (result, _) = RunGenerator(source);
69+
70+
var generatedSources = result.Results[0].GeneratedSources
71+
.Where(s => string.Equals(s.HintName, "EndpointerRegistration.g.cs", StringComparison.Ordinal))
72+
.Select(s => s.SourceText.ToString());
73+
74+
return Verify(generatedSources);
75+
}
76+
77+
[Test]
78+
public Task EndpointerRegistration_WithMultipleEndpoints_MatchesSnapshot()
79+
{
80+
const string source = """
81+
using Endpointer;
82+
using Microsoft.AspNetCore.Routing;
83+
84+
namespace App.Users
85+
{
86+
public class GetUserEndpoint
87+
{
88+
public class Endpoint : IEndpoint
89+
{
90+
public void MapEndpoint(IEndpointRouteBuilder endpoints) { }
91+
}
92+
}
93+
94+
public class CreateUserEndpoint
95+
{
96+
public class Endpoint : IEndpoint
97+
{
98+
public void MapEndpoint(IEndpointRouteBuilder endpoints) { }
99+
}
100+
}
101+
}
102+
103+
namespace App.Orders
104+
{
105+
public class GetOrderEndpoint
106+
{
107+
public class Endpoint : IEndpoint
108+
{
109+
public void MapEndpoint(IEndpointRouteBuilder endpoints) { }
110+
}
111+
}
112+
}
113+
""";
114+
115+
var (result, _) = RunGenerator(source);
116+
117+
var generatedSources = result.Results[0].GeneratedSources
118+
.Where(s => string.Equals(s.HintName, "EndpointerRegistration.g.cs", StringComparison.Ordinal))
119+
.Select(s => s.SourceText.ToString());
120+
121+
return Verify(generatedSources);
122+
}
123+
124+
private static (GeneratorDriverRunResult Result, Compilation OutputCompilation) RunGenerator(string source)
125+
{
126+
var syntaxTrees = new List<SyntaxTree>
127+
{
128+
CSharpSyntaxTree.ParseText(AspNetCoreStubs),
129+
};
130+
131+
if (!string.IsNullOrEmpty(source))
132+
{
133+
syntaxTrees.Add(CSharpSyntaxTree.ParseText(source));
134+
}
135+
136+
var compilation = CSharpCompilation.Create(
137+
assemblyName: "TestAssembly",
138+
syntaxTrees: syntaxTrees,
139+
references: Net80.References.All,
140+
options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));
141+
142+
var generator = new EndpointerGenerator();
143+
144+
GeneratorDriver driver = CSharpGeneratorDriver.Create(generator);
145+
driver = driver.RunGeneratorsAndUpdateCompilation(compilation, out var outputCompilation, out _);
146+
147+
return (driver.GetRunResult(), outputCompilation);
148+
}
149+
}

0 commit comments

Comments
 (0)