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 @@ +