From 2db8bc5302b54fbac46f7eeb816abcc71b08b8ff Mon Sep 17 00:00:00 2001
From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com>
Date: Sun, 1 Nov 2026 14:17:11 +0000
Subject: [PATCH] test(sourcegen): parse generator test fixtures with preview
language version in all harnesses (#7190)
---
tests/SharedTestHelpers/GeneratorTestInput.cs | 58 +++++++++++++++++
.../ShouldExtensionGeneratorTests.cs | 10 ++-
...rtions.Should.SourceGenerator.Tests.csproj | 8 ++-
.../AssertionExtensionGeneratorTests.cs | 3 +-
.../AssertionMethodGeneratorRunner.cs | 3 +-
.../CompileChecker.cs | 3 +-
.../GeneratorTestInputTests.cs | 63 +++++++++++++++++++
.../GlobalNamespaceRegressionTests.cs | 5 +-
...it.Assertions.SourceGenerator.Tests.csproj | 8 ++-
.../TestsBase.cs | 16 +++--
.../UnboundGenericAssertionFromTests.cs | 4 +-
.../XmlDocsRegressionTests.cs | 5 +-
.../AotConverterScanTests.cs | 8 ++-
.../TUnit.Core.SourceGenerator.Tests.csproj | 1 +
.../TestsBase.cs | 37 ++---------
.../MemberDiscoveryTests.cs | 3 +-
.../SnapshotTestBase.cs | 12 ++--
.../TUnit.Mocks.SourceGenerator.Tests.csproj | 1 +
18 files changed, 190 insertions(+), 58 deletions(-)
create mode 100644 tests/SharedTestHelpers/GeneratorTestInput.cs
create mode 100644 tests/TUnit.Assertions.SourceGenerator.Tests/GeneratorTestInputTests.cs
diff --git a/tests/SharedTestHelpers/GeneratorTestInput.cs b/tests/SharedTestHelpers/GeneratorTestInput.cs
new file mode 100644
index 00000000000..2e910834003
--- /dev/null
+++ b/tests/SharedTestHelpers/GeneratorTestInput.cs
@@ -0,0 +1,58 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace TUnit.Tests.Shared;
+
+///
+/// Parse options and input checks shared by the source-generator test harnesses.
+///
+///
+/// The repository builds with LangVersion=preview (Directory.Build.props). Default parse options reject newer
+/// syntax such as collection expressions, so a generator under test would see broken trees and the snapshots would
+/// not match a real build (#7162, #7190).
+///
+internal static class GeneratorTestInput
+{
+ /// Parse options matching the repository's language version.
+ public static CSharpParseOptions ParseOptions { get; } = new(LanguageVersion.Preview);
+
+ /// Parses with .
+ public static SyntaxTree Parse(string source, string path = "")
+ {
+ return CSharpSyntaxTree.ParseText(source, ParseOptions, path);
+ }
+
+ // Fixtures are not required to compile here: some reference packages or types that the harness
+ // does not load. Syntax errors and the binding errors that a too-old compiler reports for valid
+ // newer syntax are different, because the generator then sees a tree a real build never produces
+ // and the snapshot silently records the wrong output (#7162).
+ private static readonly HashSet UnsupportedSyntaxDiagnosticIds =
+ [
+ "CS0182", // attribute argument must be constant: collection expressions in attributes on Roslyn < 4.8
+ "CS0225", // params parameter must be an array: params collections on Roslyn < 4.12
+ "CS8652", // feature is in preview
+ ];
+
+ ///
+ /// Throws when the input has syntax errors, or binding errors that only a compiler too old for the syntax reports.
+ ///
+ public static void ThrowIfInputCannotBeParsed(Compilation compilation)
+ {
+ var errors = compilation.SyntaxTrees
+ .SelectMany(tree => tree.GetDiagnostics())
+ .Concat(compilation.GetDeclarationDiagnostics().Where(d => UnsupportedSyntaxDiagnosticIds.Contains(d.Id)))
+ .Where(d => d.Severity == DiagnosticSeverity.Error)
+ .Select(d => d.ToString())
+ .Distinct()
+ .ToArray();
+
+ if (errors.Length > 0)
+ {
+ throw new InvalidOperationException(
+ $"The test input does not parse as a real build would:{Environment.NewLine}{string.Join(Environment.NewLine, errors)}");
+ }
+ }
+}
diff --git a/tests/TUnit.Assertions.Should.SourceGenerator.Tests/ShouldExtensionGeneratorTests.cs b/tests/TUnit.Assertions.Should.SourceGenerator.Tests/ShouldExtensionGeneratorTests.cs
index 8d5f666e10f..61cfac7be06 100644
--- a/tests/TUnit.Assertions.Should.SourceGenerator.Tests/ShouldExtensionGeneratorTests.cs
+++ b/tests/TUnit.Assertions.Should.SourceGenerator.Tests/ShouldExtensionGeneratorTests.cs
@@ -433,9 +433,9 @@ private static async Task RunGenerator(string userSource, [CallerMemberN
// Polyfill copy compiled into this test assembly is internal — invisible to the synthetic
// GeneratorTest compilation. Inject a public copy so the attribute resolves consistently
// across all TFMs the test multi-targets.
- var inputTrees = new List { CSharpSyntaxTree.ParseText(userSource) };
+ var inputTrees = new List { GeneratorTestInput.Parse(userSource) };
#if NET8_0
- inputTrees.Add(CSharpSyntaxTree.ParseText("""
+ inputTrees.Add(GeneratorTestInput.Parse("""
namespace System.Runtime.CompilerServices;
[System.AttributeUsage(System.AttributeTargets.Method | System.AttributeTargets.Constructor | System.AttributeTargets.Property, Inherited = false)]
public sealed class OverloadResolutionPriorityAttribute : System.Attribute
@@ -452,7 +452,11 @@ public sealed class OverloadResolutionPriorityAttribute : System.Attribute
references: References,
options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));
- var driver = CSharpGeneratorDriver.Create(new ShouldExtensionGenerator());
+ GeneratorTestInput.ThrowIfInputCannotBeParsed(compilation);
+
+ var driver = CSharpGeneratorDriver.Create(
+ [new ShouldExtensionGenerator().AsSourceGenerator()],
+ parseOptions: GeneratorTestInput.ParseOptions);
driver = (CSharpGeneratorDriver)driver.RunGeneratorsAndUpdateCompilation(
compilation, out var updatedCompilation, out var diagnostics);
diff --git a/tests/TUnit.Assertions.Should.SourceGenerator.Tests/TUnit.Assertions.Should.SourceGenerator.Tests.csproj b/tests/TUnit.Assertions.Should.SourceGenerator.Tests/TUnit.Assertions.Should.SourceGenerator.Tests.csproj
index 4da6334b299..ae0f969e66e 100644
--- a/tests/TUnit.Assertions.Should.SourceGenerator.Tests/TUnit.Assertions.Should.SourceGenerator.Tests.csproj
+++ b/tests/TUnit.Assertions.Should.SourceGenerator.Tests/TUnit.Assertions.Should.SourceGenerator.Tests.csproj
@@ -19,6 +19,7 @@
+
@@ -28,7 +29,12 @@
-
+
+
+
+
diff --git a/tests/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.cs b/tests/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.cs
index 236347871d8..267ef924bb7 100644
--- a/tests/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.cs
+++ b/tests/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.cs
@@ -2,6 +2,7 @@
using Microsoft.CodeAnalysis.CSharp;
using TUnit.Assertions.SourceGenerator.Generators;
using TUnit.Assertions.SourceGenerator.Tests.Options;
+using TUnit.Tests.Shared;
namespace TUnit.Assertions.SourceGenerator.Tests;
@@ -86,7 +87,7 @@ public Task ConcreteReceiverWithExtraGeneric() => RunTest(
// bugs (mis-paired brackets, wrong default rendering, adjacent generic blocks)
// regardless of specific diagnostic id.
var trees = generatedFiles
- .Select(source => CSharpSyntaxTree.ParseText(source))
+ .Select(source => GeneratorTestInput.Parse(source))
.ToArray();
var compilation = CSharpCompilation.Create(
diff --git a/tests/TUnit.Assertions.SourceGenerator.Tests/AssertionMethodGeneratorRunner.cs b/tests/TUnit.Assertions.SourceGenerator.Tests/AssertionMethodGeneratorRunner.cs
index 655ef4930ce..e9b4ceef61b 100644
--- a/tests/TUnit.Assertions.SourceGenerator.Tests/AssertionMethodGeneratorRunner.cs
+++ b/tests/TUnit.Assertions.SourceGenerator.Tests/AssertionMethodGeneratorRunner.cs
@@ -3,6 +3,7 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using TUnit.Assertions.SourceGenerator.Generators;
+using TUnit.Tests.Shared;
namespace TUnit.Assertions.SourceGenerator.Tests;
@@ -26,7 +27,7 @@ public static (string[] Sources, ImmutableArray Diagnostics) Run(str
#else
string[] preprocessorSymbols = [];
#endif
- var parseOptions = CSharpParseOptions.Default.WithPreprocessorSymbols(preprocessorSymbols);
+ var parseOptions = GeneratorTestInput.ParseOptions.WithPreprocessorSymbols(preprocessorSymbols);
var compilation = CSharpCompilation.Create(
nameof(AssertionMethodGeneratorRunner),
diff --git a/tests/TUnit.Assertions.SourceGenerator.Tests/CompileChecker.cs b/tests/TUnit.Assertions.SourceGenerator.Tests/CompileChecker.cs
index b85efd47838..754cec55ea4 100644
--- a/tests/TUnit.Assertions.SourceGenerator.Tests/CompileChecker.cs
+++ b/tests/TUnit.Assertions.SourceGenerator.Tests/CompileChecker.cs
@@ -1,5 +1,6 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
+using TUnit.Tests.Shared;
namespace TUnit.Assertions.SourceGenerator.Tests;
@@ -60,7 +61,7 @@ public static async Task AssertNoErrors(IEnumerable generatedFiles)
#if NETFRAMEWORK
.Append(NetFrameworkPolyfills)
#endif
- .Select(source => CSharpSyntaxTree.ParseText(source))
+ .Select(source => GeneratorTestInput.Parse(source))
.ToArray();
var compilation = CSharpCompilation.Create(
diff --git a/tests/TUnit.Assertions.SourceGenerator.Tests/GeneratorTestInputTests.cs b/tests/TUnit.Assertions.SourceGenerator.Tests/GeneratorTestInputTests.cs
new file mode 100644
index 00000000000..a5aa7b0b022
--- /dev/null
+++ b/tests/TUnit.Assertions.SourceGenerator.Tests/GeneratorTestInputTests.cs
@@ -0,0 +1,63 @@
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+using TUnit.Tests.Shared;
+
+namespace TUnit.Assertions.SourceGenerator.Tests;
+
+///
+/// The harness must bind fixtures the way a real build does (#7190): newer syntax has to parse and bind on the
+/// Roslyn this project hosts, and input a real build would reject has to fail the test instead of being snapshotted.
+///
+public class GeneratorTestInputTests
+{
+ [Test]
+ public async Task Newer_Syntax_Is_Accepted()
+ {
+ var compilation = Compile(
+ """
+ using System;
+ using System.Collections.Generic;
+
+ [AttributeUsage(AttributeTargets.Class)]
+ public sealed class ValuesAttribute(int[] values) : Attribute
+ {
+ public int[] Values { get; } = values;
+ }
+
+ [Values([1, 2, 3])]
+ public class UsesCollectionExpressionInAttribute
+ {
+ public static int Sum(params IEnumerable values)
+ {
+ var total = 0;
+ foreach (var value in values)
+ {
+ total += value;
+ }
+
+ return total;
+ }
+ }
+ """);
+
+ await Assert.That(() => GeneratorTestInput.ThrowIfInputCannotBeParsed(compilation)).ThrowsNothing();
+ }
+
+ [Test]
+ public async Task Syntax_Errors_Are_Rejected()
+ {
+ var compilation = Compile("public class Broken {");
+
+ await Assert.That(() => GeneratorTestInput.ThrowIfInputCannotBeParsed(compilation))
+ .Throws();
+ }
+
+ private static CSharpCompilation Compile(string source)
+ {
+ return CSharpCompilation.Create(
+ nameof(GeneratorTestInputTests),
+ [GeneratorTestInput.Parse(source)],
+ ReferencesHelper.References,
+ new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));
+ }
+}
diff --git a/tests/TUnit.Assertions.SourceGenerator.Tests/GlobalNamespaceRegressionTests.cs b/tests/TUnit.Assertions.SourceGenerator.Tests/GlobalNamespaceRegressionTests.cs
index cd83ad13763..7a9f33d758e 100644
--- a/tests/TUnit.Assertions.SourceGenerator.Tests/GlobalNamespaceRegressionTests.cs
+++ b/tests/TUnit.Assertions.SourceGenerator.Tests/GlobalNamespaceRegressionTests.cs
@@ -2,6 +2,7 @@
using Microsoft.CodeAnalysis.CSharp;
using TUnit.Assertions.SourceGenerator.Generators;
using TUnit.Assertions.SourceGenerator.Tests.Options;
+using TUnit.Tests.Shared;
namespace TUnit.Assertions.SourceGenerator.Tests;
@@ -59,7 +60,7 @@ public Task CollectionShapeWrapper_InGlobalNamespace_EmitsValidTypeName() => Run
// The emitted forwarders call internal TUnit.Assertions constructors, so the output only compiles
// inside TUnit.Assertions itself. A syntax check is what catches `global::.X`.
- var syntaxErrors = CSharpSyntaxTree.ParseText(generated)
+ var syntaxErrors = GeneratorTestInput.Parse(generated)
.GetDiagnostics()
.Where(d => d.Severity == DiagnosticSeverity.Error)
.Select(d => $"{d.Id}: {d.GetMessage()}")
@@ -90,7 +91,7 @@ public static async Task AssertCompiles(string testDataFile, string[] generatedF
var trees = generatedFiles
.Prepend(source)
- .Select(text => CSharpSyntaxTree.ParseText(text))
+ .Select(text => GeneratorTestInput.Parse(text))
.ToArray();
// TestData files are also compiled into this test assembly, so referencing it would declare
diff --git a/tests/TUnit.Assertions.SourceGenerator.Tests/TUnit.Assertions.SourceGenerator.Tests.csproj b/tests/TUnit.Assertions.SourceGenerator.Tests/TUnit.Assertions.SourceGenerator.Tests.csproj
index 134fdc2ab4d..cc73ec2b972 100644
--- a/tests/TUnit.Assertions.SourceGenerator.Tests/TUnit.Assertions.SourceGenerator.Tests.csproj
+++ b/tests/TUnit.Assertions.SourceGenerator.Tests/TUnit.Assertions.SourceGenerator.Tests.csproj
@@ -15,6 +15,7 @@
+
@@ -23,7 +24,12 @@
-
+
+
+
+
diff --git a/tests/TUnit.Assertions.SourceGenerator.Tests/TestsBase.cs b/tests/TUnit.Assertions.SourceGenerator.Tests/TestsBase.cs
index 746fdcc6af2..02f8aa3a624 100644
--- a/tests/TUnit.Assertions.SourceGenerator.Tests/TestsBase.cs
+++ b/tests/TUnit.Assertions.SourceGenerator.Tests/TestsBase.cs
@@ -5,6 +5,7 @@
using TUnit.Assertions.SourceGenerator.Generators;
using TUnit.Assertions.SourceGenerator.Tests.Extensions;
using TUnit.Assertions.SourceGenerator.Tests.Options;
+using TUnit.Tests.Shared;
namespace TUnit.Assertions.SourceGenerator.Tests;
@@ -90,8 +91,13 @@ ..await Task.WhenAll(runTestOptions.AdditionalFiles.Select(x => File.ReadAllText
// Create an instance of the source generator.
var generator = new TGenerator();
+ // Parse as the repository builds (LangVersion=preview); generated trees must share these options.
+ var parseOptions = runTestOptions.PreprocessorSymbols is { Length: > 0 }
+ ? GeneratorTestInput.ParseOptions.WithPreprocessorSymbols(runTestOptions.PreprocessorSymbols)
+ : GeneratorTestInput.ParseOptions;
+
// Source generators should be tested using 'GeneratorDriver'.
- GeneratorDriver driver = CSharpGeneratorDriver.Create(generator);
+ GeneratorDriver driver = CSharpGeneratorDriver.Create([generator.AsSourceGenerator()], parseOptions: parseOptions);
if (runTestOptions.BuildProperties != null)
{
@@ -103,10 +109,6 @@ ..await Task.WhenAll(runTestOptions.AdditionalFiles.Select(x => File.ReadAllText
// To run generators, we can use an empty compilation.
- var parseOptions = runTestOptions.PreprocessorSymbols is { Length: > 0 }
- ? CSharpParseOptions.Default.WithPreprocessorSymbols(runTestOptions.PreprocessorSymbols)
- : CSharpParseOptions.Default;
-
var compilation = CSharpCompilation.Create(
GetType().Name,
[
@@ -115,7 +117,7 @@ ..await Task.WhenAll(runTestOptions.AdditionalFiles.Select(x => File.ReadAllText
options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)
)
.WithReferences(ReferencesHelper.References)
- .AddSyntaxTrees(additionalSources.Select(x => CSharpSyntaxTree.ParseText(x)));
+ .AddSyntaxTrees(additionalSources.Select(x => CSharpSyntaxTree.ParseText(x, parseOptions)));
foreach (var additionalPackage in runTestOptions.AdditionalPackages)
{
@@ -124,6 +126,8 @@ ..await Task.WhenAll(runTestOptions.AdditionalFiles.Select(x => File.ReadAllText
compilation = compilation.AddReferences(downloaded);
}
+ GeneratorTestInput.ThrowIfInputCannotBeParsed(compilation);
+
// Run generators. Don't forget to use the new compilation rather than the previous one.
driver.RunGeneratorsAndUpdateCompilation(compilation, out var newCompilation, out var diagnostics);
diff --git a/tests/TUnit.Assertions.SourceGenerator.Tests/UnboundGenericAssertionFromTests.cs b/tests/TUnit.Assertions.SourceGenerator.Tests/UnboundGenericAssertionFromTests.cs
index 1b62d19e354..f6fd152d891 100644
--- a/tests/TUnit.Assertions.SourceGenerator.Tests/UnboundGenericAssertionFromTests.cs
+++ b/tests/TUnit.Assertions.SourceGenerator.Tests/UnboundGenericAssertionFromTests.cs
@@ -429,7 +429,9 @@ private static Diagnostic[] RunGenerator(string source)
ReferencesHelper.References,
new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));
- GeneratorDriver driver = CSharpGeneratorDriver.Create(new AssertionMethodGenerator());
+ GeneratorDriver driver = CSharpGeneratorDriver.Create(
+ [new AssertionMethodGenerator().AsSourceGenerator()],
+ parseOptions: ParseOptions);
driver = driver.RunGenerators(compilation);
return driver.GetRunResult().Diagnostics.ToArray();
diff --git a/tests/TUnit.Assertions.SourceGenerator.Tests/XmlDocsRegressionTests.cs b/tests/TUnit.Assertions.SourceGenerator.Tests/XmlDocsRegressionTests.cs
index 7ec7f577c47..cea766f9977 100644
--- a/tests/TUnit.Assertions.SourceGenerator.Tests/XmlDocsRegressionTests.cs
+++ b/tests/TUnit.Assertions.SourceGenerator.Tests/XmlDocsRegressionTests.cs
@@ -1,6 +1,7 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using TUnit.Assertions.SourceGenerator.Generators;
+using TUnit.Tests.Shared;
namespace TUnit.Assertions.SourceGenerator.Tests;
@@ -42,7 +43,7 @@ private Task AssertNoCS1591(string testDataFile) => RunTest(
var trees = generatedFiles
.Select(source => CSharpSyntaxTree.ParseText(
source,
- CSharpParseOptions.Default.WithDocumentationMode(DocumentationMode.Diagnose)))
+ GeneratorTestInput.ParseOptions.WithDocumentationMode(DocumentationMode.Diagnose)))
.ToArray();
var compilation = CSharpCompilation.Create(
@@ -68,7 +69,7 @@ private Task AssertNoCompilationErrors(string testDataFile) => RunTest(
await Assert.That(generatedFiles).IsNotEmpty();
var trees = generatedFiles
- .Select(source => CSharpSyntaxTree.ParseText(source))
+ .Select(source => GeneratorTestInput.Parse(source))
.ToArray();
var compilation = CSharpCompilation.Create(
diff --git a/tests/TUnit.Core.SourceGenerator.Tests/AotConverterScanTests.cs b/tests/TUnit.Core.SourceGenerator.Tests/AotConverterScanTests.cs
index b76aff8cde7..313e84532db 100644
--- a/tests/TUnit.Core.SourceGenerator.Tests/AotConverterScanTests.cs
+++ b/tests/TUnit.Core.SourceGenerator.Tests/AotConverterScanTests.cs
@@ -103,7 +103,13 @@ public void Tuple((Token Token, int Length) value) { }
var parseOptions = new CSharpParseOptions(LanguageVersion.Preview);
var compilation = CSharpCompilation.Create(
"ObsoleteConverters",
- [CSharpSyntaxTree.ParseText(source, parseOptions)],
+ [
+ CSharpSyntaxTree.ParseText(source, parseOptions),
+#if NETFRAMEWORK
+ // The record struct needs IsExternalInit, which .NET Framework lacks.
+ CSharpSyntaxTree.ParseText(ReferencesHelper.NetFrameworkPolyfills, parseOptions),
+#endif
+ ],
ReferencesHelper.References,
new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));
diff --git a/tests/TUnit.Core.SourceGenerator.Tests/TUnit.Core.SourceGenerator.Tests.csproj b/tests/TUnit.Core.SourceGenerator.Tests/TUnit.Core.SourceGenerator.Tests.csproj
index db2c0d13866..89e9b7f7791 100644
--- a/tests/TUnit.Core.SourceGenerator.Tests/TUnit.Core.SourceGenerator.Tests.csproj
+++ b/tests/TUnit.Core.SourceGenerator.Tests/TUnit.Core.SourceGenerator.Tests.csproj
@@ -15,6 +15,7 @@
+
diff --git a/tests/TUnit.Core.SourceGenerator.Tests/TestsBase.cs b/tests/TUnit.Core.SourceGenerator.Tests/TestsBase.cs
index 440dcf2d8c8..c0de195abf8 100644
--- a/tests/TUnit.Core.SourceGenerator.Tests/TestsBase.cs
+++ b/tests/TUnit.Core.SourceGenerator.Tests/TestsBase.cs
@@ -6,6 +6,7 @@
using TUnit.Core.SourceGenerator.Generators;
using TUnit.Core.SourceGenerator.Tests.Extensions;
using TUnit.Core.SourceGenerator.Tests.Options;
+using TUnit.Tests.Shared;
namespace TUnit.Core.SourceGenerator.Tests;
@@ -35,10 +36,8 @@ public Task RunTest(string inputFile, RunTestOptions runTestOptions, Func where TGenerator : IIncrementalGenerator, new()
{
- // TUnit.TestProject builds with LangVersion=preview (Directory.Build.props). The referenced
- // Roslyn's default parse options reject newer syntax such as collection expressions, so the
- // generator would see broken trees and the snapshots would not match a real build.
- private static readonly CSharpParseOptions ParseOptions = new(LanguageVersion.Preview);
+ // TUnit.TestProject builds with LangVersion=preview (Directory.Build.props), so fixtures must be parsed the same way.
+ private static CSharpParseOptions ParseOptions => GeneratorTestInput.ParseOptions;
public Task RunTest(string inputFile, Func assertions)
{
@@ -142,7 +141,7 @@ ..await Task.WhenAll(runTestOptions.AdditionalFiles.Select(x => File.ReadAllText
compilation = compilation.AddReferences(downloaded);
}
- ThrowIfInputCannotBeParsed(compilation);
+ GeneratorTestInput.ThrowIfInputCannotBeParsed(compilation);
// Run generators. Don't forget to use the new compilation rather than the previous one.
driver.RunGeneratorsAndUpdateCompilation(compilation, out var newCompilation, out var diagnostics);
@@ -210,34 +209,6 @@ Have you added required references and additional files?
await verifyTask;
}
- // Fixtures are not required to compile here: some reference packages or types that the harness
- // does not load. Syntax errors and the binding errors that a too-old compiler reports for valid
- // newer syntax are different, because the generator then sees a tree a real build never produces
- // and the snapshot silently records the wrong output (#7162).
- private static readonly HashSet UnsupportedSyntaxDiagnosticIds =
- [
- "CS0182", // attribute argument must be constant: collection expressions in attributes on Roslyn < 4.8
- "CS0225", // params parameter must be an array: params collections on Roslyn < 4.12
- "CS8652", // feature is in preview
- ];
-
- private static void ThrowIfInputCannotBeParsed(Compilation compilation)
- {
- var errors = compilation.SyntaxTrees
- .SelectMany(tree => tree.GetDiagnostics())
- .Concat(compilation.GetDeclarationDiagnostics().Where(d => UnsupportedSyntaxDiagnosticIds.Contains(d.Id)))
- .Where(d => d.Severity == DiagnosticSeverity.Error)
- .Select(d => d.ToString())
- .Distinct()
- .ToArray();
-
- if (errors.Length > 0)
- {
- throw new InvalidOperationException(
- $"The test input does not parse as a real build would:{Environment.NewLine}{string.Join(Environment.NewLine, errors)}");
- }
- }
-
private static bool IsError(Diagnostic x)
{
if (x.Severity == DiagnosticSeverity.Error)
diff --git a/tests/TUnit.Mocks.SourceGenerator.Tests/MemberDiscoveryTests.cs b/tests/TUnit.Mocks.SourceGenerator.Tests/MemberDiscoveryTests.cs
index 9527f276992..92e5a7f1265 100644
--- a/tests/TUnit.Mocks.SourceGenerator.Tests/MemberDiscoveryTests.cs
+++ b/tests/TUnit.Mocks.SourceGenerator.Tests/MemberDiscoveryTests.cs
@@ -1,6 +1,7 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using TUnit.Mocks.SourceGenerator.Discovery;
+using TUnit.Tests.Shared;
namespace TUnit.Mocks.SourceGenerator.Tests;
@@ -57,7 +58,7 @@ public interface IChild { }
private static CSharpCompilation CreateCompilation(string source)
{
- var syntaxTree = CSharpSyntaxTree.ParseText(source);
+ var syntaxTree = GeneratorTestInput.Parse(source);
return CSharpCompilation.Create(
"TestAssembly",
[syntaxTree],
diff --git a/tests/TUnit.Mocks.SourceGenerator.Tests/SnapshotTestBase.cs b/tests/TUnit.Mocks.SourceGenerator.Tests/SnapshotTestBase.cs
index 2261096039d..a461c4c4538 100644
--- a/tests/TUnit.Mocks.SourceGenerator.Tests/SnapshotTestBase.cs
+++ b/tests/TUnit.Mocks.SourceGenerator.Tests/SnapshotTestBase.cs
@@ -54,7 +54,7 @@ protected static string[] RunGenerator(
IEnumerable? additionalReferences = null,
CSharpParseOptions? parseOptions = null)
{
- parseOptions ??= CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.Preview);
+ parseOptions ??= GeneratorTestInput.ParseOptions;
var syntaxTree = CSharpSyntaxTree.ParseText(source, parseOptions);
IEnumerable refs = additionalReferences is null
@@ -67,6 +67,8 @@ protected static string[] RunGenerator(
options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)
).WithReferences(refs);
+ GeneratorTestInput.ThrowIfInputCannotBeParsed(compilation);
+
var generator = new MockGenerator();
GeneratorDriver driver = CSharpGeneratorDriver.Create([generator.AsSourceGenerator()], parseOptions: parseOptions);
@@ -96,7 +98,7 @@ protected static (string[] Sources, IReadOnlyList Diagnostics) RunGe
IEnumerable? additionalReferences = null,
IIncrementalGenerator? generator = null)
{
- var parseOptions = CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.Preview);
+ var parseOptions = GeneratorTestInput.ParseOptions;
var syntaxTree = CSharpSyntaxTree.ParseText(source, parseOptions);
IEnumerable refs = additionalReferences is null
@@ -138,7 +140,7 @@ private static string GetGeneratedTreeSortKey(string filePath)
///
protected static MetadataReference CreateExternalAssemblyReference(string source, string assemblyName = "ExternalLib")
{
- var parseOptions = CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.Preview);
+ var parseOptions = GeneratorTestInput.ParseOptions;
var syntaxTree = CSharpSyntaxTree.ParseText(source, parseOptions);
var compilation = CSharpCompilation.Create(
@@ -265,7 +267,7 @@ private static (Compilation Compilation, IReadOnlyList Diagnostics)
IEnumerable? additionalReferences,
CSharpParseOptions? parseOptions)
{
- parseOptions ??= CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.Preview);
+ parseOptions ??= GeneratorTestInput.ParseOptions;
var syntaxTree = CSharpSyntaxTree.ParseText(source, parseOptions);
IEnumerable refs = additionalReferences is null
@@ -279,6 +281,8 @@ private static (Compilation Compilation, IReadOnlyList Diagnostics)
new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)
.WithNullableContextOptions(NullableContextOptions.Enable));
+ GeneratorTestInput.ThrowIfInputCannotBeParsed(inputCompilation);
+
var generator = new MockGenerator();
GeneratorDriver driver = CSharpGeneratorDriver.Create([generator.AsSourceGenerator()], parseOptions: parseOptions);
driver = driver.RunGeneratorsAndUpdateCompilation(inputCompilation, out var outputCompilation, out var generatorDiagnostics);
diff --git a/tests/TUnit.Mocks.SourceGenerator.Tests/TUnit.Mocks.SourceGenerator.Tests.csproj b/tests/TUnit.Mocks.SourceGenerator.Tests/TUnit.Mocks.SourceGenerator.Tests.csproj
index c2856892c32..2192922de77 100644
--- a/tests/TUnit.Mocks.SourceGenerator.Tests/TUnit.Mocks.SourceGenerator.Tests.csproj
+++ b/tests/TUnit.Mocks.SourceGenerator.Tests/TUnit.Mocks.SourceGenerator.Tests.csproj
@@ -4,6 +4,7 @@
+