Skip to content

Compile SourceGenerator-tests to ensure that the sample itself is valid #8484

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

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ public static Task<IDictionary<int, Entity2>> GetEntityByIdAsync(
CancellationToken cancellationToken)
=> default!;

public static KeyValuePair<int, Entity2> CreateLookupKey(Entity1 key)
public static KeyValuePair<int, Entity2>? CreateLookupKey(Entity1 key)
=> default!;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,45 +6,48 @@ public class RequestMiddlewareTests
public async Task GenerateSource_RequestMiddleware_MatchesSnapshot()
{
await TestHelper.GetGeneratedSourceSnapshot(
"""
#nullable enable
using System.Threading.Tasks;
using HotChocolate;
using HotChocolate.Execution;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
[
"""
#nullable enable
using System.Threading.Tasks;
using HotChocolate;
using HotChocolate.Execution;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;

public class Program
{
public static void Main(string[] args)
public class Program
{
var builder = WebApplication.CreateBuilder(args);
builder.Services
.AddGraphQLServer()
.UseRequest<SomeRequestMiddleware>();
public static void Main(string[] args)
{
var builder = WebApplication.CreateBuilder(args);
builder.Services
.AddGraphQLServer()
.UseRequest<SomeRequestMiddleware>();
}
}
}

public class SomeRequestMiddleware(
RequestDelegate next,
#pragma warning disable CS9113
[SchemaService] Service1 service1,
[SchemaService] Service2? service2)
#pragma warning restore CS9113
{
public async ValueTask InvokeAsync(
IRequestContext context,
public class SomeRequestMiddleware(
RequestDelegate next,
#pragma warning disable CS9113
Service1 service1,
Service2? service2)
[SchemaService] Service1 service1,
[SchemaService] Service2? service2)
#pragma warning restore CS9113
{
await next(context);
public async ValueTask InvokeAsync(
RequestContext context,
#pragma warning disable CS9113
Service1 service1,
Service2? service2)
#pragma warning restore CS9113
{
await next(context);
}
}
}

public class Service1;
public class Service2;
""").MatchMarkdownAsync();
public class Service1;
public class Service2;
"""
],
enableInterceptors: true).MatchMarkdownAsync();
}
}
62 changes: 59 additions & 3 deletions src/HotChocolate/Core/test/Types.Analyzers.Tests/TestHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,15 @@
using HotChocolate.Data.Filters;
using HotChocolate.Execution;
using HotChocolate.Execution.Configuration;
using HotChocolate.Execution.Processing;
using HotChocolate.Features;
using HotChocolate.Language;
using HotChocolate.Types.Analyzers;
using HotChocolate.Types.Pagination;
using Microsoft.AspNetCore.Builder;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.Extensions.DependencyInjection;

namespace HotChocolate.Types;
Expand All @@ -28,7 +32,10 @@ public static Snapshot GetGeneratedSourceSnapshot([StringSyntax("csharp")] strin
return GetGeneratedSourceSnapshot([sourceText]);
}

public static Snapshot GetGeneratedSourceSnapshot(string[] sourceTexts, string? assemblyName = "Tests")
public static Snapshot GetGeneratedSourceSnapshot(
string[] sourceTexts,
string? assemblyName = "Tests",
bool enableInterceptors = false)
{
IEnumerable<PortableExecutableReference> references =
[
Expand All @@ -39,17 +46,38 @@ public static Snapshot GetGeneratedSourceSnapshot(string[] sourceTexts, string?
#elif NET10_0
.. Net100.References.All,
#endif
// HotChocolate.Primitives
MetadataReference.CreateFromFile(typeof(ITypeSystemMember).Assembly.Location),

// HotChocolate.Execution
MetadataReference.CreateFromFile(typeof(RequestDelegate).Assembly.Location),

// HotChocolate.Execution.Abstractions
MetadataReference.CreateFromFile(typeof(RequestContext).Assembly.Location),

// HotChocolate.Execution.Processing
MetadataReference.CreateFromFile(typeof(HotChocolateExecutionSelectionExtensions).Assembly.Location),

// HotChocolate.Execution.Abstractions
MetadataReference.CreateFromFile(typeof(IRequestExecutorBuilder).Assembly.Location),

// HotChocolate.Execution.DependencyInjection
MetadataReference.CreateFromFile(typeof(RequestExecutorBuilderExtensions).Assembly.Location),

// HotChocolate.Types
MetadataReference.CreateFromFile(typeof(ObjectTypeAttribute).Assembly.Location),
MetadataReference.CreateFromFile(typeof(Connection).Assembly.Location),
MetadataReference.CreateFromFile(typeof(PageConnection<>).Assembly.Location),

// HotChocolate.Types.Abstractions
MetadataReference.CreateFromFile(typeof(ISchemaDefinition).Assembly.Location),

// HotChocolate.Features
MetadataReference.CreateFromFile(typeof(IFeatureProvider).Assembly.Location),

// HotChocolate.Language
MetadataReference.CreateFromFile(typeof(OperationType).Assembly.Location),

// HotChocolate.Abstractions
MetadataReference.CreateFromFile(typeof(ParentAttribute).Assembly.Location),

Expand All @@ -64,6 +92,7 @@ public static Snapshot GetGeneratedSourceSnapshot(string[] sourceTexts, string?
// GreenDonut.Data
MetadataReference.CreateFromFile(typeof(PagingArguments).Assembly.Location),
MetadataReference.CreateFromFile(typeof(IPredicateBuilder).Assembly.Location),
MetadataReference.CreateFromFile(typeof(DefaultPredicateBuilder).Assembly.Location),

// HotChocolate.Data
MetadataReference.CreateFromFile(typeof(IFilterContext).Assembly.Location),
Expand All @@ -76,9 +105,18 @@ public static Snapshot GetGeneratedSourceSnapshot(string[] sourceTexts, string?
];

// Create a Roslyn compilation for the syntax tree.
var parseOptions = !enableInterceptors
? CSharpParseOptions.Default
: CSharpParseOptions.Default
.WithPreprocessorSymbols("InterceptorsPreviewFeature")
.WithFeatures(new Dictionary<string, string>
{
["InterceptorsNamespaces"] = "HotChocolate.Execution.Generated"
});

var compilation = CSharpCompilation.Create(
assemblyName: assemblyName,
syntaxTrees: sourceTexts.Select(s => CSharpSyntaxTree.ParseText(s)),
syntaxTrees: sourceTexts.Select(s => CSharpSyntaxTree.ParseText(s, parseOptions)),
references);

// Create an instance of our GraphQLServerGenerator incremental source generator.
Expand All @@ -91,7 +129,25 @@ public static Snapshot GetGeneratedSourceSnapshot(string[] sourceTexts, string?
driver = driver.RunGenerators(compilation);

// Create a snapshot.
return CreateSnapshot(compilation, driver);
var snapshot = CreateSnapshot(compilation, driver);

// Finally, compile the entire assembly (original code + generated code) to check
// if the sample is valid as a whole
var updatedCompilation = compilation.AddSyntaxTrees(
driver.GetRunResult()
.Results
.SelectMany(r => r.GeneratedSources)
.Select(gs => CSharpSyntaxTree.ParseText(gs.SourceText, parseOptions, path: gs.HintName))
);

using var dllStream = new MemoryStream();
var emitResult = updatedCompilation.Emit(dllStream);
if (!emitResult.Success || emitResult.Diagnostics.Any())
{
AddDiagnosticsToSnapshot(snapshot, emitResult.Diagnostics, "Assembly Emit Diagnostics");
}

return snapshot;
}

private static Snapshot CreateSnapshot(CSharpCompilation compilation, GeneratorDriver driver)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,6 @@ namespace TestNamespace
{
_services = services ??
throw new global::System.ArgumentNullException(nameof(services));

global::GreenDonut.PromiseCacheObserver
.Create<int, global::TestNamespace.Entity2, global::TestNamespace.Entity1>(global::TestNamespace.TestClass.CreateLookupKey, this)
.Accept(this);
}

protected override async global::System.Threading.Tasks.ValueTask FetchAsync(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
# GenerateSource_RequestMiddleware_MatchesSnapshot

## HotChocolateMiddleware.735550c.g.cs

```csharp
// <auto-generated/>

Expand All @@ -28,10 +26,9 @@ namespace HotChocolate.Execution.Generated
var middleware = new global::SomeRequestMiddleware(next, cp1, cp2);
return async context =>
{
var ip0 = context.RequestServices.GetRequiredService<IRequestContext>();
var ip1 = context.RequestServices.GetRequiredService<global::Service1>();
var ip2 = context.RequestServices.GetService<global::Service2>();
await middleware.InvokeAsync(ip0, ip1, ip2).ConfigureAwait(false);
await middleware.InvokeAsync(context, ip1, ip2).ConfigureAwait(false);
};
};

Expand All @@ -52,43 +49,3 @@ namespace System.Runtime.CompilerServices
}
#pragma warning restore CS9113 // Parameter is unread.
```

## Compilation Diagnostics

```json
[
{
"Id": "CS0246",
"Title": "",
"Severity": "Error",
"WarningLevel": 0,
"Location": ": (26,8)-(26,23)",
"HelpLinkUri": "https://msdn.microsoft.com/query/roslyn.query?appId=roslyn&k=k(CS0246)",
"MessageFormat": "The type or namespace name '{0}' could not be found (are you missing a using directive or an assembly reference?)",
"Message": "The type or namespace name 'IRequestContext' could not be found (are you missing a using directive or an assembly reference?)",
"Category": "Compiler",
"CustomTags": [
"Compiler",
"Telemetry",
"NotConfigurable"
]
},
{
"Id": "CS1061",
"Title": "",
"Severity": "Error",
"WarningLevel": 0,
"Location": ": (14,13)-(14,46)",
"HelpLinkUri": "https://msdn.microsoft.com/query/roslyn.query?appId=roslyn&k=k(CS1061)",
"MessageFormat": "'{0}' does not contain a definition for '{1}' and no accessible extension method '{1}' accepting a first argument of type '{0}' could be found (are you missing a using directive or an assembly reference?)",
"Message": "'IRequestExecutorBuilder' does not contain a definition for 'UseRequest' and no accessible extension method 'UseRequest' accepting a first argument of type 'IRequestExecutorBuilder' could be found (are you missing a using directive or an assembly reference?)",
"Category": "Compiler",
"CustomTags": [
"Compiler",
"Telemetry",
"NotConfigurable"
]
}
]
```