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
114 changes: 114 additions & 0 deletions TUnit.Core.SourceGenerator.Tests/MatrixTests.Test.verified.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1365,3 +1365,117 @@ internal static class TUnit_TestProject_MatrixTests_Exclusion__int_int_ModuleIni
global::TUnit.Core.SourceRegistrar.Register(typeof(global::TUnit.TestProject.MatrixTests), new TUnit_TestProject_MatrixTests_Exclusion__int_int_TestSource());
}
}


// ===== FILE SEPARATOR =====

// <auto-generated/>
#pragma warning disable

#nullable enable
namespace TUnit.Generated;
internal sealed class TUnit_TestProject_MatrixTests_MatrixMethod_WithEnumParameter_UsesOnlyMethodValues__bool_CountToTenEnum_TestSource : global::TUnit.Core.Interfaces.SourceGenerator.ITestSource
{
public async global::System.Collections.Generic.IAsyncEnumerable<global::TUnit.Core.TestMetadata> GetTestsAsync(string testSessionId, [global::System.Runtime.CompilerServices.EnumeratorCancellation] global::System.Threading.CancellationToken cancellationToken = default)
{
var metadata = new global::TUnit.Core.TestMetadata<global::TUnit.TestProject.MatrixTests>
{
TestName = "MatrixMethod_WithEnumParameter_UsesOnlyMethodValues",
TestClassType = typeof(global::TUnit.TestProject.MatrixTests),
TestMethodName = "MatrixMethod_WithEnumParameter_UsesOnlyMethodValues",
Dependencies = global::System.Array.Empty<global::TUnit.Core.TestDependency>(),
AttributeFactory = static () =>
[
new global::TUnit.Core.TestAttribute(),
new global::TUnit.TestProject.Attributes.EngineTest(global::TUnit.TestProject.Attributes.ExpectedResult.Pass)
],
DataSources = new global::TUnit.Core.IDataSourceAttribute[]
{
new global::TUnit.Core.MatrixDataSourceAttribute(),
},
ClassDataSources = global::System.Array.Empty<global::TUnit.Core.IDataSourceAttribute>(),
PropertyDataSources = global::System.Array.Empty<global::TUnit.Core.PropertyDataSource>(),
PropertyInjections = global::System.Array.Empty<global::TUnit.Core.PropertyInjectionData>(),
InheritanceDepth = 0,
FilePath = @"",
LineNumber = 197,
MethodMetadata = new global::TUnit.Core.MethodMetadata
{
Type = typeof(global::TUnit.TestProject.MatrixTests),
TypeInfo = new global::TUnit.Core.ConcreteType(typeof(global::TUnit.TestProject.MatrixTests)),
Name = "MatrixMethod_WithEnumParameter_UsesOnlyMethodValues",
GenericTypeCount = 0,
ReturnType = typeof(global::System.Threading.Tasks.Task),
ReturnTypeInfo = new global::TUnit.Core.ConcreteType(typeof(global::System.Threading.Tasks.Task)),
Parameters = new global::TUnit.Core.ParameterMetadata[]
{
new global::TUnit.Core.ParameterMetadata(typeof(bool))
{
Name = "flag",
TypeInfo = new global::TUnit.Core.ConcreteType(typeof(bool)),
IsNullable = false,
ReflectionInfo = typeof(global::TUnit.TestProject.MatrixTests).GetMethod("MatrixMethod_WithEnumParameter_UsesOnlyMethodValues", global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Instance, null, new global::System.Type[] { typeof(bool), typeof(global::TUnit.TestProject.MatrixTests.CountToTenEnum) }, null)!.GetParameters()[0]
},
new global::TUnit.Core.ParameterMetadata(typeof(global::TUnit.TestProject.MatrixTests.CountToTenEnum))
{
Name = "enum",
TypeInfo = new global::TUnit.Core.ConcreteType(typeof(global::TUnit.TestProject.MatrixTests.CountToTenEnum)),
IsNullable = false,
ReflectionInfo = typeof(global::TUnit.TestProject.MatrixTests).GetMethod("MatrixMethod_WithEnumParameter_UsesOnlyMethodValues", global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | global::System.Reflection.BindingFlags.Instance, null, new global::System.Type[] { typeof(bool), typeof(global::TUnit.TestProject.MatrixTests.CountToTenEnum) }, null)!.GetParameters()[1]
}
},
Class = global::TUnit.Core.ClassMetadata.GetOrAdd("TestsBase`1:global::TUnit.TestProject.MatrixTests", static () =>
{
var classMetadata = new global::TUnit.Core.ClassMetadata
{
Type = typeof(global::TUnit.TestProject.MatrixTests),
TypeInfo = new global::TUnit.Core.ConcreteType(typeof(global::TUnit.TestProject.MatrixTests)),
Name = "MatrixTests",
Namespace = "TUnit.TestProject",
Assembly = global::TUnit.Core.AssemblyMetadata.GetOrAdd("TestsBase`1", static () => new global::TUnit.Core.AssemblyMetadata { Name = "TestsBase`1" }),
Parameters = global::System.Array.Empty<global::TUnit.Core.ParameterMetadata>(),
Properties = global::System.Array.Empty<global::TUnit.Core.PropertyMetadata>(),
Parent = null
};
foreach (var prop in classMetadata.Properties)
{
prop.ClassMetadata = classMetadata;
prop.ContainingTypeMetadata = classMetadata;
}
return classMetadata;
})
},
InstanceFactory = (typeArgs, args) => new global::TUnit.TestProject.MatrixTests(),
InvokeTypedTest = static (instance, args, cancellationToken) =>
{
try
{
switch (args.Length)
{
case 2:
{
return new global::System.Threading.Tasks.ValueTask(instance.MatrixMethod_WithEnumParameter_UsesOnlyMethodValues(TUnit.Core.Helpers.CastHelper.Cast<bool>(args[0]), TUnit.Core.Helpers.CastHelper.Cast<global::TUnit.TestProject.MatrixTests.CountToTenEnum>(args[1])));
}
default:
throw new global::System.ArgumentException($"Expected exactly 2 arguments, but got {args.Length}");
}
}
catch (global::System.Exception ex)
{
return new global::System.Threading.Tasks.ValueTask(global::System.Threading.Tasks.Task.FromException(ex));
}
},
};
metadata.UseRuntimeDataGeneration(testSessionId);
yield return metadata;
yield break;
}
}
internal static class TUnit_TestProject_MatrixTests_MatrixMethod_WithEnumParameter_UsesOnlyMethodValues__bool_CountToTenEnum_ModuleInitializer
{
[global::System.Runtime.CompilerServices.ModuleInitializer]
public static void Initialize()
{
global::TUnit.Core.SourceRegistrar.Register(typeof(global::TUnit.TestProject.MatrixTests), new TUnit_TestProject_MatrixTests_MatrixMethod_WithEnumParameter_UsesOnlyMethodValues__bool_CountToTenEnum_TestSource());
}
}
6 changes: 4 additions & 2 deletions TUnit.Core.SourceGenerator/CodeGenerationHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,14 +49,16 @@ public static string GenerateParameterMetadataArray(IMethodSymbol method)
}

// Generate cached data source attributes for AOT compatibility
// Include both IDataSourceAttribute and IDataSourceMemberAttribute implementations
var dataSourceAttributes = param.GetAttributes()
.Where(attr => attr.AttributeClass != null &&
attr.AttributeClass.AllInterfaces.Any(i => i.Name == "IDataSourceAttribute"))
attr.AttributeClass.AllInterfaces.Any(i =>
i.Name == "IDataSourceAttribute" || i.Name == "IDataSourceMemberAttribute"))
.ToArray();

if (dataSourceAttributes.Length > 0)
{
writer.AppendLine($"CachedDataSourceAttributes = new global::TUnit.Core.IDataSourceAttribute[]");
writer.AppendLine($"CachedDataSourceAttributes = new global::System.Attribute[]");
writer.AppendLine("{");
writer.SetIndentLevel(3);
foreach (var attr in dataSourceAttributes)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,9 @@ public sealed class CombinedDataSourcesAttribute : AsyncUntypedDataSourceGenerat
if (parameterMetadata.CachedDataSourceAttributes != null)
{
// Source-generated mode: use cached attributes (no reflection!)
dataSourceAttributes = parameterMetadata.CachedDataSourceAttributes;
dataSourceAttributes = parameterMetadata.CachedDataSourceAttributes
.OfType<IDataSourceAttribute>()
.ToArray();
}
else
{
Expand Down
9 changes: 9 additions & 0 deletions TUnit.Core/Attributes/TestData/IDataSourceMemberAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace TUnit.Core;

/// <summary>
/// Marker interface for attributes that provide data values for individual parameters
/// within a data source context (e.g., matrix testing).
/// Attributes implementing this interface will be cached by the source generator
/// for AOT-compatible runtime access.
/// </summary>
public interface IDataSourceMemberAttribute;
2 changes: 1 addition & 1 deletion TUnit.Core/Attributes/TestData/MatrixSourceAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ namespace TUnit.Core;
/// </remarks>
/// <param name="objects">The values to be used for this parameter in the test matrix.</param>
[AttributeUsage(AttributeTargets.Parameter)]
public class MatrixAttribute(params object?[]? objects) : TUnitAttribute
public class MatrixAttribute(params object?[]? objects) : TUnitAttribute, IDataSourceMemberAttribute
{
protected MatrixAttribute() : this(null)
{
Expand Down
3 changes: 2 additions & 1 deletion TUnit.Core/Models/TestModels/ParameterMetadata.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,10 @@ public record ParameterMetadata([DynamicallyAccessedMembers(DynamicallyAccessedM
/// Cached data source attributes to avoid reflection call.
/// Set by source generator for AOT compatibility.
/// When null, falls back to using ReflectionInfo.GetCustomAttributes().
/// Includes attributes implementing IDataSourceAttribute or IDataSourceMemberAttribute.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public IDataSourceAttribute[]? CachedDataSourceAttributes { get; internal init; }
public Attribute[]? CachedDataSourceAttributes { get; internal init; }

/// <summary>
/// Position of this parameter in the method/constructor signature.
Expand Down
2 changes: 1 addition & 1 deletion TUnit.Engine.Tests/MatrixTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ public class MatrixTests(TestMode testMode) : InvokableTestBase(testMode)
[Test]
public async Task Test()
{
var expectedCount = IsNetFramework ? 133 : 271;
var expectedCount = IsNetFramework ? 137 : 275;

await RunTestsWithFilter(
"/*/*/MatrixTests/*",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -864,6 +864,7 @@ namespace
bool SkipIfEmpty { get; set; }
.<<.<object?[]?>>> GetDataRowsAsync(.DataGeneratorMetadata dataGeneratorMetadata);
}
public interface IDataSourceMemberAttribute { }
public interface IDynamicTestCreatorLocation
{
string? CreatorFilePath { get; set; }
Expand Down Expand Up @@ -909,7 +910,7 @@ namespace
public InvalidTestMetadataException(string message, .TestMetadata metadata, innerException) { }
}
[(.Parameter)]
public class MatrixAttribute : .TUnitAttribute
public class MatrixAttribute : .TUnitAttribute, .IDataSourceMemberAttribute
{
protected MatrixAttribute() { }
public MatrixAttribute(params object?[]? objects) { }
Expand Down Expand Up @@ -1053,7 +1054,7 @@ namespace
public class ParameterMetadata : <.ParameterMetadata>, .IMemberMetadata
{
public ParameterMetadata([.(..None | ..PublicParameterlessConstructor | ..PublicConstructors | ..NonPublicConstructors | ..PublicProperties)] Type) { }
public .IDataSourceAttribute[]? CachedDataSourceAttributes { get; }
public []? CachedDataSourceAttributes { get; }
public object? CachedDefaultValue { get; }
public bool? CachedIsOptional { get; }
public bool? CachedIsParams { get; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -864,6 +864,7 @@ namespace
bool SkipIfEmpty { get; set; }
.<<.<object?[]?>>> GetDataRowsAsync(.DataGeneratorMetadata dataGeneratorMetadata);
}
public interface IDataSourceMemberAttribute { }
public interface IDynamicTestCreatorLocation
{
string? CreatorFilePath { get; set; }
Expand Down Expand Up @@ -909,7 +910,7 @@ namespace
public InvalidTestMetadataException(string message, .TestMetadata metadata, innerException) { }
}
[(.Parameter)]
public class MatrixAttribute : .TUnitAttribute
public class MatrixAttribute : .TUnitAttribute, .IDataSourceMemberAttribute
{
protected MatrixAttribute() { }
public MatrixAttribute(params object?[]? objects) { }
Expand Down Expand Up @@ -1053,7 +1054,7 @@ namespace
public class ParameterMetadata : <.ParameterMetadata>, .IMemberMetadata
{
public ParameterMetadata([.(..None | ..PublicParameterlessConstructor | ..PublicConstructors | ..NonPublicConstructors | ..PublicProperties)] Type) { }
public .IDataSourceAttribute[]? CachedDataSourceAttributes { get; }
public []? CachedDataSourceAttributes { get; }
public object? CachedDefaultValue { get; }
public bool? CachedIsOptional { get; }
public bool? CachedIsParams { get; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -864,6 +864,7 @@ namespace
bool SkipIfEmpty { get; set; }
.<<.<object?[]?>>> GetDataRowsAsync(.DataGeneratorMetadata dataGeneratorMetadata);
}
public interface IDataSourceMemberAttribute { }
public interface IDynamicTestCreatorLocation
{
string? CreatorFilePath { get; set; }
Expand Down Expand Up @@ -909,7 +910,7 @@ namespace
public InvalidTestMetadataException(string message, .TestMetadata metadata, innerException) { }
}
[(.Parameter)]
public class MatrixAttribute : .TUnitAttribute
public class MatrixAttribute : .TUnitAttribute, .IDataSourceMemberAttribute
{
protected MatrixAttribute() { }
public MatrixAttribute(params object?[]? objects) { }
Expand Down Expand Up @@ -1053,7 +1054,7 @@ namespace
public class ParameterMetadata : <.ParameterMetadata>, .IMemberMetadata
{
public ParameterMetadata([.(..None | ..PublicParameterlessConstructor | ..PublicConstructors | ..NonPublicConstructors | ..PublicProperties)] Type) { }
public .IDataSourceAttribute[]? CachedDataSourceAttributes { get; }
public []? CachedDataSourceAttributes { get; }
public object? CachedDefaultValue { get; }
public bool? CachedIsOptional { get; }
public bool? CachedIsParams { get; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -841,6 +841,7 @@ namespace
bool SkipIfEmpty { get; set; }
.<<.<object?[]?>>> GetDataRowsAsync(.DataGeneratorMetadata dataGeneratorMetadata);
}
public interface IDataSourceMemberAttribute { }
public interface IDynamicTestCreatorLocation
{
string? CreatorFilePath { get; set; }
Expand Down Expand Up @@ -886,7 +887,7 @@ namespace
public InvalidTestMetadataException(string message, .TestMetadata metadata, innerException) { }
}
[(.Parameter)]
public class MatrixAttribute : .TUnitAttribute
public class MatrixAttribute : .TUnitAttribute, .IDataSourceMemberAttribute
{
protected MatrixAttribute() { }
public MatrixAttribute(params object?[]? objects) { }
Expand Down Expand Up @@ -1016,7 +1017,7 @@ namespace
public class ParameterMetadata : <.ParameterMetadata>, .IMemberMetadata
{
public ParameterMetadata( Type) { }
public .IDataSourceAttribute[]? CachedDataSourceAttributes { get; }
public []? CachedDataSourceAttributes { get; }
public object? CachedDefaultValue { get; }
public bool? CachedIsOptional { get; }
public bool? CachedIsParams { get; }
Expand Down
18 changes: 18 additions & 0 deletions TUnit.TestProject/MatrixTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -192,4 +192,22 @@ public static object ObjectMethod()
{
return 1;
}

// Test for GitHub Discussion #4145: MatrixMethod with enum parameter
[Test]
[MatrixDataSource]
public async Task MatrixMethod_WithEnumParameter_UsesOnlyMethodValues(
[Matrix(true, false)] bool flag,
[MatrixMethod<MatrixTests>(nameof(GetSpecificEnumValues))] CountToTenEnum @enum)
{
// This test verifies that MatrixMethod returns only the specific enum values
// and doesn't auto-generate all enum values (should only be One or Five)
await Assert.That(@enum == CountToTenEnum.One || @enum == CountToTenEnum.Five).IsTrue();
}

public static IEnumerable<CountToTenEnum> GetSpecificEnumValues()
{
yield return CountToTenEnum.One;
yield return CountToTenEnum.Five;
}
}
Loading