diff --git a/TUnit.Core.SourceGenerator/CodeGenerators/AssemblyLoaderGenerator.cs b/TUnit.Core.SourceGenerator/CodeGenerators/AssemblyLoaderGenerator.cs index 1a85f53357..57e91c9efe 100644 --- a/TUnit.Core.SourceGenerator/CodeGenerators/AssemblyLoaderGenerator.cs +++ b/TUnit.Core.SourceGenerator/CodeGenerators/AssemblyLoaderGenerator.cs @@ -17,10 +17,27 @@ public class AssemblyLoaderGenerator : IIncrementalGenerator ]; public void Initialize(IncrementalGeneratorInitializationContext context) { + var enabledProvider = context.AnalyzerConfigOptionsProvider + .Select((options, _) => + { + options.GlobalOptions.TryGetValue("build_property.EnableTUnitSourceGeneration", out var value); + return !string.Equals(value, "false", StringComparison.OrdinalIgnoreCase); + }); + var provider = context.CompilationProvider - .WithComparer(new PreventCompilationTriggerOnEveryKeystrokeComparer()); + .WithComparer(new PreventCompilationTriggerOnEveryKeystrokeComparer()) + .Combine(enabledProvider); + + context.RegisterSourceOutput(provider, (sourceProductionContext, data) => + { + var (compilation, isEnabled) = data; + if (!isEnabled) + { + return; + } - context.RegisterSourceOutput(provider, (sourceProductionContext, source) => GenerateCode(sourceProductionContext, source)); + GenerateCode(sourceProductionContext, compilation); + }); } private void GenerateCode(SourceProductionContext context, Compilation compilation) diff --git a/TUnit.Core.SourceGenerator/CodeGenerators/DisableReflectionScannerGenerator.cs b/TUnit.Core.SourceGenerator/CodeGenerators/DisableReflectionScannerGenerator.cs index 7fd52ff284..9374f6526c 100644 --- a/TUnit.Core.SourceGenerator/CodeGenerators/DisableReflectionScannerGenerator.cs +++ b/TUnit.Core.SourceGenerator/CodeGenerators/DisableReflectionScannerGenerator.cs @@ -9,10 +9,27 @@ public class DisableReflectionScannerGenerator : IIncrementalGenerator { public void Initialize(IncrementalGeneratorInitializationContext context) { + var enabledProvider = context.AnalyzerConfigOptionsProvider + .Select((options, _) => + { + options.GlobalOptions.TryGetValue("build_property.EnableTUnitSourceGeneration", out var value); + return !string.Equals(value, "false", StringComparison.OrdinalIgnoreCase); + }); + var provider = context.CompilationProvider - .WithComparer(new PreventCompilationTriggerOnEveryKeystrokeComparer()); + .WithComparer(new PreventCompilationTriggerOnEveryKeystrokeComparer()) + .Combine(enabledProvider); + + context.RegisterSourceOutput(provider, (sourceProductionContext, data) => + { + var (_, isEnabled) = data; + if (!isEnabled) + { + return; + } - context.RegisterSourceOutput(provider, (sourceProductionContext, _) => GenerateCode(sourceProductionContext)); + GenerateCode(sourceProductionContext); + }); } private void GenerateCode(SourceProductionContext context) diff --git a/TUnit.Core.SourceGenerator/CodeGenerators/DynamicTestsGenerator.cs b/TUnit.Core.SourceGenerator/CodeGenerators/DynamicTestsGenerator.cs index 909f407905..e9ac52adf7 100644 --- a/TUnit.Core.SourceGenerator/CodeGenerators/DynamicTestsGenerator.cs +++ b/TUnit.Core.SourceGenerator/CodeGenerators/DynamicTestsGenerator.cs @@ -11,14 +11,31 @@ public class DynamicTestsGenerator : IIncrementalGenerator { public void Initialize(IncrementalGeneratorInitializationContext context) { + var enabledProvider = context.AnalyzerConfigOptionsProvider + .Select((options, _) => + { + options.GlobalOptions.TryGetValue("build_property.EnableTUnitSourceGeneration", out var value); + return !string.Equals(value, "false", StringComparison.OrdinalIgnoreCase); + }); + var standardTests = context.SyntaxProvider .ForAttributeWithMetadataName( "TUnit.Core.DynamicTestBuilderAttribute", predicate: static (_, _) => true, transform: static (ctx, _) => GetSemanticTargetForTestMethodGeneration(ctx)) - .Where(static m => m is not null); + .Where(static m => m is not null) + .Combine(enabledProvider); + + context.RegisterSourceOutput(standardTests, (sourceContext, data) => + { + var (testData, isEnabled) = data; + if (!isEnabled) + { + return; + } - context.RegisterSourceOutput(standardTests, (sourceContext, data) => GenerateTests(sourceContext, data!)); + GenerateTests(sourceContext, testData!); + }); } static DynamicTestSourceDataModel? GetSemanticTargetForTestMethodGeneration(GeneratorAttributeSyntaxContext context) diff --git a/TUnit.Core.SourceGenerator/CodeGenerators/LanguageVersionCheckGenerator.cs b/TUnit.Core.SourceGenerator/CodeGenerators/LanguageVersionCheckGenerator.cs index 412fea7a60..93e0aea6b9 100644 --- a/TUnit.Core.SourceGenerator/CodeGenerators/LanguageVersionCheckGenerator.cs +++ b/TUnit.Core.SourceGenerator/CodeGenerators/LanguageVersionCheckGenerator.cs @@ -8,6 +8,13 @@ public class LanguageVersionCheckGenerator : IIncrementalGenerator { public void Initialize(IncrementalGeneratorInitializationContext context) { + var enabledProvider = context.AnalyzerConfigOptionsProvider + .Select((options, _) => + { + options.GlobalOptions.TryGetValue("build_property.EnableTUnitSourceGeneration", out var value); + return !string.Equals(value, "false", StringComparison.OrdinalIgnoreCase); + }); + var settings = context.CompilationProvider .Select((c, _) => { @@ -16,10 +23,18 @@ public void Initialize(IncrementalGeneratorInitializationContext context) : null; return csharpVersion; - }); + }) + .Combine(enabledProvider); - context.RegisterSourceOutput(settings, static (sourceProductionContext, languageVersion) => + context.RegisterSourceOutput(settings, static (sourceProductionContext, data) => { + var (languageVersion, isEnabled) = data; + + if (!isEnabled) + { + return; + } + if (languageVersion is null) { return; diff --git a/TUnit.Core.SourceGenerator/CodeGenerators/StaticPropertyInitializationGenerator.cs b/TUnit.Core.SourceGenerator/CodeGenerators/StaticPropertyInitializationGenerator.cs index 5dafe1d93f..7f4cf2e9d6 100644 --- a/TUnit.Core.SourceGenerator/CodeGenerators/StaticPropertyInitializationGenerator.cs +++ b/TUnit.Core.SourceGenerator/CodeGenerators/StaticPropertyInitializationGenerator.cs @@ -15,15 +15,31 @@ public class StaticPropertyInitializationGenerator : IIncrementalGenerator { public void Initialize(IncrementalGeneratorInitializationContext context) { + var enabledProvider = context.AnalyzerConfigOptionsProvider + .Select((options, _) => + { + options.GlobalOptions.TryGetValue("build_property.EnableTUnitSourceGeneration", out var value); + return !string.Equals(value, "false", StringComparison.OrdinalIgnoreCase); + }); + var testClasses = context.SyntaxProvider .CreateSyntaxProvider( predicate: static (s, _) => s is ClassDeclarationSyntax, transform: static (ctx, _) => GetSemanticTargetForGeneration(ctx)) .Where(static t => t is not null) - .Collect(); + .Collect() + .Combine(enabledProvider); + + context.RegisterSourceOutput(testClasses, (sourceProductionContext, data) => + { + var (classes, isEnabled) = data; + if (!isEnabled) + { + return; + } - context.RegisterSourceOutput(testClasses, (sourceProductionContext, testClasses) => - GenerateStaticPropertyInitialization(sourceProductionContext, testClasses.Where(t => t != null).ToImmutableArray()!)); + GenerateStaticPropertyInitialization(sourceProductionContext, classes.Where(t => t != null).ToImmutableArray()!); + }); } private static INamedTypeSymbol? GetSemanticTargetForGeneration(GeneratorSyntaxContext context) diff --git a/TUnit.Core/TUnit.Core.props b/TUnit.Core/TUnit.Core.props index 39e51639ab..b9533b105d 100644 --- a/TUnit.Core/TUnit.Core.props +++ b/TUnit.Core/TUnit.Core.props @@ -38,8 +38,14 @@ true + + + true + + + diff --git a/docs/docs/execution/engine-modes.md b/docs/docs/execution/engine-modes.md index 009d9f59a6..4a6eefbc0b 100644 --- a/docs/docs/execution/engine-modes.md +++ b/docs/docs/execution/engine-modes.md @@ -91,6 +91,59 @@ Alternatively, you can configure this in a `.runsettings` file: ``` +### Optimizing Build Performance in Reflection Mode + +When using reflection mode exclusively (via `[assembly: ReflectionMode]`), you can improve build performance by disabling source generation entirely. Since the generated code won't be used at runtime in reflection mode, skipping source generation reduces compile times. + +Add this MSBuild property to your test project file (`.csproj`): + +```xml + + false + +``` + +**Example: bUnit Test Project with Optimized Build** +```xml + + + net9.0 + + false + + + + + + + +``` + +Then in your code: +```csharp +// Enable reflection mode for Razor component testing +[assembly: ReflectionMode] + +namespace MyApp.Tests; + +public class CounterComponentTests : TestContext +{ + [Test] + public void CounterStartsAtZero() + { + var cut = RenderComponent(); + cut.Find("p").TextContent.ShouldBe("Current count: 0"); + } +} +``` + +**Benefits:** +- **Faster Builds**: Eliminates source generator execution at compile time +- **Reduced Compiler Overhead**: Less work for the compiler to do +- **Clear Intent**: Explicitly indicates the project uses reflection mode + +**Note:** This optimization is only beneficial when you're exclusively using reflection mode. If you're using source generation mode (the default), keep `EnableTUnitSourceGeneration` set to `true` (or omit it entirely, as `true` is the default). + ## Native AOT Support When publishing with Native AOT, TUnit's source generation mode provides additional benefits: