Skip to content

Commit 531a93a

Browse files
authored
Add shared test infra for RDF and RDG (#46838)
1 parent ffa0ec3 commit 531a93a

File tree

5 files changed

+272
-244
lines changed

5 files changed

+272
-244
lines changed
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
using Microsoft.CodeAnalysis;
4+
namespace Microsoft.AspNetCore.Http.Generators.Tests;
5+
6+
public class CompileTimeGeneratorTests : RequestDelegateGeneratorTests
7+
{
8+
protected override bool IsGeneratorEnabled { get; } = true;
9+
10+
[Fact]
11+
public async Task MapAction_UnknownParameter_EmitsDiagnostic_NoSource()
12+
{
13+
// This will eventually be handled by the EndpointParameterSource.JsonBodyOrService.
14+
// All parameters should theoretically be handleable with enough "Or"s in the future
15+
// we'll remove this test and diagnostic.
16+
var source = """
17+
app.MapGet("/", (IServiceProvider provider) => "Hello world!");
18+
""";
19+
var expectedBody = "Hello world!";
20+
var (generatorRunResult, compilation) = await RunGeneratorAsync(source);
21+
22+
// Emits diagnostic but generates no source
23+
var result = Assert.IsType<GeneratorRunResult>(generatorRunResult);
24+
var diagnostic = Assert.Single(result.Diagnostics);
25+
Assert.Equal(DiagnosticDescriptors.GetUnableToResolveParameterDescriptor("provider").Id, diagnostic.Id);
26+
Assert.Empty(result.GeneratedSources);
27+
28+
// Falls back to runtime-generated endpoint
29+
var endpoint = GetEndpointFromCompilation(compilation, false);
30+
31+
var httpContext = CreateHttpContext();
32+
await endpoint.RequestDelegate(httpContext);
33+
await VerifyResponseBodyAsync(httpContext, expectedBody);
34+
}
35+
36+
[Fact]
37+
public async Task MapGet_WithRequestDelegate_DoesNotGenerateSources()
38+
{
39+
var (generatorRunResult, compilation) = await RunGeneratorAsync("""
40+
app.MapGet("/hello", (HttpContext context) => Task.CompletedTask);
41+
""");
42+
var results = Assert.IsType<GeneratorRunResult>(generatorRunResult);
43+
Assert.Empty(GetStaticEndpoints(results, GeneratorSteps.EndpointModelStep));
44+
45+
var endpoint = GetEndpointFromCompilation(compilation, false);
46+
47+
var httpContext = CreateHttpContext();
48+
await endpoint.RequestDelegate(httpContext);
49+
await VerifyResponseBodyAsync(httpContext, "");
50+
}
51+
52+
// Todo: Move this to a shared test that checks metadata once that is supported
53+
// in the source generator.
54+
[Theory]
55+
[InlineData(@"app.MapGet(""/"", () => Console.WriteLine(""Returns void""));", null)]
56+
[InlineData(@"app.MapGet(""/"", () => TypedResults.Ok(""Alright!""));", null)]
57+
[InlineData(@"app.MapGet(""/"", () => Results.NotFound(""Oops!""));", null)]
58+
[InlineData(@"app.MapGet(""/"", () => Task.FromResult(new Todo() { Name = ""Test Item""}));", "application/json")]
59+
[InlineData(@"app.MapGet(""/"", () => ""Hello world!"");", "text/plain")]
60+
public async Task MapAction_ProducesCorrectContentType(string source, string expectedContentType)
61+
{
62+
var (result, compilation) = await RunGeneratorAsync(source);
63+
64+
VerifyStaticEndpointModel(result, endpointModel =>
65+
{
66+
Assert.Equal("/", endpointModel.RoutePattern);
67+
Assert.Equal("MapGet", endpointModel.HttpMethod);
68+
Assert.Equal(expectedContentType, endpointModel.Response.ContentType);
69+
});
70+
}
71+
72+
[Fact]
73+
public async Task MapAction_ExplicitRouteParamWithInvalidName_SimpleReturn()
74+
{
75+
var source = $$"""app.MapGet("/{routeValue}", ([FromRoute(Name = "invalidName" )] string parameterName) => parameterName);""";
76+
var (_, compilation) = await RunGeneratorAsync(source);
77+
var endpoint = GetEndpointFromCompilation(compilation);
78+
79+
var httpContext = CreateHttpContext();
80+
81+
var exception = await Assert.ThrowsAsync<InvalidOperationException>(() => endpoint.RequestDelegate(httpContext));
82+
Assert.Equal("'invalidName' is not a route parameter.", exception.Message);
83+
}
84+
}
Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,10 @@
55

66
namespace Microsoft.AspNetCore.Http.Generators.Tests;
77

8-
public class RequestDelegateGeneratorIncrementalityTests : RequestDelegateGeneratorTestBase
8+
public class CompileTimeIncrementalityTests : RequestDelegateGeneratorTestBase
99
{
10+
protected override bool IsGeneratorEnabled { get; } = true;
11+
1012
[Fact]
1113
public async Task MapAction_SameReturnType_DoesNotTriggerUpdate()
1214
{
@@ -59,5 +61,5 @@ public async Task MapAction_ChangeBodyParamNullability_TriggersUpdate()
5961
Assert.All(outputSteps, (value) => Assert.Equal(IncrementalStepRunReason.New, value.Reason));
6062
}
6163

62-
private static IEnumerable<(object Value, IncrementalStepRunReason Reason)> GetRunStepOutputs(GeneratorRunResult result) => result.TrackedOutputSteps.SelectMany(step => step.Value).SelectMany(value => value.Outputs);
64+
private static IEnumerable<(object Value, IncrementalStepRunReason Reason)> GetRunStepOutputs(GeneratorRunResult? result) => result?.TrackedOutputSteps.SelectMany(step => step.Value).SelectMany(value => value.Outputs);
6365
}

src/Http/Http.Extensions/test/RequestDelegateGenerator/RequestDelegateGeneratorTestBase.cs

Lines changed: 39 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,21 +23,30 @@
2323

2424
namespace Microsoft.AspNetCore.Http.Generators.Tests;
2525

26-
public class RequestDelegateGeneratorTestBase : LoggedTest
26+
public abstract class RequestDelegateGeneratorTestBase : LoggedTest
2727
{
28-
internal static async Task<(GeneratorRunResult, Compilation)> RunGeneratorAsync(string sources, params string[] updatedSources)
28+
protected abstract bool IsGeneratorEnabled { get; }
29+
30+
internal async Task<(GeneratorRunResult?, Compilation)> RunGeneratorAsync(string sources, params string[] updatedSources)
2931
{
3032
var compilation = await CreateCompilationAsync(sources);
31-
var generator = new RequestDelegateGenerator().AsSourceGenerator();
3233

33-
// Enable the source generator in tests
34+
// Return the compilation immediately if
35+
// the generator is not enabled.
36+
if (!IsGeneratorEnabled)
37+
{
38+
return (null, compilation);
39+
}
40+
41+
// Configure the generator driver and run
42+
// the compilation with it if the generator
43+
// is enabled.
44+
var generator = new RequestDelegateGenerator().AsSourceGenerator();
3445
GeneratorDriver driver = CSharpGeneratorDriver.Create(generators: new[]
3546
{
3647
generator
3748
},
3849
driverOptions: new GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps: true));
39-
40-
// Run the source generator
4150
driver = driver.RunGeneratorsAndUpdateCompilation(compilation, out var updatedCompilation,
4251
out var _);
4352
foreach (var updatedSource in updatedSources)
@@ -72,13 +81,30 @@ internal static StaticRouteHandlerModel.Endpoint[] GetStaticEndpoints(GeneratorR
7281
return Array.Empty<StaticRouteHandlerModel.Endpoint>();
7382
}
7483

75-
internal static Endpoint GetEndpointFromCompilation(Compilation compilation, bool expectSourceKey = true) =>
76-
Assert.Single(GetEndpointsFromCompilation(compilation, expectSourceKey));
84+
internal static void VerifyStaticEndpointModel(GeneratorRunResult? result, Action<StaticRouteHandlerModel.Endpoint> runAssertions)
85+
{
86+
if (result.HasValue)
87+
{
88+
runAssertions(GetStaticEndpoint(result.Value, GeneratorSteps.EndpointModelStep));
89+
}
90+
}
7791

78-
internal static Endpoint[] GetEndpointsFromCompilation(Compilation compilation, bool expectSourceKey = true)
92+
internal static void VerifyStaticEndpointModels(GeneratorRunResult? result, Action<StaticRouteHandlerModel.Endpoint[]> runAssertions)
93+
{
94+
if (result.HasValue)
95+
{
96+
runAssertions(GetStaticEndpoints(result.Value, GeneratorSteps.EndpointModelStep));
97+
}
98+
}
99+
100+
internal Endpoint GetEndpointFromCompilation(Compilation compilation, bool? expectSourceKeyOverride = null) =>
101+
Assert.Single(GetEndpointsFromCompilation(compilation, expectSourceKeyOverride));
102+
103+
internal Endpoint[] GetEndpointsFromCompilation(Compilation compilation, bool? expectSourceKeyOverride = null)
79104
{
80105
var assemblyName = compilation.AssemblyName!;
81106
var symbolsName = Path.ChangeExtension(assemblyName, "pdb");
107+
var expectSourceKey = (expectSourceKeyOverride ?? true) && IsGeneratorEnabled;
82108

83109
var output = new MemoryStream();
84110
var pdb = new MemoryStream();
@@ -353,6 +379,10 @@ private static Task<Compilation> CreateCompilationAsync(string sources)
353379

354380
internal async Task VerifyAgainstBaselineUsingFile(Compilation compilation, [CallerMemberName] string callerName = "")
355381
{
382+
if (!IsGeneratorEnabled)
383+
{
384+
return;
385+
}
356386
var baselineFilePath = Path.Combine("RequestDelegateGenerator", "Baselines", $"{callerName}.generated.txt");
357387
var generatedSyntaxTree = compilation.SyntaxTrees.Last();
358388
var generatedCode = await generatedSyntaxTree.GetTextAsync();

0 commit comments

Comments
 (0)