Skip to content
Merged
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
@@ -1,6 +1,7 @@
using System.Collections.Immutable;
using System.Text;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using TUnit.Core.SourceGenerator.Extensions;
using TUnit.Core.SourceGenerator.Helpers;
Expand All @@ -26,9 +27,11 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
});

// Pipeline 1: Find properties with IDataSourceAttribute and group by containing class
// Partial properties can receive attributes from their other declaration.
var propertyDataSources = context.SyntaxProvider
.CreateSyntaxProvider(
predicate: static (node, _) => node is PropertyDeclarationSyntax,
predicate: static (node, _) => node is PropertyDeclarationSyntax property
&& (property.AttributeLists.Count > 0 || property.Modifiers.Any(SyntaxKind.PartialKeyword)),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Partial Path Lacks Coverage

The new predicate specifically keeps partial properties because their data-source attribute may be on the other declaration, but the added tests cover only a non-partial property with a directly attached attribute. Add a test that places the attribute on each partial-property declaration, ideally including an edit with a reused driver, so future predicate changes cannot silently omit generated injection source.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

transform: static (ctx, _) => ExtractPropertyDataSource(ctx))
.Where(static x => x is not null)
.Select(static (x, _) => x!);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using TUnit.Core.SourceGenerator.Generators;

namespace TUnit.Core.SourceGenerator.Tests;

public class PropertyInjectionIncrementalTests
{
private const string Source = """
using TUnit.Core;
public class Fixture { }
public class Tests
{
[ClassDataSource<Fixture>]
public Fixture Value { get; set; }
}
""";

[Test]
public async Task AddingAndRemovingReferenceRefreshesCompilationTypes()
{
var withoutCore = ReferencesHelper.References
.Where(reference => !string.Equals(Path.GetFileName(reference.FilePath), "TUnit.Core.dll", StringComparison.OrdinalIgnoreCase))
.ToArray();
var compilation = CreateCompilation().WithReferences(withoutCore);
GeneratorDriver driver = CSharpGeneratorDriver.Create(new PropertyInjectionSourceGenerator());

driver = driver.RunGenerators(compilation);
await Assert.That(driver.GetRunResult().GeneratedTrees).IsEmpty();

compilation = compilation.WithReferences(ReferencesHelper.References);
await Assert.That(compilation.GetDiagnostics().Where(d => d.Severity == DiagnosticSeverity.Error)).IsEmpty();
driver = driver.RunGenerators(compilation);
await Assert.That(GetSource(driver)).Contains("PropertyName = \"Value\"");

driver = driver.RunGenerators(compilation.WithReferences(withoutCore));
await Assert.That(driver.GetRunResult().GeneratedTrees).IsEmpty();
await Assert.That(driver.GetRunResult().Diagnostics.Where(d => d.Severity == DiagnosticSeverity.Error)).IsEmpty();
}

[Test]
public async Task PropertyEditUpdatesReusedDriver()
{
var compilation = CreateCompilation();
GeneratorDriver driver = CSharpGeneratorDriver.Create(new PropertyInjectionSourceGenerator());
driver = driver.RunGenerators(compilation);
await Assert.That(GetSource(driver)).Contains("PropertyName = \"Value\"");

var originalTree = compilation.SyntaxTrees.Single();
compilation = compilation.ReplaceSyntaxTree(originalTree,
CSharpSyntaxTree.ParseText(Source.Replace("Value", "Renamed")));
driver = driver.RunGenerators(compilation);

await Assert.That(GetSource(driver)).Contains("PropertyName = \"Renamed\"");
await Assert.That(GetSource(driver)).DoesNotContain("PropertyName = \"Value\"");
await Assert.That(driver.GetRunResult().Diagnostics.Where(d => d.Severity == DiagnosticSeverity.Error)).IsEmpty();
}

private static string GetSource(GeneratorDriver driver) =>
string.Join("\n", driver.GetRunResult().GeneratedTrees.Select(tree => tree.ToString()));

[Test]
public async Task AddingAndRemovingAttributeUpdatesReusedDriver()
{
var plainSource = Source.Replace("[ClassDataSource<Fixture>]", "");
var compilation = CreateCompilation().RemoveAllSyntaxTrees().AddSyntaxTrees(CSharpSyntaxTree.ParseText(plainSource));
GeneratorDriver driver = CSharpGeneratorDriver.Create(new PropertyInjectionSourceGenerator());
driver = driver.RunGenerators(compilation);
await Assert.That(driver.GetRunResult().GeneratedTrees).IsEmpty();

compilation = compilation.ReplaceSyntaxTree(compilation.SyntaxTrees.Single(), CSharpSyntaxTree.ParseText(Source));
driver = driver.RunGenerators(compilation);
await Assert.That(GetSource(driver)).Contains("PropertyName = \"Value\"");

compilation = compilation.ReplaceSyntaxTree(compilation.SyntaxTrees.Single(), CSharpSyntaxTree.ParseText(plainSource));
driver = driver.RunGenerators(compilation);
await Assert.That(driver.GetRunResult().GeneratedTrees).IsEmpty();
await Assert.That(driver.GetRunResult().Diagnostics.Where(d => d.Severity == DiagnosticSeverity.Error)).IsEmpty();
}

private static CSharpCompilation CreateCompilation() => CSharpCompilation.Create(
"PropertyInjectionEdits",
[CSharpSyntaxTree.ParseText(Source)],
ReferencesHelper.References,
new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));
}

Loading