Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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 @@ -2544,7 +2544,10 @@ private static void GenerateDataSourceAttribute(CodeWriter writer, CompilationCo

var attrName = attrClass.GloballyQualifiedNonGeneric();

if (attrName == "global::TUnit.Core.MethodDataSourceAttribute")
// InstanceMethodDataSourceAttribute derives from MethodDataSourceAttribute and needs the
// same generated factory; without one, Native AOT falls back to a reflection lookup that
// finds no trimmed field or property metadata (#7225).
if (attrName is "global::TUnit.Core.MethodDataSourceAttribute" or "global::TUnit.Core.InstanceMethodDataSourceAttribute")
{
GenerateMethodDataSourceAttribute(writer, attr, typeSymbol, compilationContext.Compilation);
Comment thread
thomhurst marked this conversation as resolved.
Resolved
Comment thread
thomhurst marked this conversation as resolved.
Resolved
Comment thread
thomhurst marked this conversation as resolved.
Resolved
Comment thread
thomhurst marked this conversation as resolved.
Resolved
}
Expand Down Expand Up @@ -2916,7 +2919,10 @@ private static void GenerateMethodDataSourceAttribute(CodeWriter writer, Attribu
return;
}

var attrTypeName = usesTestClassInstance
// Keep a declared [InstanceMethodDataSource] as one even when its member is static, so the
// engine still sees IAccessesInstanceData as it does in reflection mode.
var isDeclaredInstanceAttribute = attr.AttributeClass?.GloballyQualifiedNonGeneric() == "global::TUnit.Core.InstanceMethodDataSourceAttribute";
var attrTypeName = usesTestClassInstance || isDeclaredInstanceAttribute
? "global::TUnit.Core.InstanceMethodDataSourceAttribute"
: "global::TUnit.Core.MethodDataSourceAttribute";

Expand Down
24 changes: 24 additions & 0 deletions tests/TUnit.Core.SourceGenerator.Tests/Bugs/7225/Tests7225.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
namespace TUnit.Core.SourceGenerator.Tests.Bugs._7225;

/// <summary>
/// [InstanceMethodDataSource] gets the same generated Factory as [MethodDataSource], so Native AOT
/// does not fall back to a reflection member lookup (#7225).
/// </summary>
internal class Tests7225 : TestsBase
{
[Test]
public Task Test() => RunTest(Path.Combine(Git.TestsDirectory.FullName,
"TUnit.TestProject",
"Bugs",
"_7225",
"InstanceMethodDataSourceFieldTests.cs"),
async generatedFiles =>
{
var source = string.Join(Environment.NewLine, generatedFiles);
const string testClass = "global::TUnit.TestProject.Bugs._7225.InstanceMethodDataSourceFieldTests";
await Assert.That(source).Contains($"FromInstanceRuntimeValue(dataGeneratorMetadata, static instance => (({testClass})instance).Rows)");
await Assert.That(source).Contains($"FromInstanceEnumerable(dataGeneratorMetadata, static instance => (({testClass})instance).PropertyRows)");
await Assert.That(source).Contains($"FromEnumerable(static () => {testClass}.StaticRows())");
await Assert.That(source).DoesNotContain("new global::TUnit.Core.InstanceMethodDataSourceAttribute(\"Rows\"),");
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,10 @@ internal static class TUnit_TestProject_Bugs__1821_Tests__TestSource
hasDataSource: true,
testDataSources: new global::TUnit.Core.IDataSourceAttribute[]
{
new global::TUnit.Core.InstanceMethodDataSourceAttribute("DataSource"),
new global::TUnit.Core.InstanceMethodDataSourceAttribute("DataSource")
{
Factory = static dataGeneratorMetadata => global::TUnit.Core.Helpers.DataSourceFactories.FromInstanceEnumerable(dataGeneratorMetadata, static instance => ((global::TUnit.TestProject.Bugs._1821.Tests)instance).DataSource()),
},
},
classDataSources: new global::TUnit.Core.IDataSourceAttribute[]
{
Expand Down
188 changes: 188 additions & 0 deletions tests/TUnit.Core.SourceGenerator.Tests/Tests7225.Test.verified.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
// <auto-generated/>
#pragma warning disable

#nullable enable
namespace TUnit.Generated;
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverageAttribute]
[global::System.CodeDom.Compiler.GeneratedCode("TUnit", "VERSION_SCRUBBED")]
internal static class TUnit_TestProject_Bugs__7225_InstanceMethodDataSourceFieldTests__TestSource
{
private static readonly global::TUnit.Core.ClassMetadata __classMetadata = global::TUnit.Core.ClassMetadata.GetOrAdd("TestsBase`1:global::TUnit.TestProject.Bugs._7225.InstanceMethodDataSourceFieldTests", new global::TUnit.Core.ClassMetadata
{
Type = typeof(global::TUnit.TestProject.Bugs._7225.InstanceMethodDataSourceFieldTests),
TypeInfo = new global::TUnit.Core.ConcreteType(typeof(global::TUnit.TestProject.Bugs._7225.InstanceMethodDataSourceFieldTests)),
Name = "InstanceMethodDataSourceFieldTests",
Namespace = "TUnit.TestProject.Bugs._7225",
Assembly = global::TUnit.Core.AssemblyMetadata.GetOrAdd("TestsBase`1", "TestsBase`1"),
Parameters = global::System.Array.Empty<global::TUnit.Core.ParameterMetadata>(),
Properties = new global::TUnit.Core.PropertyMetadata[]
{
new global::TUnit.Core.PropertyMetadata
{
ReflectionInfo = typeof(global::TUnit.TestProject.Bugs._7225.InstanceMethodDataSourceFieldTests).GetProperty("PropertyRows", typeof(global::System.Collections.Generic.IEnumerable<int>)),
Type = typeof(global::System.Collections.Generic.IEnumerable<int>),
Name = "PropertyRows",
IsStatic = false,
IsNullable = false,
Getter = o => ((global::TUnit.TestProject.Bugs._7225.InstanceMethodDataSourceFieldTests)o).PropertyRows,
ClassMetadata = null!,
ContainingTypeMetadata = null!
}
},
Parent = null
});
private static readonly global::System.Type __classType = typeof(global::TUnit.TestProject.Bugs._7225.InstanceMethodDataSourceFieldTests);
private static global::TUnit.TestProject.Bugs._7225.InstanceMethodDataSourceFieldTests __CreateInstance(global::System.Type[] typeArgs, object?[] args)
{
return new global::TUnit.TestProject.Bugs._7225.InstanceMethodDataSourceFieldTests();
}
private static global::System.Threading.Tasks.ValueTask __Invoke(global::TUnit.TestProject.Bugs._7225.InstanceMethodDataSourceFieldTests instance, int methodIndex, object?[] args, global::System.Threading.CancellationToken cancellationToken)
{
try
{
switch (methodIndex)
{
case 0:
{
switch (args.Length)
{
case 1:
{
return new global::System.Threading.Tasks.ValueTask(instance.Instance_Field(global::TUnit.Core.Helpers.CastHelper.Cast<int>(args[0])));
}
default:
throw new global::System.ArgumentException($"Expected exactly 1 argument, but got {args.Length}");
}
}
case 1:
{
switch (args.Length)
{
case 1:
{
return new global::System.Threading.Tasks.ValueTask(instance.Instance_Property(global::TUnit.Core.Helpers.CastHelper.Cast<int>(args[0])));
}
default:
throw new global::System.ArgumentException($"Expected exactly 1 argument, but got {args.Length}");
}
}
case 2:
{
switch (args.Length)
{
case 1:
{
return new global::System.Threading.Tasks.ValueTask(instance.Static_Method(global::TUnit.Core.Helpers.CastHelper.Cast<int>(args[0])));
}
default:
throw new global::System.ArgumentException($"Expected exactly 1 argument, but got {args.Length}");
}
}
default:
throw new global::System.ArgumentOutOfRangeException(nameof(methodIndex));
}
}
catch (global::System.Exception ex)
{
return new global::System.Threading.Tasks.ValueTask(global::System.Threading.Tasks.Task.FromException(ex));
}
}
private static global::System.Attribute[] __Attributes(int groupIndex)
{
switch (groupIndex)
{
case 0:
{
return
[
new global::TUnit.Core.TestAttribute(),
new global::TUnit.TestProject.Attributes.EngineTest(global::TUnit.TestProject.Attributes.ExpectedResult.Pass)
];
}
default:
throw new global::System.ArgumentOutOfRangeException(nameof(groupIndex));
}
}
private static readonly global::System.Func<global::System.Type[], object?[], global::TUnit.TestProject.Bugs._7225.InstanceMethodDataSourceFieldTests> __createInstance = __CreateInstance;
private static readonly global::System.Func<global::TUnit.TestProject.Bugs._7225.InstanceMethodDataSourceFieldTests, int, object?[], global::System.Threading.CancellationToken, global::System.Threading.Tasks.ValueTask> __invoke = __Invoke;
private static readonly global::System.Func<int, global::System.Attribute[]> __attributes = __Attributes;
public static readonly global::TUnit.Core.TestEntry<global::TUnit.TestProject.Bugs._7225.InstanceMethodDataSourceFieldTests>[] Entries = new global::TUnit.Core.TestEntry<global::TUnit.TestProject.Bugs._7225.InstanceMethodDataSourceFieldTests>[]
{
global::TUnit.Core.TestEntryFactory.CreateWithClassMetadata<global::TUnit.TestProject.Bugs._7225.InstanceMethodDataSourceFieldTests>(
methodName: "Instance_Field",
fullyQualifiedName: "TUnit.TestProject.Bugs._7225.InstanceMethodDataSourceFieldTests.Instance_Field",
filePath: "",
lineNumber: 18,
hasDataSource: true,
testDataSources: new global::TUnit.Core.IDataSourceAttribute[]
{
new global::TUnit.Core.InstanceMethodDataSourceAttribute("Rows")
{
Factory = static dataGeneratorMetadata => global::TUnit.Core.Helpers.DataSourceFactories.FromInstanceRuntimeValue(dataGeneratorMetadata, static instance => ((global::TUnit.TestProject.Bugs._7225.InstanceMethodDataSourceFieldTests)instance).Rows),
},
},
returnType: typeof(global::System.Threading.Tasks.Task),
parameters: global::TUnit.Core.ParameterMetadataFactory.ForMethod(typeof(global::TUnit.TestProject.Bugs._7225.InstanceMethodDataSourceFieldTests), "Instance_Field", false, new global::TUnit.Core.ParameterMetadata[]
{
global::TUnit.Core.ParameterMetadataFactory.Create(typeof(int), "value", new global::TUnit.Core.ConcreteType(typeof(int)), false)
}),
classMetadata: __classMetadata,
createInstance: __createInstance,
invokeBody: __invoke,
methodIndex: 0,
createAttributes: __attributes,
attributeGroupIndex: 0),
global::TUnit.Core.TestEntryFactory.CreateWithClassMetadata<global::TUnit.TestProject.Bugs._7225.InstanceMethodDataSourceFieldTests>(
methodName: "Instance_Property",
fullyQualifiedName: "TUnit.TestProject.Bugs._7225.InstanceMethodDataSourceFieldTests.Instance_Property",
filePath: "",
lineNumber: 25,
hasDataSource: true,
testDataSources: new global::TUnit.Core.IDataSourceAttribute[]
{
new global::TUnit.Core.InstanceMethodDataSourceAttribute("PropertyRows")
{
Factory = static dataGeneratorMetadata => global::TUnit.Core.Helpers.DataSourceFactories.FromInstanceEnumerable(dataGeneratorMetadata, static instance => ((global::TUnit.TestProject.Bugs._7225.InstanceMethodDataSourceFieldTests)instance).PropertyRows),
},
},
returnType: typeof(global::System.Threading.Tasks.Task),
parameters: global::TUnit.Core.ParameterMetadataFactory.ForMethod(typeof(global::TUnit.TestProject.Bugs._7225.InstanceMethodDataSourceFieldTests), "Instance_Property", false, new global::TUnit.Core.ParameterMetadata[]
{
global::TUnit.Core.ParameterMetadataFactory.Create(typeof(int), "value", new global::TUnit.Core.ConcreteType(typeof(int)), false)
}),
classMetadata: __classMetadata,
createInstance: __createInstance,
invokeBody: __invoke,
methodIndex: 1,
createAttributes: __attributes,
attributeGroupIndex: 0),
global::TUnit.Core.TestEntryFactory.CreateWithClassMetadata<global::TUnit.TestProject.Bugs._7225.InstanceMethodDataSourceFieldTests>(
methodName: "Static_Method",
fullyQualifiedName: "TUnit.TestProject.Bugs._7225.InstanceMethodDataSourceFieldTests.Static_Method",
filePath: "",
lineNumber: 32,
hasDataSource: true,
testDataSources: new global::TUnit.Core.IDataSourceAttribute[]
{
new global::TUnit.Core.InstanceMethodDataSourceAttribute("StaticRows")
{
Factory = static dataGeneratorMetadata => global::TUnit.Core.Helpers.DataSourceFactories.FromEnumerable(static () => global::TUnit.TestProject.Bugs._7225.InstanceMethodDataSourceFieldTests.StaticRows()),
},
},
returnType: typeof(global::System.Threading.Tasks.Task),
parameters: global::TUnit.Core.ParameterMetadataFactory.ForMethod(typeof(global::TUnit.TestProject.Bugs._7225.InstanceMethodDataSourceFieldTests), "Static_Method", false, new global::TUnit.Core.ParameterMetadata[]
{
global::TUnit.Core.ParameterMetadataFactory.Create(typeof(int), "value", new global::TUnit.Core.ConcreteType(typeof(int)), false)
}),
classMetadata: __classMetadata,
createInstance: __createInstance,
invokeBody: __invoke,
methodIndex: 2,
createAttributes: __attributes,
attributeGroupIndex: 0),
};
}
internal static partial class TUnit_TestRegistration
{
static readonly int _r_TUnit_TestProject_Bugs__7225_InstanceMethodDataSourceFieldTests__TestSource = global::TUnit.Core.SourceRegistrar.RegisterEntries<global::TUnit.TestProject.Bugs._7225.InstanceMethodDataSourceFieldTests>(static () => TUnit_TestProject_Bugs__7225_InstanceMethodDataSourceFieldTests__TestSource.Entries);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
using TUnit.TestProject.Attributes;

namespace TUnit.TestProject.Bugs._7225;

/// <summary>
/// [InstanceMethodDataSource] naming an instance field or property must get a generated factory, like
/// [MethodDataSource] does; otherwise Native AOT falls back to a reflection lookup that finds no member.
/// </summary>
[EngineTest(ExpectedResult.Pass)]
public class InstanceMethodDataSourceFieldTests
{
public readonly object Rows = new[] { 53, 54 };

public IEnumerable<int> PropertyRows => [55, 56];

public static IEnumerable<int> StaticRows() => [57, 58];

[Test]
[InstanceMethodDataSource(nameof(Rows))]
public async Task Instance_Field(int value)
{
await Assert.That(value).IsBetween(53, 54);
}

[Test]
[InstanceMethodDataSource(nameof(PropertyRows))]
public async Task Instance_Property(int value)
{
await Assert.That(value).IsBetween(55, 56);
}

[Test]
[InstanceMethodDataSource(nameof(StaticRows))]
public async Task Static_Method(int value)
{
await Assert.That(value).IsBetween(57, 58);
}
}
Loading