diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml
index 112c2ecbeb..31b833ae8d 100644
--- a/.github/workflows/dotnet.yml
+++ b/.github/workflows/dotnet.yml
@@ -66,6 +66,12 @@ jobs:
- name: Build
run: dotnet build -c Release
+ - name: Publish AOT
+ run: dotnet publish TUnit.TestProject/TUnit.TestProject.csproj -c Release --use-current-runtime -p:Aot=true -o TESTPROJECT_AOT --framework net8.0
+
+ - name: Publish Single File
+ run: dotnet publish TUnit.TestProject/TUnit.TestProject.csproj -c Release --use-current-runtime -p:SingleFile=true -o TESTPROJECT_SINGLEFILE --framework net8.0
+
- name: Run Pipeline
uses: ./.github/actions/execute-pipeline
with:
diff --git a/Directory.Packages.props b/Directory.Packages.props
index c743e90186..2914315e37 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -80,6 +80,7 @@
+
diff --git a/TUnit.Analyzers/AnalyzerReleases.Unshipped.md b/TUnit.Analyzers/AnalyzerReleases.Unshipped.md
index 366f682b07..c52332fd49 100644
--- a/TUnit.Analyzers/AnalyzerReleases.Unshipped.md
+++ b/TUnit.Analyzers/AnalyzerReleases.Unshipped.md
@@ -1,5 +1,4 @@
### New Rules
-Rule ID | Category | Severity | Notes
---------|----------|----------|-------
-TUnit0052 | Usage | Warning | Multiple constructors found without [TestConstructor] attribute
\ No newline at end of file
+Rule ID | Category | Severity | Notes
+--------|----------|----------|-------
\ No newline at end of file
diff --git a/TUnit.Assertions.SourceGenerator/Generators/AssertionExtensionGenerator.cs b/TUnit.Assertions.SourceGenerator/Generators/AssertionExtensionGenerator.cs
index df27b7c2c7..0c42f0d347 100644
--- a/TUnit.Assertions.SourceGenerator/Generators/AssertionExtensionGenerator.cs
+++ b/TUnit.Assertions.SourceGenerator/Generators/AssertionExtensionGenerator.cs
@@ -92,13 +92,13 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
return null;
}
- // Check for RequiresDynamicCode attribute
- var requiresDynamicCodeAttr = classSymbol.GetAttributes()
- .FirstOrDefault(attr => attr.AttributeClass?.Name == "RequiresDynamicCodeAttribute");
- string? requiresDynamicCodeMessage = null;
- if (requiresDynamicCodeAttr != null && requiresDynamicCodeAttr.ConstructorArguments.Length > 0)
+ // Check for RequiresUnreferencedCode attribute
+ var RequiresUnreferencedCodeAttr = classSymbol.GetAttributes()
+ .FirstOrDefault(attr => attr.AttributeClass?.Name == "RequiresUnreferencedCodeAttribute");
+ string? RequiresUnreferencedCodeMessage = null;
+ if (RequiresUnreferencedCodeAttr != null && RequiresUnreferencedCodeAttr.ConstructorArguments.Length > 0)
{
- requiresDynamicCodeMessage = requiresDynamicCodeAttr.ConstructorArguments[0].Value?.ToString();
+ RequiresUnreferencedCodeMessage = RequiresUnreferencedCodeAttr.ConstructorArguments[0].Value?.ToString();
}
return new AssertionExtensionData(
@@ -108,7 +108,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
assertionBaseType,
constructors,
overloadPriority,
- requiresDynamicCodeMessage
+ RequiresUnreferencedCodeMessage
);
}
@@ -153,7 +153,7 @@ private static void GenerateExtensionMethods(SourceProductionContext context, As
sourceBuilder.AppendLine($"namespace TUnit.Assertions.Extensions;");
sourceBuilder.AppendLine();
-
+
// Extension class
var extensionClassName = $"{data.ClassSymbol.Name}Extensions";
sourceBuilder.AppendLine($"/// ");
@@ -309,11 +309,11 @@ private static void GenerateExtensionMethod(
sourceBuilder.AppendLine($" /// Extension method for {assertionType.Name}.");
sourceBuilder.AppendLine(" /// ");
- // Add RequiresDynamicCode attribute if present
- if (!string.IsNullOrEmpty(data.RequiresDynamicCodeMessage))
+ // Add RequiresUnreferencedCode attribute if present
+ if (!string.IsNullOrEmpty(data.RequiresUnreferencedCodeMessage))
{
- var escapedMessage = data.RequiresDynamicCodeMessage.Replace("\"", "\\\"");
- sourceBuilder.AppendLine($" [global::System.Diagnostics.CodeAnalysis.RequiresDynamicCode(\"{escapedMessage}\")]");
+ var escapedMessage = data.RequiresUnreferencedCodeMessage!.Replace("\"", "\\\"");
+ sourceBuilder.AppendLine($" [global::System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode(\"{escapedMessage}\")]");
}
// Add OverloadResolutionPriority attribute only if priority > 0
@@ -454,6 +454,6 @@ private record AssertionExtensionData(
INamedTypeSymbol AssertionBaseType,
ImmutableArray Constructors,
int OverloadResolutionPriority,
- string? RequiresDynamicCodeMessage
+ string? RequiresUnreferencedCodeMessage
);
}
diff --git a/TUnit.Assertions.Tests/DictionaryCollectionTests.cs b/TUnit.Assertions.Tests/DictionaryCollectionTests.cs
index 466ef9f457..46db66210d 100644
--- a/TUnit.Assertions.Tests/DictionaryCollectionTests.cs
+++ b/TUnit.Assertions.Tests/DictionaryCollectionTests.cs
@@ -184,7 +184,7 @@ public async Task Dictionary_IsEquivalentTo_Works()
// IsEquivalentTo works on collections regardless of order
// Cast both to IEnumerable to use collection equivalency
await Assert.That((IEnumerable>)dictionary1)
- .IsEquivalentTo((IEnumerable>)dictionary2);
+ .IsEquivalentTo(dictionary2);
}
[Test]
diff --git a/TUnit.Assertions.Tests/Old/DictionaryAssertionTests.cs b/TUnit.Assertions.Tests/Old/DictionaryAssertionTests.cs
index 499cf6447f..e602b77e8b 100644
--- a/TUnit.Assertions.Tests/Old/DictionaryAssertionTests.cs
+++ b/TUnit.Assertions.Tests/Old/DictionaryAssertionTests.cs
@@ -32,7 +32,7 @@ public async Task String_ReadOnlyDictionary_Contains_Key()
{
var dictionary = new ReadDictionary();
- await TUnitAssert.That((IReadOnlyDictionary)dictionary).ContainsKey("Blah");
+ await TUnitAssert.That(dictionary).ContainsKey("Blah");
}
[Test]
diff --git a/TUnit.Assertions.Tests/Old/EquivalentAssertionTests.cs b/TUnit.Assertions.Tests/Old/EquivalentAssertionTests.cs
index 8b2fc9b555..ea82d335f6 100644
--- a/TUnit.Assertions.Tests/Old/EquivalentAssertionTests.cs
+++ b/TUnit.Assertions.Tests/Old/EquivalentAssertionTests.cs
@@ -98,7 +98,7 @@ public async Task Different_Dictionaries_Are_Equivalent_With_Different_Ordered_K
// Dictionaries are equivalent regardless of key order by default
// Cast both to IEnumerable to use collection equivalency
await TUnitAssert.That((IEnumerable>)dict1)
- .IsEquivalentTo((IEnumerable>)dict2);
+ .IsEquivalentTo(dict2);
}
[Test]
diff --git a/TUnit.Assertions.Tests/TypeOfTests.cs b/TUnit.Assertions.Tests/TypeOfTests.cs
index 630c418606..2af746a842 100644
--- a/TUnit.Assertions.Tests/TypeOfTests.cs
+++ b/TUnit.Assertions.Tests/TypeOfTests.cs
@@ -242,7 +242,7 @@ public async Task CollectionAssertion_NullableByteArray_CanUseCollectionMethods(
// Should be able to use collection assertion methods
await Assert.That(nullableBytes).HasCount(5);
- await Assert.That(nullableBytes).Contains((byte)3);
+ await Assert.That(nullableBytes).Contains(3);
await Assert.That(nullableBytes).IsInOrder();
}
diff --git a/TUnit.Assertions/Assertions/Enums/EnumAssertions.cs b/TUnit.Assertions/Assertions/Enums/EnumAssertions.cs
index bcbc0d1eb2..9aec4b26ff 100644
--- a/TUnit.Assertions/Assertions/Enums/EnumAssertions.cs
+++ b/TUnit.Assertions/Assertions/Enums/EnumAssertions.cs
@@ -30,8 +30,8 @@ protected override Task CheckAsync(EvaluationMetadata me
}
// Use HasFlag method for enum flag checking
- var enumValue = (Enum)(object)value;
- var enumFlag = (Enum)(object)_expectedFlag;
+ var enumValue = (Enum)value;
+ var enumFlag = (Enum)_expectedFlag;
if (enumValue.HasFlag(enumFlag))
{
@@ -70,8 +70,8 @@ protected override Task CheckAsync(EvaluationMetadata me
return Task.FromResult(AssertionResult.Failed($"threw {exception.GetType().Name}: {exception.Message}"));
}
- var enumValue = (Enum)(object)value;
- var enumFlag = (Enum)(object)_unexpectedFlag;
+ var enumValue = (Enum)value;
+ var enumFlag = (Enum)_unexpectedFlag;
if (!enumValue.HasFlag(enumFlag))
{
@@ -106,7 +106,11 @@ protected override Task CheckAsync(EvaluationMetadata me
return Task.FromResult(AssertionResult.Failed($"threw {exception.GetType().Name}: {exception.Message}"));
}
+#if NET
+ if (Enum.IsDefined(value))
+#else
if (Enum.IsDefined(typeof(TEnum), value))
+#endif
{
return Task.FromResult(AssertionResult.Passed);
}
@@ -139,7 +143,11 @@ protected override Task CheckAsync(EvaluationMetadata me
return Task.FromResult(AssertionResult.Failed($"threw {exception.GetType().Name}: {exception.Message}"));
}
+#if NET
+ if (!Enum.IsDefined(value))
+#else
if (!Enum.IsDefined(typeof(TEnum), value))
+#endif
{
return Task.FromResult(AssertionResult.Passed);
}
diff --git a/TUnit.Assertions/Conditions/Helpers/StructuralEqualityComparer.cs b/TUnit.Assertions/Conditions/Helpers/StructuralEqualityComparer.cs
index 1c0a7448da..af1a1876e9 100644
--- a/TUnit.Assertions/Conditions/Helpers/StructuralEqualityComparer.cs
+++ b/TUnit.Assertions/Conditions/Helpers/StructuralEqualityComparer.cs
@@ -10,7 +10,7 @@ namespace TUnit.Assertions.Conditions.Helpers;
/// For complex objects, performs deep comparison of properties and fields.
///
/// The type of objects to compare
-[RequiresDynamicCode("Structural equality comparison uses reflection to access object members and is not compatible with AOT")]
+[RequiresUnreferencedCode("Structural equality comparison uses reflection to access object members and is not compatible with AOT")]
public sealed class StructuralEqualityComparer : IEqualityComparer
{
///
diff --git a/TUnit.Assertions/Conditions/IsEquivalentToAssertion.cs b/TUnit.Assertions/Conditions/IsEquivalentToAssertion.cs
index ec82f1e13f..6eaf69bf8b 100644
--- a/TUnit.Assertions/Conditions/IsEquivalentToAssertion.cs
+++ b/TUnit.Assertions/Conditions/IsEquivalentToAssertion.cs
@@ -14,7 +14,7 @@ namespace TUnit.Assertions.Conditions;
/// Inherits from CollectionComparerBasedAssertion to preserve collection type awareness in And/Or chains.
///
[AssertionExtension("IsEquivalentTo")]
-[RequiresDynamicCode("Collection equivalency uses structural comparison for complex objects, which requires reflection and is not compatible with AOT")]
+[RequiresUnreferencedCode("Collection equivalency uses structural comparison for complex objects, which requires reflection and is not compatible with AOT")]
public class IsEquivalentToAssertion : CollectionComparerBasedAssertion
where TCollection : IEnumerable
{
diff --git a/TUnit.Assertions/Conditions/NotEquivalentToAssertion.cs b/TUnit.Assertions/Conditions/NotEquivalentToAssertion.cs
index 66444cef0d..5fbb18d0d7 100644
--- a/TUnit.Assertions/Conditions/NotEquivalentToAssertion.cs
+++ b/TUnit.Assertions/Conditions/NotEquivalentToAssertion.cs
@@ -13,7 +13,7 @@ namespace TUnit.Assertions.Conditions;
/// Inherits from CollectionComparerBasedAssertion to preserve collection type awareness in And/Or chains.
///
[AssertionExtension("IsNotEquivalentTo")]
-[RequiresDynamicCode("Collection equivalency uses structural comparison for complex objects, which requires reflection and is not compatible with AOT")]
+[RequiresUnreferencedCode("Collection equivalency uses structural comparison for complex objects, which requires reflection and is not compatible with AOT")]
public class NotEquivalentToAssertion : CollectionComparerBasedAssertion
where TCollection : IEnumerable
{
diff --git a/TUnit.Assertions/Conditions/NotStructuralEquivalencyAssertion.cs b/TUnit.Assertions/Conditions/NotStructuralEquivalencyAssertion.cs
index 50b4ebf588..4e58fd0bb6 100644
--- a/TUnit.Assertions/Conditions/NotStructuralEquivalencyAssertion.cs
+++ b/TUnit.Assertions/Conditions/NotStructuralEquivalencyAssertion.cs
@@ -1,3 +1,4 @@
+using System.Diagnostics.CodeAnalysis;
using System.Text;
using TUnit.Assertions.Core;
@@ -6,6 +7,7 @@ namespace TUnit.Assertions.Conditions;
///
/// Asserts that two objects are NOT structurally equivalent.
///
+[RequiresUnreferencedCode("Uses reflection for structural equivalency comparison")]
public class NotStructuralEquivalencyAssertion : Assertion
{
private readonly object? _notExpected;
diff --git a/TUnit.Assertions/Conditions/StructuralEquivalencyAssertion.cs b/TUnit.Assertions/Conditions/StructuralEquivalencyAssertion.cs
index 8eee381ec2..8591bf4da9 100644
--- a/TUnit.Assertions/Conditions/StructuralEquivalencyAssertion.cs
+++ b/TUnit.Assertions/Conditions/StructuralEquivalencyAssertion.cs
@@ -10,6 +10,7 @@ namespace TUnit.Assertions.Conditions;
/// Asserts that two objects are structurally equivalent by comparing their properties and fields.
/// Supports partial equivalency and member exclusion.
///
+[RequiresUnreferencedCode("Uses reflection to compare object properties and fields.")]
public class StructuralEquivalencyAssertion : Assertion
{
private readonly object? _expected;
@@ -166,9 +167,7 @@ internal AssertionResult CompareObjects(object? actual, object? expected, string
}
// Compare properties and fields
-#pragma warning disable IL2072 // GetType() does not preserve DynamicallyAccessedMembers - acceptable for runtime structural comparison
var expectedMembers = GetMembersToCompare(expectedType);
-#pragma warning restore IL2072
foreach (var member in expectedMembers)
{
@@ -199,9 +198,7 @@ internal AssertionResult CompareObjects(object? actual, object? expected, string
// In partial equivalency mode, skip members that don't exist on actual
if (_usePartialEquivalency)
{
-#pragma warning disable IL2072 // GetType() does not preserve DynamicallyAccessedMembers - acceptable for runtime structural comparison
var actualMember = GetMemberInfo(actualType, member.Name);
-#pragma warning restore IL2072
if (actualMember == null)
{
continue;
@@ -211,9 +208,7 @@ internal AssertionResult CompareObjects(object? actual, object? expected, string
}
else
{
-#pragma warning disable IL2072 // GetType() does not preserve DynamicallyAccessedMembers - acceptable for runtime structural comparison
var actualMember = GetMemberInfo(actualType, member.Name);
-#pragma warning restore IL2072
if (actualMember == null)
{
return AssertionResult.Failed($"Property {memberPath} did not match{Environment.NewLine}Expected: {FormatValue(expectedValue)}{Environment.NewLine}Received: null");
@@ -231,9 +226,7 @@ internal AssertionResult CompareObjects(object? actual, object? expected, string
// In non-partial mode, check for extra properties on actual
if (!_usePartialEquivalency)
{
-#pragma warning disable IL2072 // GetType() does not preserve DynamicallyAccessedMembers - acceptable for runtime structural comparison
var actualMembers = GetMembersToCompare(actualType);
-#pragma warning restore IL2072
var expectedMemberNames = new HashSet(expectedMembers.Select(m => m.Name));
foreach (var member in actualMembers)
diff --git a/TUnit.Assertions/Extensions/AssertionExtensions.cs b/TUnit.Assertions/Extensions/AssertionExtensions.cs
index 55d966c3d5..aa2eedb6b1 100644
--- a/TUnit.Assertions/Extensions/AssertionExtensions.cs
+++ b/TUnit.Assertions/Extensions/AssertionExtensions.cs
@@ -548,6 +548,7 @@ public static StringLengthAssertion HasLength(
/// Performs deep comparison of properties and fields.
/// Supports .WithPartialEquivalency() and .IgnoringMember() for advanced scenarios.
///
+ [RequiresUnreferencedCode("Uses reflection to compare members")]
public static StructuralEquivalencyAssertion IsEquivalentTo(
this IAssertionSource source,
object? expected,
@@ -562,6 +563,7 @@ public static StructuralEquivalencyAssertion IsEquivalentTo(
/// Performs deep comparison of properties and fields.
/// Supports .WithPartialEquivalency() and .IgnoringMember() for advanced scenarios.
///
+ [RequiresUnreferencedCode("Uses reflection to compare members")]
public static NotStructuralEquivalencyAssertion IsNotEquivalentTo(
this IAssertionSource source,
object? expected,
diff --git a/TUnit.Core.SourceGenerator/CodeGenerators/StaticPropertyInitializationGenerator.cs b/TUnit.Core.SourceGenerator/CodeGenerators/StaticPropertyInitializationGenerator.cs
index ffcdfb1a15..5dafe1d93f 100644
--- a/TUnit.Core.SourceGenerator/CodeGenerators/StaticPropertyInitializationGenerator.cs
+++ b/TUnit.Core.SourceGenerator/CodeGenerators/StaticPropertyInitializationGenerator.cs
@@ -294,7 +294,7 @@ private static void GenerateAsyncDataSourceGeneratorWithPropertyWithAssignment(C
writer.Indent();
writer.AppendLine("Type = global::TUnit.Core.Enums.DataGeneratorType.Property,");
writer.AppendLine("TestBuilderContext = new global::TUnit.Core.TestBuilderContextAccessor(globalContext),");
- writer.AppendLine("MembersToGenerate = new global::TUnit.Core.MemberMetadata[] { propertyMetadata },");
+ writer.AppendLine("MembersToGenerate = new global::TUnit.Core.IMemberMetadata[] { propertyMetadata },");
writer.AppendLine("TestInformation = null,");
writer.AppendLine("TestSessionId = global::TUnit.Core.TestSessionContext.Current?.Id ?? \"static-property-init\",");
writer.AppendLine("TestClassInstance = null,");
diff --git a/TUnit.Core.SourceGenerator/CodeGenerators/Writers/AttributeWriter.cs b/TUnit.Core.SourceGenerator/CodeGenerators/Writers/AttributeWriter.cs
index 44dcf0c647..fe12ca38aa 100644
--- a/TUnit.Core.SourceGenerator/CodeGenerators/Writers/AttributeWriter.cs
+++ b/TUnit.Core.SourceGenerator/CodeGenerators/Writers/AttributeWriter.cs
@@ -20,6 +20,12 @@ public static void WriteAttributes(ICodeWriter sourceCodeWriter, Compilation com
// Include attributes without syntax reference (from other assemblies) as long as they have an AttributeClass
if (attributeData.ApplicationSyntaxReference is not null || attributeData.AttributeClass is not null)
{
+ // Skip compiler-internal and assembly-level attributes
+ if (ShouldSkipCompilerInternalAttribute(attributeData))
+ {
+ continue;
+ }
+
// Skip framework-specific attributes when targeting older frameworks
// We determine this by checking if we can compile the attribute
if (ShouldSkipFrameworkSpecificAttribute(compilation, attributeData))
@@ -28,8 +34,8 @@ public static void WriteAttributes(ICodeWriter sourceCodeWriter, Compilation com
}
// Skip attributes with compiler-generated type arguments
- if (attributeData.ConstructorArguments.Any(arg =>
- arg is { Kind: TypedConstantKind.Type, Value: ITypeSymbol typeSymbol } &&
+ if (attributeData.ConstructorArguments.Any(arg =>
+ arg is { Kind: TypedConstantKind.Type, Value: ITypeSymbol typeSymbol } &&
typeSymbol.IsCompilerGeneratedType()))
{
continue;
@@ -274,4 +280,30 @@ private static bool IsNullableAttribute(string fullyQualifiedName)
fullyQualifiedName.Contains("NullablePublicOnlyAttribute");
}
+ private static bool ShouldSkipCompilerInternalAttribute(AttributeData attributeData)
+ {
+ if (attributeData.AttributeClass == null)
+ {
+ return false;
+ }
+
+ var fullyQualifiedName = attributeData.AttributeClass.ToDisplayString();
+
+ // Skip compiler-internal attributes that should never be re-emitted
+ // System.Runtime.CompilerServices contains compiler-generated and structural metadata attributes
+ if (fullyQualifiedName.StartsWith("System.Runtime.CompilerServices."))
+ {
+ return true;
+ }
+
+ // Skip debugger attributes (compiler-generated for debugging support)
+ if (fullyQualifiedName.StartsWith("System.Diagnostics.Debugger"))
+ {
+ return true;
+ }
+
+ // Skip ParamArrayAttribute (compiler-generated for params keyword)
+ return fullyQualifiedName == "System.ParamArrayAttribute";
+ }
+
}
diff --git a/TUnit.Core.SourceGenerator/Generators/AotConverterGenerator.cs b/TUnit.Core.SourceGenerator/Generators/AotConverterGenerator.cs
index 958a97fa7e..e7f8fe57f8 100644
--- a/TUnit.Core.SourceGenerator/Generators/AotConverterGenerator.cs
+++ b/TUnit.Core.SourceGenerator/Generators/AotConverterGenerator.cs
@@ -20,51 +20,252 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
.Select((compilation, _) =>
{
var conversionInfos = new List();
-
- foreach (var tree in compilation.SyntaxTrees)
- {
- var root = tree.GetRoot();
- var semanticModel = compilation.GetSemanticModel(tree);
-
- var conversionOperators = root.DescendantNodes()
- .OfType()
- .ToList();
-
- foreach (var operatorDecl in conversionOperators)
- {
- var conversionInfo = GetConversionInfo(operatorDecl, semanticModel);
- if (conversionInfo != null)
- {
- conversionInfos.Add(conversionInfo);
- }
- }
- }
-
+
+ // Scan ALL types in the compilation (including source-generated) for conversion operators
+ // This must come first to ensure we catch all types before filtering
+ ScanAllTypesInCompilation(compilation, conversionInfos);
+
+ // Scan referenced assemblies for conversion operators
+ ScanReferencedAssemblies(compilation, conversionInfos);
+
+ // Scan method parameters for closed generic types like OneOf
+ ScanClosedGenericTypesInParameters(compilation, conversionInfos);
+
return conversionInfos.ToImmutableArray();
});
context.RegisterSourceOutput(allTypes, GenerateConverters!);
}
- private ConversionInfo? GetConversionInfo(ConversionOperatorDeclarationSyntax operatorDeclaration, SemanticModel semanticModel)
+ private void ScanAllTypesInCompilation(Compilation compilation, List conversionInfos)
{
- var isImplicit = operatorDeclaration.ImplicitOrExplicitKeyword.IsKind(SyntaxKind.ImplicitKeyword);
- var isExplicit = operatorDeclaration.ImplicitOrExplicitKeyword.IsKind(SyntaxKind.ExplicitKeyword);
-
- if (!isImplicit && !isExplicit)
+ // Scan ALL types declared in this compilation (including source-generated types)
+ // This catches types generated by other source generators like OneOf.SourceGenerator
+ var compilationTypes = new HashSet(SymbolEqualityComparer.Default);
+
+ // Scan from the assembly's global namespace - this includes all types in the compilation
+ CollectTypesFromNamespace(compilation.Assembly.GlobalNamespace, compilationTypes);
+
+ // For each type in the compilation, check if it has conversion operators
+ foreach (var type in compilationTypes)
{
- return null;
+ // Only process public types
+ if (type.DeclaredAccessibility != Accessibility.Public)
+ {
+ continue;
+ }
+
+ // Special handling for OneOf types - generate converters from base class info
+ // This works even if OneOf.SourceGenerator hasn't run yet
+ if (InheritsFromOneOfBase(type, out var oneOfTypeArguments) && oneOfTypeArguments != null)
+ {
+ // Generate implicit converters from each type argument to the OneOf type
+ foreach (var typeArg in oneOfTypeArguments)
+ {
+ // Create a synthetic conversion info for Enum4/Enum5/string -> MixedMatrixTestsUnion1
+ var syntheticConversion = new ConversionInfo
+ {
+ ContainingType = type,
+ SourceType = typeArg,
+ TargetType = type,
+ IsImplicit = true,
+ MethodSymbol = null! // We'll generate this synthetically
+ };
+ conversionInfos.Add(syntheticConversion);
+ }
+ }
+
+ // Get existing conversion operators for this type (in case they're already generated)
+ var conversionOperators = type.GetMembers()
+ .OfType()
+ .Where(m => (m.Name == "op_Implicit" || m.Name == "op_Explicit") &&
+ m.IsStatic &&
+ m.Parameters.Length == 1);
+
+ foreach (var method in conversionOperators)
+ {
+ var conversionInfo = GetConversionInfoFromSymbol(method);
+ if (conversionInfo != null)
+ {
+ conversionInfos.Add(conversionInfo);
+ }
+ }
}
+ }
+
+ private bool InheritsFromOneOfBase(INamedTypeSymbol type, out ImmutableArray? typeArguments)
+ {
+ typeArguments = null;
- var methodSymbol = semanticModel.GetDeclaredSymbol(operatorDeclaration) as IMethodSymbol;
- if (methodSymbol == null || !methodSymbol.IsStatic || methodSymbol.Parameters.Length != 1)
+ var currentType = type.BaseType;
+ while (currentType != null)
{
- return null;
+ // Check if this is OneOfBase
+ if (currentType.Name == "OneOfBase" && currentType.ContainingNamespace?.ToDisplayString() == "OneOf")
+ {
+ typeArguments = currentType.TypeArguments;
+ return true;
+ }
+ currentType = currentType.BaseType;
+ }
+
+ return false;
+ }
+
+ private void ScanClosedGenericTypesInParameters(Compilation compilation, List conversionInfos)
+ {
+ // Find all closed generic types used in method parameters
+ var closedGenericTypesInUse = new HashSet(SymbolEqualityComparer.Default);
+
+ foreach (var tree in compilation.SyntaxTrees)
+ {
+ var semanticModel = compilation.GetSemanticModel(tree);
+ var root = tree.GetRoot();
+
+ // Find all method declarations
+ var methods = root.DescendantNodes()
+ .OfType();
+
+ foreach (var method in methods)
+ {
+ var methodSymbol = semanticModel.GetDeclaredSymbol(method);
+ if (methodSymbol == null)
+ {
+ continue;
+ }
+
+ // Collect parameter types
+ foreach (var parameter in methodSymbol.Parameters)
+ {
+ CollectClosedGenericTypes(parameter.Type, closedGenericTypesInUse);
+ }
+ }
+ }
+
+ // For each closed generic type, check if it has conversion operators
+ foreach (var type in closedGenericTypesInUse)
+ {
+ // Get all conversion operators from this type
+ var conversionOperators = type.GetMembers()
+ .OfType()
+ .Where(m => (m.Name == "op_Implicit" || m.Name == "op_Explicit") &&
+ m.IsStatic &&
+ m.Parameters.Length == 1);
+
+ foreach (var method in conversionOperators)
+ {
+ var conversionInfo = GetConversionInfoFromSymbol(method);
+ if (conversionInfo != null)
+ {
+ conversionInfos.Add(conversionInfo);
+ }
+ }
+ }
+ }
+
+ private void CollectClosedGenericTypes(ITypeSymbol type, HashSet types)
+ {
+ if (type is INamedTypeSymbol { IsGenericType: true } namedType && !namedType.IsUnboundGenericType)
+ {
+ types.Add(namedType);
+
+ // Recursively collect type arguments
+ foreach (var typeArg in namedType.TypeArguments)
+ {
+ CollectClosedGenericTypes(typeArg, types);
+ }
+ }
+
+ // Handle arrays
+ if (type is IArrayTypeSymbol arrayType)
+ {
+ CollectClosedGenericTypes(arrayType.ElementType, types);
+ }
+ }
+
+ private void ScanReferencedAssemblies(Compilation compilation, List conversionInfos)
+ {
+ // Get all types from referenced assemblies
+ var referencedTypes = new HashSet(SymbolEqualityComparer.Default);
+
+ foreach (var reference in compilation.References)
+ {
+ if (compilation.GetAssemblyOrModuleSymbol(reference) is IAssemblySymbol assemblySymbol)
+ {
+ // Skip System assemblies and other common assemblies that won't have test-relevant converters
+ var assemblyName = assemblySymbol.Name;
+ if (assemblyName.StartsWith("System.") ||
+ assemblyName.StartsWith("Microsoft.") ||
+ assemblyName == "mscorlib" ||
+ assemblyName == "netstandard")
+ {
+ continue;
+ }
+
+ CollectTypesFromNamespace(assemblySymbol.GlobalNamespace, referencedTypes);
+ }
+ }
+
+ // Find conversion operators in referenced types
+ foreach (var type in referencedTypes)
+ {
+ // Only process public types
+ if (type.DeclaredAccessibility != Accessibility.Public)
+ {
+ continue;
+ }
+
+ // Get all members and filter for conversion operators
+ var conversionOperators = type.GetMembers()
+ .OfType()
+ .Where(m => (m.Name == "op_Implicit" || m.Name == "op_Explicit") &&
+ m.IsStatic &&
+ m.Parameters.Length == 1);
+
+ foreach (var method in conversionOperators)
+ {
+ var conversionInfo = GetConversionInfoFromSymbol(method);
+ if (conversionInfo != null)
+ {
+ conversionInfos.Add(conversionInfo);
+ }
+ }
}
+ }
+ private void CollectTypesFromNamespace(INamespaceSymbol namespaceSymbol, HashSet types)
+ {
+ foreach (var member in namespaceSymbol.GetMembers())
+ {
+ if (member is INamedTypeSymbol type)
+ {
+ types.Add(type);
+
+ // Recursively collect nested types
+ CollectNestedTypes(type, types);
+ }
+ else if (member is INamespaceSymbol childNamespace)
+ {
+ CollectTypesFromNamespace(childNamespace, types);
+ }
+ }
+ }
+
+ private void CollectNestedTypes(INamedTypeSymbol type, HashSet types)
+ {
+ foreach (var nestedType in type.GetTypeMembers())
+ {
+ types.Add(nestedType);
+ CollectNestedTypes(nestedType, types);
+ }
+ }
+
+ private ConversionInfo? GetConversionInfoFromSymbol(IMethodSymbol methodSymbol)
+ {
var containingType = methodSymbol.ContainingType;
var sourceType = methodSymbol.Parameters[0].Type;
var targetType = methodSymbol.ReturnType;
+ var isImplicit = methodSymbol.Name == "op_Implicit";
// Skip conversion operators with unbound generic type parameters
// These cannot be properly represented in AOT converters at runtime
@@ -73,15 +274,34 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
return null;
}
+ // Skip ref structs (Span, ReadOnlySpan, etc.) - they cannot be boxed to object
+ if (sourceType.IsRefLikeType || targetType.IsRefLikeType)
+ {
+ return null;
+ }
+
+ // Skip pointer types and void - they cannot be used as object
+ if (sourceType.TypeKind == TypeKind.Pointer || targetType.TypeKind == TypeKind.Pointer ||
+ sourceType.SpecialType == SpecialType.System_Void || targetType.SpecialType == SpecialType.System_Void)
+ {
+ return null;
+ }
+
// Skip conversion operators where the containing type is not publicly accessible
// The generated code won't be able to reference private/internal types
if (containingType.DeclaredAccessibility != Accessibility.Public)
{
return null;
}
-
- // Also skip if the target type of the conversion is not publicly accessible
+
+ // Also skip if the source or target type is not publicly accessible
// (unless it's a built-in type)
+ if (sourceType is INamedTypeSymbol { SpecialType: SpecialType.None } namedSourceType &&
+ namedSourceType.DeclaredAccessibility != Accessibility.Public)
+ {
+ return null;
+ }
+
if (targetType is INamedTypeSymbol { SpecialType: SpecialType.None } namedTargetType &&
namedTargetType.DeclaredAccessibility != Accessibility.Public)
{
@@ -102,7 +322,7 @@ private void GenerateConverters(SourceProductionContext context, ImmutableArray<
{
var writer = new CodeWriter();
writer.AppendLine("#nullable enable");
-
+
if (conversions.IsEmpty)
{
writer.AppendLine();
@@ -110,24 +330,38 @@ private void GenerateConverters(SourceProductionContext context, ImmutableArray<
context.AddSource("AotConverters.g.cs", writer.ToString());
return;
}
-
+
+ // Deduplicate conversions based on source and target types
+ var seenConversions = new HashSet<(ITypeSymbol Source, ITypeSymbol Target)>(
+ new TypePairEqualityComparer());
+ var uniqueConversions = new List();
+
+ foreach (var conversion in conversions)
+ {
+ if (conversion == null)
+ {
+ continue;
+ }
+
+ var key = (conversion.SourceType, conversion.TargetType);
+ if (seenConversions.Add(key))
+ {
+ uniqueConversions.Add(conversion);
+ }
+ }
+
writer.AppendLine();
writer.AppendLine("using System;");
writer.AppendLine("using TUnit.Core.Converters;");
writer.AppendLine();
writer.AppendLine("namespace TUnit.Generated;");
writer.AppendLine();
-
+
var converterIndex = 0;
var registrations = new List();
-
- foreach (var conversion in conversions)
- {
- if (conversion == null)
- {
- continue;
- }
+ foreach (var conversion in uniqueConversions)
+ {
var converterClassName = $"AotConverter_{converterIndex++}";
var sourceTypeName = conversion.SourceType.GloballyQualified();
var targetTypeName = conversion.TargetType.GloballyQualified();
@@ -143,12 +377,25 @@ private void GenerateConverters(SourceProductionContext context, ImmutableArray<
writer.AppendLine("public object? Convert(object? value)");
writer.AppendLine("{");
writer.Indent();
-
+
writer.AppendLine("if (value == null) return null;");
- writer.AppendLine($"if (value is {sourceTypeName} typedValue)");
+
+ // For nullable value types, we need to use the underlying type in the pattern
+ // because you can't use nullable types in patterns in older C# versions
+ var sourceType = conversion.SourceType;
+ var underlyingType = sourceType.IsValueType && sourceType is INamedTypeSymbol named && named.OriginalDefinition?.SpecialType == SpecialType.System_Nullable_T
+ ? ((INamedTypeSymbol)sourceType).TypeArguments[0]
+ : sourceType;
+
+ var patternTypeName = underlyingType.GloballyQualified();
+
+ writer.AppendLine($"if (value is {patternTypeName} typedValue)");
writer.AppendLine("{");
writer.Indent();
+
+ // Use regular cast syntax - it works fine in AOT when types are known at compile-time
writer.AppendLine($"return ({targetTypeName})typedValue;");
+
writer.Unindent();
writer.AppendLine("}");
writer.AppendLine("return value; // Return original value if type doesn't match");
@@ -196,4 +443,24 @@ private class ConversionInfo
public required bool IsImplicit { get; init; }
public required IMethodSymbol MethodSymbol { get; init; }
}
+
+ private class TypePairEqualityComparer : IEqualityComparer<(ITypeSymbol Source, ITypeSymbol Target)>
+ {
+ public bool Equals((ITypeSymbol Source, ITypeSymbol Target) x, (ITypeSymbol Source, ITypeSymbol Target) y)
+ {
+ return SymbolEqualityComparer.Default.Equals(x.Source, y.Source) &&
+ SymbolEqualityComparer.Default.Equals(x.Target, y.Target);
+ }
+
+ public int GetHashCode((ITypeSymbol Source, ITypeSymbol Target) obj)
+ {
+ unchecked
+ {
+ var hash = 17;
+ hash = hash * 31 + SymbolEqualityComparer.Default.GetHashCode(obj.Source);
+ hash = hash * 31 + SymbolEqualityComparer.Default.GetHashCode(obj.Target);
+ return hash;
+ }
+ }
+ }
}
\ No newline at end of file
diff --git a/TUnit.Core.SourceGenerator/Generators/PropertyInjectionSourceGenerator.cs b/TUnit.Core.SourceGenerator/Generators/PropertyInjectionSourceGenerator.cs
index 39327816d3..18a68b1e70 100644
--- a/TUnit.Core.SourceGenerator/Generators/PropertyInjectionSourceGenerator.cs
+++ b/TUnit.Core.SourceGenerator/Generators/PropertyInjectionSourceGenerator.cs
@@ -119,7 +119,7 @@ private static bool CanSetProperty(IPropertySymbol property)
private static bool IsPubliclyAccessible(INamedTypeSymbol typeSymbol)
{
// Check if the type itself is public or internal
- if (typeSymbol.DeclaredAccessibility != Accessibility.Public &&
+ if (typeSymbol.DeclaredAccessibility != Accessibility.Public &&
typeSymbol.DeclaredAccessibility != Accessibility.Internal)
{
return false;
@@ -130,7 +130,7 @@ private static bool IsPubliclyAccessible(INamedTypeSymbol typeSymbol)
var containingType = typeSymbol.ContainingType;
while (containingType != null)
{
- if (containingType.DeclaredAccessibility != Accessibility.Public &&
+ if (containingType.DeclaredAccessibility != Accessibility.Public &&
containingType.DeclaredAccessibility != Accessibility.Internal)
{
return false;
@@ -161,10 +161,10 @@ private static void GenerateIndividualPropertyInjectionSource(SourceProductionCo
var sourceBuilder = new StringBuilder();
WriteFileHeader(sourceBuilder);
-
+
// Generate individual module initializer for this class
GenerateIndividualModuleInitializer(sourceBuilder, classInfo, sourceClassName);
-
+
// Generate property source for this class
GeneratePropertySource(sourceBuilder, classInfo, sourceClassName);
@@ -188,7 +188,7 @@ private static string GetSafeClassName(INamedTypeSymbol classSymbol)
private static void GenerateIndividualModuleInitializer(StringBuilder sb, ClassWithDataSourceProperties classInfo, string sourceClassName)
{
var safeName = GetSafeClassName(classInfo.ClassSymbol);
-
+
sb.AppendLine($"internal static class {safeName}_PropertyInjectionInitializer");
sb.AppendLine("{");
sb.AppendLine(" [global::System.Runtime.CompilerServices.ModuleInitializer]");
@@ -238,7 +238,7 @@ private static void GenerateUnsafeAccessorMethods(StringBuilder sb, ClassWithDat
{
if (propInfo.Property.SetMethod?.IsInitOnly == true)
{
- var propertyType = propInfo.Property.Type.ToDisplayString();
+ var propertyType = GetNonNullableTypeName(propInfo.Property.Type);
var backingFieldName = $"<{propInfo.Property.Name}>k__BackingField";
// Use the property's containing type for the UnsafeAccessor, not the derived class
@@ -276,7 +276,7 @@ private static void GenerateGetPropertyMetadata(StringBuilder sb, ClassWithDataS
private static void GeneratePropertyMetadata(StringBuilder sb, PropertyWithDataSourceAttribute propInfo, INamedTypeSymbol classSymbol, string classTypeName)
{
var propertyName = propInfo.Property.Name;
- var propertyType = propInfo.Property.Type.ToDisplayString();
+ var propertyType = GetNonNullableTypeName(propInfo.Property.Type);
var propertyTypeForTypeof = GetNonNullableTypeString(propInfo.Property.Type);
var attributeTypeName = propInfo.DataSourceAttribute.AttributeClass!.ToDisplayString();
var attributeClass = propInfo.DataSourceAttribute.AttributeClass!;
@@ -324,7 +324,7 @@ private static void GenerateDataSourceCreation(StringBuilder sb, AttributeData a
// which is called by PropertyDataResolver.GetInitializedDataSourceAsync
sb.AppendLine(" return dataSource;");
}
-
+
private static bool IsCustomDataSource(INamedTypeSymbol attributeClass)
{
// Check if this class implements IDataSourceAttribute
@@ -339,7 +339,7 @@ private static void GeneratePropertySetting(StringBuilder sb, PropertyWithDataSo
sb.AppendLine("#if NET8_0_OR_GREATER");
// Cast to the property's containing type if needed
- var containingType = propInfo.Property.ContainingType.ToDisplayString();
+ var containingType = GetNonNullableTypeName(propInfo.Property.ContainingType);
if (containingType != classTypeName)
{
sb.AppendLine($" Get{propInfo.Property.Name}BackingField(({containingType}){instanceVariableName}) = {castExpression};");
@@ -349,14 +349,14 @@ private static void GeneratePropertySetting(StringBuilder sb, PropertyWithDataSo
sb.AppendLine($" Get{propInfo.Property.Name}BackingField({instanceVariableName}) = {castExpression};");
}
sb.AppendLine("#else");
- sb.AppendLine($" var backingField = typeof({propInfo.Property.ContainingType.ToDisplayString()}).GetField(\"<{propInfo.Property.Name}>k__BackingField\",");
+ sb.AppendLine($" var backingField = typeof({GetNonNullableTypeName(propInfo.Property.ContainingType)}).GetField(\"<{propInfo.Property.Name}>k__BackingField\",");
sb.AppendLine(" global::System.Reflection.BindingFlags.Instance | global::System.Reflection.BindingFlags.NonPublic);");
- sb.AppendLine($" backingField?.SetValue({instanceVariableName}, value);");
+ sb.AppendLine($" backingField.SetValue({instanceVariableName}, value);");
sb.AppendLine("#endif");
}
else if (propInfo.Property.IsStatic)
{
- var className = propInfo.Property.ContainingType.ToDisplayString();
+ var className = GetNonNullableTypeName(propInfo.Property.ContainingType);
var castExpression = GetPropertyCastExpression(propInfo.Property, propertyType);
sb.AppendLine($" {className}.{propInfo.Property.Name} = {castExpression};");
}
@@ -428,16 +428,16 @@ private static string GetNonNullableTypeString(ITypeSymbol typeSymbol)
{
if (typeSymbol is INamedTypeSymbol { IsReferenceType: true })
{
- return typeSymbol.WithNullableAnnotation(NullableAnnotation.NotAnnotated).ToDisplayString();
+ return typeSymbol.WithNullableAnnotation(NullableAnnotation.NotAnnotated).GloballyQualified();
}
}
if (typeSymbol is INamedTypeSymbol { IsGenericType: true, ConstructedFrom.SpecialType: SpecialType.System_Nullable_T } namedType)
{
- return namedType.TypeArguments[0].ToDisplayString();
+ return namedType.TypeArguments[0].GloballyQualified();
}
- var displayString = typeSymbol.ToDisplayString();
+ var displayString = typeSymbol.GloballyQualified();
if (displayString.EndsWith("?"))
{
@@ -446,6 +446,9 @@ private static string GetNonNullableTypeString(ITypeSymbol typeSymbol)
return displayString;
}
+
+ // Alias for consistency
+ private static string GetNonNullableTypeName(ITypeSymbol typeSymbol) => GetNonNullableTypeString(typeSymbol);
}
internal sealed class ClassWithDataSourceProperties
diff --git a/TUnit.Core.SourceGenerator/Helpers/GenericTypeInference.cs b/TUnit.Core.SourceGenerator/Helpers/GenericTypeInference.cs
index 547bfe31b2..52cfbbd9ce 100644
--- a/TUnit.Core.SourceGenerator/Helpers/GenericTypeInference.cs
+++ b/TUnit.Core.SourceGenerator/Helpers/GenericTypeInference.cs
@@ -166,7 +166,7 @@ internal static class GenericTypeInference
{
// Look for IInfersType in the attribute's interfaces
var infersTypeInterface = attr.AttributeClass.AllInterfaces
- .FirstOrDefault(i => ((ISymbol)i).GloballyQualifiedNonGeneric() == "global::TUnit.Core.Interfaces.IInfersType" &&
+ .FirstOrDefault(i => i.GloballyQualifiedNonGeneric() == "global::TUnit.Core.Interfaces.IInfersType" &&
i.IsGenericType &&
i.TypeArguments.Length == 1);
diff --git a/TUnit.Core.Tests/Helpers/ArgumentFormatterTests.cs b/TUnit.Core.Tests/Helpers/ArgumentFormatterTests.cs
index 0ea03ce2af..e0df369cf3 100644
--- a/TUnit.Core.Tests/Helpers/ArgumentFormatterTests.cs
+++ b/TUnit.Core.Tests/Helpers/ArgumentFormatterTests.cs
@@ -58,9 +58,39 @@ public void FormatDefault_LargeTuple_FormatsAllElements()
public void FormatDefault_TupleWithNull_HandlesNullCorrectly()
{
var tuple = (1, null, "test");
-
+
var result = ArgumentFormatter.Format(tuple, []);
-
+
Assert.That(result, Is.EqualTo("(1, null, test)"));
}
+
+ [Test]
+ public void FormatDefault_StringWithDots_EscapesDotsWithMiddleDot()
+ {
+ var stringWithDots = "1.2.3";
+
+ var result = ArgumentFormatter.Format(stringWithDots, []);
+
+ Assert.That(result, Is.EqualTo("1·2·3"));
+ }
+
+ [Test]
+ public void FormatDefault_StringWithoutDots_ReturnsUnchanged()
+ {
+ var stringWithoutDots = "hello world";
+
+ var result = ArgumentFormatter.Format(stringWithoutDots, []);
+
+ Assert.That(result, Is.EqualTo("hello world"));
+ }
+
+ [Test]
+ public void FormatArguments_WithStringsContainingDots_EscapesDots()
+ {
+ var args = new object?[] { "hello", "with.dot", "1.2.3" };
+
+ var result = ArgumentFormatter.FormatArguments(args);
+
+ Assert.That(result, Is.EqualTo("hello, with·dot, 1·2·3"));
+ }
}
\ No newline at end of file
diff --git a/TUnit.Core/AotCompatibility/GenericTestRegistry.cs b/TUnit.Core/AotCompatibility/GenericTestRegistry.cs
index 3e5a8f1b74..35e3955885 100644
--- a/TUnit.Core/AotCompatibility/GenericTestRegistry.cs
+++ b/TUnit.Core/AotCompatibility/GenericTestRegistry.cs
@@ -91,7 +91,7 @@ public static void MarkAsAotCompatible(MethodInfo method)
AotCompatibleMethods.Add(method);
}
- private static readonly HashSet AotCompatibleMethods = new();
+ private static readonly HashSet AotCompatibleMethods = [];
///
/// Checks if a method has been marked as AOT-compatible.
diff --git a/TUnit.Core/AsyncConvert.cs b/TUnit.Core/AsyncConvert.cs
index d793369cf3..bb660a847b 100644
--- a/TUnit.Core/AsyncConvert.cs
+++ b/TUnit.Core/AsyncConvert.cs
@@ -55,10 +55,7 @@ public static async ValueTask Convert(Func action)
| MethodImplOptions.AggressiveOptimization
#endif
)]
- #if NET6_0_OR_GREATER
- [RequiresUnreferencedCode("ConvertObject uses reflection to handle custom awaitable types and F# async. For AOT compatibility, use Task or ValueTask directly.")]
- [RequiresDynamicCode("ConvertObject may require dynamic invocation for custom awaitable types. For AOT compatibility, use Task or ValueTask directly.")]
- #endif
+
public static async ValueTask ConvertObject(object? invoke)
{
if (invoke is Delegate @delegate)
@@ -116,17 +113,23 @@ public static async ValueTask ConvertObject(object? invoke)
}
}
- [System.Diagnostics.CodeAnalysis.DynamicDependency(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicMethods, "Microsoft.FSharp.Control.FSharpAsync", "FSharp.Core")]
- [System.Diagnostics.CodeAnalysis.DynamicDependency("StartAsTask", "Microsoft.FSharp.Control.FSharpAsync", "FSharp.Core")]
- #if NET6_0_OR_GREATER
- [RequiresUnreferencedCode("F# async support requires FSharp.Core types and reflection. For AOT, use Task-based APIs.")]
- [RequiresDynamicCode("F# async interop requires MakeGenericMethod. For AOT, use Task-based APIs.")]
- #endif
+ [DynamicDependency(DynamicallyAccessedMemberTypes.PublicMethods, "Microsoft.FSharp.Control.FSharpAsync", "FSharp.Core")]
+ [DynamicDependency("StartAsTask", "Microsoft.FSharp.Control.FSharpAsync", "FSharp.Core")]
+ [UnconditionalSuppressMessage("Trimming", "IL2026:Members annotated with \'RequiresUnreferencedCodeAttribute\' require dynamic access otherwise can break functionality when trimming application code")]
+ [UnconditionalSuppressMessage("Trimming", "IL2060:Call to \'System.Reflection.MethodInfo.MakeGenericMethod\' can not be statically analyzed. It\'s not possible to guarantee the availability of requirements of the generic method.")]
+ [UnconditionalSuppressMessage("Trimming", "IL2072:Target parameter argument does not satisfy \'DynamicallyAccessedMembersAttribute\' in call to target method. The return value of the source method does not have matching annotations.")]
+ [UnconditionalSuppressMessage("Trimming", "IL2077:Target parameter argument does not satisfy \'DynamicallyAccessedMembersAttribute\' in call to target method. The source field does not have matching annotations.")]
+ [UnconditionalSuppressMessage("AOT", "IL3050:Calling members annotated with \'RequiresDynamicCodeAttribute\' may break functionality when AOT compiling.")]
private static ValueTask StartAsFSharpTask(object invoke, Type type)
{
var startAsTaskOpenGenericMethod = (_fSharpAsyncType ??= type.Assembly.GetType("Microsoft.FSharp.Control.FSharpAsync"))!
.GetRuntimeMethods()
- .First(m => m.Name == "StartAsTask");
+ .FirstOrDefault(m => m.Name == "StartAsTask");
+
+ if (startAsTaskOpenGenericMethod is null)
+ {
+ return default;
+ }
var fSharpTask = (Task) startAsTaskOpenGenericMethod.MakeGenericMethod(type.GetGenericArguments()[0])
.Invoke(null, [invoke, null, null])!;
@@ -143,16 +146,11 @@ private static ValueTask StartAsFSharpTask(object invoke, Type type)
///
/// Safely invokes F# async conversion with proper suppression for the call site.
///
- #if NET6_0_OR_GREATER
- [RequiresUnreferencedCode("F# async is an optional feature. AOT applications should use Task-based APIs.")]
- [RequiresDynamicCode("F# async requires runtime code generation. This is documented as not AOT-compatible.")]
- #endif
private static async ValueTask StartAsFSharpTaskSafely(object invoke, Type type)
{
if (IsFSharpAsyncSupported())
{
await StartAsFSharpTask(invoke, type);
- return;
}
}
@@ -181,10 +179,7 @@ private static bool IsFSharpAsyncSupported()
}
}
- #if NET6_0_OR_GREATER
- [RequiresUnreferencedCode("GetAwaiter pattern detection requires reflection for custom awaitable types. For AOT, use Task/ValueTask.")]
- [RequiresDynamicCode("Custom awaitable handling may require dynamic invocation. For AOT, use Task/ValueTask.")]
- #endif
+ [UnconditionalSuppressMessage("Trimming", "IL2075:\'this\' argument does not satisfy \'DynamicallyAccessedMembersAttribute\' in call to target method. The return value of the source method does not have matching annotations.")]
public static bool TryGetAwaitableTask(object awaitable, [NotNullWhen(true)] out Task? task)
{
var getAwaiter = awaitable.GetType().GetMethod("GetAwaiter", Type.EmptyTypes);
diff --git a/TUnit.Core/Attributes/TestData/AsyncDataSourceGeneratorAttribute.cs b/TUnit.Core/Attributes/TestData/AsyncDataSourceGeneratorAttribute.cs
index 3bb6cf0ff4..8045badda6 100644
--- a/TUnit.Core/Attributes/TestData/AsyncDataSourceGeneratorAttribute.cs
+++ b/TUnit.Core/Attributes/TestData/AsyncDataSourceGeneratorAttribute.cs
@@ -3,7 +3,7 @@
namespace TUnit.Core;
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class | AttributeTargets.Property, AllowMultiple = true)]
-public abstract class AsyncDataSourceGeneratorAttribute<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicMethods)] T> : TypedDataSourceAttribute
+public abstract class AsyncDataSourceGeneratorAttribute<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] T> : TypedDataSourceAttribute
{
protected abstract IAsyncEnumerable>> GenerateDataSourcesAsync(DataGeneratorMetadata dataGeneratorMetadata);
@@ -18,9 +18,9 @@ public sealed override async IAsyncEnumerable>> GetTypedDataRowsAsy
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = true)]
public abstract class AsyncDataSourceGeneratorAttribute<
- [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicMethods)]
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)]
T1,
- [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicMethods)]
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)]
T2> : TypedDataSourceAttribute<(T1, T2)>
{
protected abstract IAsyncEnumerable>> GenerateDataSourcesAsync(DataGeneratorMetadata dataGeneratorMetadata);
@@ -36,11 +36,11 @@ public abstract class AsyncDataSourceGeneratorAttribute<
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = true)]
public abstract class AsyncDataSourceGeneratorAttribute<
- [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicMethods)]
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)]
T1,
- [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicMethods)]
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)]
T2,
- [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicMethods)]
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)]
T3> : TypedDataSourceAttribute<(T1, T2, T3)>
{
protected abstract IAsyncEnumerable>> GenerateDataSourcesAsync(DataGeneratorMetadata dataGeneratorMetadata);
@@ -56,13 +56,13 @@ public abstract class AsyncDataSourceGeneratorAttribute<
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = true)]
public abstract class AsyncDataSourceGeneratorAttribute<
- [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicMethods)]
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)]
T1,
- [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicMethods)]
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)]
T2,
- [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicMethods)]
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)]
T3,
- [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicMethods)]
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)]
T4> : TypedDataSourceAttribute<(T1, T2, T3, T4)>
{
protected abstract IAsyncEnumerable>> GenerateDataSourcesAsync(DataGeneratorMetadata dataGeneratorMetadata);
@@ -78,15 +78,15 @@ public abstract class AsyncDataSourceGeneratorAttribute<
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = true)]
public abstract class AsyncDataSourceGeneratorAttribute<
- [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicMethods)]
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)]
T1,
- [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicMethods)]
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)]
T2,
- [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicMethods)]
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)]
T3,
- [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicMethods)]
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)]
T4,
- [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicMethods)]
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)]
T5> : TypedDataSourceAttribute<(T1, T2, T3, T4, T5)>
{
protected abstract IAsyncEnumerable>> GenerateDataSourcesAsync(DataGeneratorMetadata dataGeneratorMetadata);
diff --git a/TUnit.Core/Attributes/TestData/AsyncUntypedDataSourceSourceGeneratorAttribute.cs b/TUnit.Core/Attributes/TestData/AsyncUntypedDataSourceSourceGeneratorAttribute.cs
index e483358c28..d2bf7ce4c8 100644
--- a/TUnit.Core/Attributes/TestData/AsyncUntypedDataSourceSourceGeneratorAttribute.cs
+++ b/TUnit.Core/Attributes/TestData/AsyncUntypedDataSourceSourceGeneratorAttribute.cs
@@ -3,10 +3,6 @@
namespace TUnit.Core;
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class | AttributeTargets.Property, AllowMultiple = true)]
-#if NET6_0_OR_GREATER
-[RequiresDynamicCode("AsyncUntypedDataSourceGeneratorAttribute requires dynamic code generation for runtime data source creation. Consider using strongly-typed AsyncDataSourceGeneratorAttribute overloads for AOT compatibility.")]
-[RequiresUnreferencedCode("AsyncUntypedDataSourceGeneratorAttribute may require unreferenced code for runtime data source creation. Consider using strongly-typed AsyncDataSourceGeneratorAttribute overloads for AOT compatibility.")]
-#endif
public abstract class AsyncUntypedDataSourceGeneratorAttribute : Attribute, IAsyncUntypedDataSourceGeneratorAttribute
{
protected abstract IAsyncEnumerable>> GenerateDataSourcesAsync(DataGeneratorMetadata dataGeneratorMetadata);
diff --git a/TUnit.Core/Attributes/TestData/ClassDataSourceAttribute.cs b/TUnit.Core/Attributes/TestData/ClassDataSourceAttribute.cs
index 371ab024ba..f1fc523760 100644
--- a/TUnit.Core/Attributes/TestData/ClassDataSourceAttribute.cs
+++ b/TUnit.Core/Attributes/TestData/ClassDataSourceAttribute.cs
@@ -4,7 +4,7 @@
namespace TUnit.Core;
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.Property, AllowMultiple = true)]
-public sealed class ClassDataSourceAttribute<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties | DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)] T>
+public sealed class ClassDataSourceAttribute<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)] T>
: DataSourceGeneratorAttribute
{
public SharedType Shared { get; set; } = SharedType.None;
@@ -25,88 +25,66 @@ protected override IEnumerable> GenerateDataSources(DataGeneratorMetadat
}
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.Property, AllowMultiple = true)]
-#if NET6_0_OR_GREATER
-[RequiresUnreferencedCode("ClassDataSourceAttribute uses reflection to instantiate and access test data classes. For AOT compatibility, use strongly-typed ClassDataSourceAttribute instead.")]
-[RequiresDynamicCode("ClassDataSourceAttribute may require runtime type generation. For AOT compatibility, use strongly-typed ClassDataSourceAttribute instead.")]
-#endif
public sealed class ClassDataSourceAttribute : UntypedDataSourceGeneratorAttribute
{
private readonly Type[] _types;
- #if NET6_0_OR_GREATER
- [RequiresUnreferencedCode("ClassDataSourceAttribute uses reflection to instantiate and access test data classes. For AOT compatibility, use strongly-typed ClassDataSourceAttribute instead.")]
- [RequiresDynamicCode("ClassDataSourceAttribute may require runtime type generation. For AOT compatibility, use strongly-typed ClassDataSourceAttribute instead.")]
- #endif
+ [UnconditionalSuppressMessage("Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", Justification = "Non-params constructor calls params one with proper annotations.")]
public ClassDataSourceAttribute(
- [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties | DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)]
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)]
Type type) : this([type])
{
}
- #if NET6_0_OR_GREATER
- [RequiresUnreferencedCode("ClassDataSourceAttribute uses reflection to instantiate and access test data classes. For AOT compatibility, use strongly-typed ClassDataSourceAttribute instead.")]
- [RequiresDynamicCode("ClassDataSourceAttribute may require runtime type generation. For AOT compatibility, use strongly-typed ClassDataSourceAttribute instead.")]
- #endif
+ [UnconditionalSuppressMessage("Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", Justification = "Non-params constructor calls params one with proper annotations.")]
public ClassDataSourceAttribute(
- [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties | DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)]
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)]
Type type,
- [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties | DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)]
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)]
Type type2) : this([type, type2])
{
}
- #if NET6_0_OR_GREATER
- [RequiresUnreferencedCode("ClassDataSourceAttribute uses reflection to instantiate and access test data classes. For AOT compatibility, use strongly-typed ClassDataSourceAttribute instead.")]
- [RequiresDynamicCode("ClassDataSourceAttribute may require runtime type generation. For AOT compatibility, use strongly-typed ClassDataSourceAttribute instead.")]
- #endif
+ [UnconditionalSuppressMessage("Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", Justification = "Non-params constructor calls params one with proper annotations.")]
public ClassDataSourceAttribute(
- [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties | DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)]
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)]
Type type,
- [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties | DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)]
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)]
Type type2,
- [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties | DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)]
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)]
Type type3) : this([type, type2, type3])
{
}
- #if NET6_0_OR_GREATER
- [RequiresUnreferencedCode("ClassDataSourceAttribute uses reflection to instantiate and access test data classes. For AOT compatibility, use strongly-typed ClassDataSourceAttribute instead.")]
- [RequiresDynamicCode("ClassDataSourceAttribute may require runtime type generation. For AOT compatibility, use strongly-typed ClassDataSourceAttribute instead.")]
- #endif
+ [UnconditionalSuppressMessage("Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", Justification = "Non-params constructor calls params one with proper annotations.")]
public ClassDataSourceAttribute(
- [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties | DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)]
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)]
Type type,
- [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties | DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)]
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)]
Type type2,
- [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties | DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)]
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)]
Type type3,
- [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties | DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)]
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)]
Type type4) : this([type, type2, type3, type4])
{
}
- #if NET6_0_OR_GREATER
- [RequiresUnreferencedCode("ClassDataSourceAttribute uses reflection to instantiate and access test data classes. For AOT compatibility, use strongly-typed ClassDataSourceAttribute instead.")]
- [RequiresDynamicCode("ClassDataSourceAttribute may require runtime type generation. For AOT compatibility, use strongly-typed ClassDataSourceAttribute instead.")]
- #endif
+ [UnconditionalSuppressMessage("Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", Justification = "Non-params constructor calls params one with proper annotations.")]
public ClassDataSourceAttribute(
- [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties | DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)]
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)]
Type type,
- [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties | DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)]
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)]
Type type2,
- [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties | DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)]
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)]
Type type3,
- [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties | DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)]
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)]
Type type4,
- [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties | DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)]
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)]
Type type5) : this([type, type2, type3, type4, type5])
{
}
- #if NET6_0_OR_GREATER
- [RequiresUnreferencedCode("Reflection")]
- [RequiresDynamicCode("Reflection")]
- #endif
+ [RequiresUnreferencedCode("Uses dynamically accessed types.")]
public ClassDataSourceAttribute(params Type[] types)
{
_types = types;
@@ -115,10 +93,8 @@ public ClassDataSourceAttribute(params Type[] types)
public SharedType[] Shared { get; set; } = [SharedType.None];
public string[] Keys { get; set; } = [];
- #if NET6_0_OR_GREATER
- [RequiresUnreferencedCode("Reflection")]
- [RequiresDynamicCode("Reflection")]
- #endif
+ [UnconditionalSuppressMessage("Trimming", "IL2062:The parameter of method has a DynamicallyAccessedMembersAttribute, but the value passed to it can not be statically analyzed.",
+ Justification = "Constructor parameter is annotated with DynamicallyAccessedMembers, so _types elements have the required annotations.")]
protected override IEnumerable> GenerateDataSources(DataGeneratorMetadata dataGeneratorMetadata)
{
yield return () =>
diff --git a/TUnit.Core/Attributes/TestData/ClassDataSourceAttribute_2.cs b/TUnit.Core/Attributes/TestData/ClassDataSourceAttribute_2.cs
index 402427739d..b6a06a98d9 100644
--- a/TUnit.Core/Attributes/TestData/ClassDataSourceAttribute_2.cs
+++ b/TUnit.Core/Attributes/TestData/ClassDataSourceAttribute_2.cs
@@ -5,8 +5,8 @@ namespace TUnit.Core;
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)]
public sealed class ClassDataSourceAttribute<
- [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties | DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)] T1,
- [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties | DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)] T2>
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)] T1,
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)] T2>
: DataSourceGeneratorAttribute
where T1 : new()
where T2 : new()
diff --git a/TUnit.Core/Attributes/TestData/ClassDataSourceAttribute_3.cs b/TUnit.Core/Attributes/TestData/ClassDataSourceAttribute_3.cs
index 62393fd596..010a7dbde6 100644
--- a/TUnit.Core/Attributes/TestData/ClassDataSourceAttribute_3.cs
+++ b/TUnit.Core/Attributes/TestData/ClassDataSourceAttribute_3.cs
@@ -5,9 +5,9 @@ namespace TUnit.Core;
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)]
public sealed class ClassDataSourceAttribute<
- [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties | DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)] T1,
- [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties | DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)] T2,
- [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties | DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)] T3>
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)] T1,
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)] T2,
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)] T3>
: DataSourceGeneratorAttribute
where T1 : new()
where T2 : new()
diff --git a/TUnit.Core/Attributes/TestData/ClassDataSourceAttribute_4.cs b/TUnit.Core/Attributes/TestData/ClassDataSourceAttribute_4.cs
index 221c47c382..ed4c73fa1c 100644
--- a/TUnit.Core/Attributes/TestData/ClassDataSourceAttribute_4.cs
+++ b/TUnit.Core/Attributes/TestData/ClassDataSourceAttribute_4.cs
@@ -5,10 +5,10 @@ namespace TUnit.Core;
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)]
public sealed class ClassDataSourceAttribute<
- [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties | DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)] T1,
- [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties | DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)] T2,
- [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties | DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)] T3,
- [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties | DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)] T4>
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)] T1,
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)] T2,
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)] T3,
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)] T4>
: DataSourceGeneratorAttribute
where T1 : new()
where T2 : new()
diff --git a/TUnit.Core/Attributes/TestData/ClassDataSourceAttribute_5.cs b/TUnit.Core/Attributes/TestData/ClassDataSourceAttribute_5.cs
index 1c74d6da23..f65b362fd5 100644
--- a/TUnit.Core/Attributes/TestData/ClassDataSourceAttribute_5.cs
+++ b/TUnit.Core/Attributes/TestData/ClassDataSourceAttribute_5.cs
@@ -5,11 +5,11 @@ namespace TUnit.Core;
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)]
public sealed class ClassDataSourceAttribute<
- [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties | DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)] T1,
- [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties | DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)] T2,
- [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties | DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)] T3,
- [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties | DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)] T4,
- [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties | DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)] T5>
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)] T1,
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)] T2,
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)] T3,
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)] T4,
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)] T5>
: DataSourceGeneratorAttribute
where T1 : new()
where T2 : new()
diff --git a/TUnit.Core/Attributes/TestData/ClassDataSources.cs b/TUnit.Core/Attributes/TestData/ClassDataSources.cs
index b66dd6cdfd..a933e860b4 100644
--- a/TUnit.Core/Attributes/TestData/ClassDataSources.cs
+++ b/TUnit.Core/Attributes/TestData/ClassDataSources.cs
@@ -18,7 +18,7 @@ public static ClassDataSources Get(string sessionId)
return SourcesPerSession.GetOrAdd(sessionId, static _ => new ClassDataSources());
}
- public (T, SharedType, string) GetItemForIndexAsync<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties | DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)] T>(int index, Type testClassType, SharedType[] sharedTypes, string[] keys, DataGeneratorMetadata dataGeneratorMetadata) where T : new()
+ public (T, SharedType, string) GetItemForIndexAsync<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)] T>(int index, Type testClassType, SharedType[] sharedTypes, string[] keys, DataGeneratorMetadata dataGeneratorMetadata) where T : new()
{
var shared = sharedTypes.ElementAtOrDefault(index);
@@ -39,7 +39,7 @@ private string GetKey(int index, SharedType[] sharedTypes, string[] keys)
return keys.ElementAtOrDefault(keyedIndex) ?? throw new ArgumentException($"Key at index {keyedIndex} not found");
}
- public T Get<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties | DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)] T>(SharedType sharedType, Type testClassType, string key, DataGeneratorMetadata dataGeneratorMetadata)
+ public T Get<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)] T>(SharedType sharedType, Type testClassType, string key, DataGeneratorMetadata dataGeneratorMetadata)
{
return sharedType switch
{
@@ -52,7 +52,7 @@ private string GetKey(int index, SharedType[] sharedTypes, string[] keys)
};
}
- public object? Get(SharedType sharedType, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties | DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)] Type type, Type testClassType, string? key, DataGeneratorMetadata dataGeneratorMetadata)
+ public object? Get(SharedType sharedType, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)] Type type, Type testClassType, string? key, DataGeneratorMetadata dataGeneratorMetadata)
{
return sharedType switch
{
@@ -66,16 +66,31 @@ private string GetKey(int index, SharedType[] sharedTypes, string[] keys)
}
[return: NotNull]
- private static T Create<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties | DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)] T>(DataGeneratorMetadata dataGeneratorMetadata)
+ private static T Create<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)] T>(DataGeneratorMetadata dataGeneratorMetadata)
{
return ((T) Create(typeof(T), dataGeneratorMetadata))!;
}
- private static object Create([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties | DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)] Type type, DataGeneratorMetadata dataGeneratorMetadata)
+ private static object Create([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)] Type type, DataGeneratorMetadata dataGeneratorMetadata)
{
+ return CreateWithNestedDependencies(type, dataGeneratorMetadata, recursionDepth: 0);
+ }
+
+ private const int MaxRecursionDepth = 10;
+
+ [UnconditionalSuppressMessage("Trimming", "IL2072:Target parameter argument does not satisfy 'DynamicallyAccessedMembersAttribute' requirements",
+ Justification = "PropertyType from PropertyInjectionMetadata has the required DynamicallyAccessedMembers annotations")]
+ private static object CreateWithNestedDependencies([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)] Type type, DataGeneratorMetadata dataGeneratorMetadata, int recursionDepth)
+ {
+ if (recursionDepth >= MaxRecursionDepth)
+ {
+ throw new InvalidOperationException($"Maximum recursion depth ({MaxRecursionDepth}) exceeded when creating nested ClassDataSource dependencies. This may indicate a circular dependency.");
+ }
+
+ object instance;
try
{
- return Activator.CreateInstance(type)!;
+ instance = Activator.CreateInstance(type)!;
}
catch (TargetInvocationException targetInvocationException)
{
@@ -86,5 +101,21 @@ private static object Create([DynamicallyAccessedMembers(DynamicallyAccessedMemb
throw;
}
+
+ // Populate nested ClassDataSource properties recursively
+ var propertySource = PropertySourceRegistry.GetSource(type);
+ if (propertySource?.ShouldInitialize == true)
+ {
+ var propertyMetadata = propertySource.GetPropertyMetadata();
+ foreach (var metadata in propertyMetadata)
+ {
+ // Recursively create the property value using CreateWithNestedDependencies
+ // This will handle nested ClassDataSource properties
+ var propertyValue = CreateWithNestedDependencies(metadata.PropertyType, dataGeneratorMetadata, recursionDepth + 1);
+ metadata.SetProperty(instance, propertyValue);
+ }
+ }
+
+ return instance;
}
}
diff --git a/TUnit.Core/Attributes/TestData/DataSourceGeneratorAttribute.cs b/TUnit.Core/Attributes/TestData/DataSourceGeneratorAttribute.cs
index 134099ca1b..93ade8f6ab 100644
--- a/TUnit.Core/Attributes/TestData/DataSourceGeneratorAttribute.cs
+++ b/TUnit.Core/Attributes/TestData/DataSourceGeneratorAttribute.cs
@@ -4,7 +4,7 @@ namespace TUnit.Core;
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class | AttributeTargets.Property, AllowMultiple = true)]
public abstract class DataSourceGeneratorAttribute<
- [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicMethods)]
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)]
T> : AsyncDataSourceGeneratorAttribute
{
protected abstract IEnumerable> GenerateDataSources(DataGeneratorMetadata dataGeneratorMetadata);
@@ -22,9 +22,9 @@ protected override async IAsyncEnumerable>> GenerateDataSourcesAsyn
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = true)]
public abstract class DataSourceGeneratorAttribute<
- [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicMethods)]
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)]
T1,
- [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicMethods)]
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)]
T2> : AsyncDataSourceGeneratorAttribute
{
protected abstract IEnumerable> GenerateDataSources(DataGeneratorMetadata dataGeneratorMetadata);
@@ -42,11 +42,11 @@ public abstract class DataSourceGeneratorAttribute<
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = true)]
public abstract class DataSourceGeneratorAttribute<
- [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicMethods)]
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)]
T1,
- [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicMethods)]
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)]
T2,
- [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicMethods)]
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)]
T3> : AsyncDataSourceGeneratorAttribute
{
protected abstract IEnumerable> GenerateDataSources(DataGeneratorMetadata dataGeneratorMetadata);
@@ -64,13 +64,13 @@ public abstract class DataSourceGeneratorAttribute<
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = true)]
public abstract class DataSourceGeneratorAttribute<
- [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicMethods)]
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)]
T1,
- [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicMethods)]
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)]
T2,
- [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicMethods)]
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)]
T3,
- [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicMethods)]
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)]
T4> : AsyncDataSourceGeneratorAttribute
{
protected abstract IEnumerable> GenerateDataSources(DataGeneratorMetadata dataGeneratorMetadata);
@@ -88,15 +88,15 @@ public abstract class DataSourceGeneratorAttribute<
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = true)]
public abstract class DataSourceGeneratorAttribute<
- [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicMethods)]
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)]
T1,
- [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicMethods)]
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)]
T2,
- [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicMethods)]
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)]
T3,
- [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicMethods)]
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)]
T4,
- [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicMethods)]
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)]
T5> : AsyncDataSourceGeneratorAttribute
{
protected abstract IEnumerable> GenerateDataSources(DataGeneratorMetadata dataGeneratorMetadata);
diff --git a/TUnit.Core/Attributes/TestData/DependencyInjectionDataSourceSourceAttribute.cs b/TUnit.Core/Attributes/TestData/DependencyInjectionDataSourceSourceAttribute.cs
index 817035eba9..3d7ea2468b 100644
--- a/TUnit.Core/Attributes/TestData/DependencyInjectionDataSourceSourceAttribute.cs
+++ b/TUnit.Core/Attributes/TestData/DependencyInjectionDataSourceSourceAttribute.cs
@@ -2,10 +2,6 @@
namespace TUnit.Core;
-#if NET6_0_OR_GREATER
-[RequiresDynamicCode("DependencyInjectionDataSourceAttribute requires dynamic code generation for dependency injection container access. This attribute is inherently incompatible with AOT compilation.")]
-[RequiresUnreferencedCode("DependencyInjectionDataSourceAttribute may require unreferenced code for dependency injection container access. This attribute is inherently incompatible with AOT compilation.")]
-#endif
public abstract class DependencyInjectionDataSourceAttribute : UntypedDataSourceGeneratorAttribute
{
protected override IEnumerable> GenerateDataSources(DataGeneratorMetadata dataGeneratorMetadata)
@@ -16,23 +12,27 @@ public abstract class DependencyInjectionDataSourceAttribute : UntypedDa
var scope = CreateScope(dataGeneratorMetadata);
// Set up disposal for this specific scope in the current test context
- if (dataGeneratorMetadata.TestBuilderContext != null)
+ dataGeneratorMetadata.TestBuilderContext.Current.Events.OnDispose += async (_, _) =>
{
- dataGeneratorMetadata.TestBuilderContext.Current.Events.OnDispose += async (_, _) =>
+ if (scope is IAsyncDisposable asyncDisposable)
{
- if (scope is IAsyncDisposable asyncDisposable)
- {
- await asyncDisposable.DisposeAsync().ConfigureAwait(false);
- }
- else if (scope is IDisposable disposable)
- {
- disposable.Dispose();
- }
- };
- }
+ await asyncDisposable.DisposeAsync().ConfigureAwait(false);
+ }
+ else if (scope is IDisposable disposable)
+ {
+ disposable.Dispose();
+ }
+ };
return dataGeneratorMetadata.MembersToGenerate
- .Select(m => m.Type)
+ .Select(m => m switch
+ {
+ PropertyMetadata prop => prop.Type,
+ ParameterMetadata param => param.Type,
+ ClassMetadata cls => cls.Type,
+ MethodMetadata method => method.Type,
+ _ => throw new InvalidOperationException($"Unknown member type: {m.GetType()}")
+ })
.Select(x => Create(scope, x))
.ToArray();
};
diff --git a/TUnit.Core/Attributes/TestData/MatrixDataSourceAttribute.cs b/TUnit.Core/Attributes/TestData/MatrixDataSourceAttribute.cs
index ef6a40bdc8..1d975a8dbc 100644
--- a/TUnit.Core/Attributes/TestData/MatrixDataSourceAttribute.cs
+++ b/TUnit.Core/Attributes/TestData/MatrixDataSourceAttribute.cs
@@ -5,16 +5,8 @@
namespace TUnit.Core;
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
-#if NET6_0_OR_GREATER
-[RequiresUnreferencedCode("MatrixDataSource uses reflection to access parameter attributes and test metadata. For AOT compatibility, consider using explicit data sources.")]
-[RequiresDynamicCode("MatrixDataSource may process enum types dynamically")]
-#endif
public sealed class MatrixDataSourceAttribute : UntypedDataSourceGeneratorAttribute, IAccessesInstanceData
{
- #if NET6_0_OR_GREATER
- [RequiresUnreferencedCode("Matrix generation requires reflection")]
- [RequiresDynamicCode("Matrix generation may process enum types dynamically")]
- #endif
protected override IEnumerable> GenerateDataSources(DataGeneratorMetadata dataGeneratorMetadata)
{
var parameterInformation = dataGeneratorMetadata
@@ -95,9 +87,6 @@ private bool IsExcluded(object?[] exclusion, IEnumerable
#if NET6_0_OR_GREATER
[RequiresUnreferencedCode("Hook discovery uses reflection to scan assemblies and types")]
-[RequiresDynamicCode("Hook delegate creation requires dynamic code generation")]
#endif
internal sealed class ReflectionBasedHookDiscoveryService : IHookDiscoveryService
{
diff --git a/TUnit.Engine/Discovery/ReflectionGenericTypeResolver.cs b/TUnit.Engine/Discovery/ReflectionGenericTypeResolver.cs
index 884d87605e..d6ca2d7236 100644
--- a/TUnit.Engine/Discovery/ReflectionGenericTypeResolver.cs
+++ b/TUnit.Engine/Discovery/ReflectionGenericTypeResolver.cs
@@ -9,14 +9,13 @@ namespace TUnit.Engine.Discovery;
///
/// Handles generic type resolution and instantiation for reflection-based test discovery
///
+[RequiresUnreferencedCode("Uses reflection to analyze and instantiate generic types")]
+[RequiresDynamicCode("Uses reflection to analyze and instantiate generic types")]
internal static class ReflectionGenericTypeResolver
{
///
/// Determines generic type arguments from data row values
///
-#if NET6_0_OR_GREATER
- [RequiresUnreferencedCode("Generic type argument determination uses reflection")]
-#endif
public static Type[]? DetermineGenericTypeArguments(Type genericTypeDefinition, object?[] dataRow)
{
#if NET
@@ -79,9 +78,6 @@ internal static class ReflectionGenericTypeResolver
///
/// Extracts generic type information including constraints
///
-#if NET6_0_OR_GREATER
- [RequiresUnreferencedCode("Generic type info extraction uses reflection on type parameters")]
-#endif
public static GenericTypeInfo? ExtractGenericTypeInfo(Type testClass)
{
// Handle both generic type definitions and constructed generic types
@@ -129,9 +125,6 @@ internal static class ReflectionGenericTypeResolver
///
/// Extracts generic method information including parameter positions
///
-#if NET6_0_OR_GREATER
- [RequiresUnreferencedCode("Generic method info extraction uses reflection on method parameters")]
-#endif
public static GenericMethodInfo? ExtractGenericMethodInfo(MethodInfo method)
{
if (!method.IsGenericMethodDefinition)
@@ -180,10 +173,6 @@ internal static class ReflectionGenericTypeResolver
///
/// Creates a concrete type from a generic type definition and validates the type arguments
///
-#if NET6_0_OR_GREATER
- [RequiresUnreferencedCode("Concrete type creation uses Type.GetGenericArguments and reflection")]
- [RequiresDynamicCode("Type construction uses MakeGenericType")]
-#endif
public static Type CreateConcreteType(Type genericTypeDefinition, Type[] typeArguments)
{
var genericParams = genericTypeDefinition.GetGenericArguments();
diff --git a/TUnit.Engine/Discovery/ReflectionHookDiscoveryService.cs b/TUnit.Engine/Discovery/ReflectionHookDiscoveryService.cs
index 7501cead8e..2048aa8187 100644
--- a/TUnit.Engine/Discovery/ReflectionHookDiscoveryService.cs
+++ b/TUnit.Engine/Discovery/ReflectionHookDiscoveryService.cs
@@ -15,7 +15,6 @@ namespace TUnit.Engine.Discovery;
///
#if NET6_0_OR_GREATER
[RequiresUnreferencedCode("Uses reflection to access nested members")]
-[RequiresDynamicCode("Uses reflection to access nested members")]
#endif
internal sealed class ReflectionHookDiscoveryService
{
@@ -150,7 +149,6 @@ public static void DiscoverInstanceHooksForType(Type closedGenericType)
#if NET6_0_OR_GREATER
[RequiresUnreferencedCode("Hook discovery scans assemblies and types using reflection")]
- [RequiresDynamicCode("Hook delegate creation may require dynamic code generation")]
#endif
public static void DiscoverHooks()
{
diff --git a/TUnit.Engine/Discovery/ReflectionTestDataCollector.cs b/TUnit.Engine/Discovery/ReflectionTestDataCollector.cs
index dd76cb83f8..dce151c07d 100644
--- a/TUnit.Engine/Discovery/ReflectionTestDataCollector.cs
+++ b/TUnit.Engine/Discovery/ReflectionTestDataCollector.cs
@@ -15,11 +15,16 @@ namespace TUnit.Engine.Discovery;
/// Discovers tests at runtime using reflection with assembly scanning and caching
[UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Reflection mode isn't used in AOT scenarios")]
[UnconditionalSuppressMessage("Trimming", "IL2062", Justification = "Reflection mode isn't used in AOT scenarios")]
+[UnconditionalSuppressMessage("Trimming", "IL2065", Justification = "Reflection mode isn't used in AOT scenarios")]
+[UnconditionalSuppressMessage("Trimming", "IL2067:Target parameter argument does not satisfy \'DynamicallyAccessedMembersAttribute\' in call to target method. The parameter of method does not have matching annotations.")]
[UnconditionalSuppressMessage("Trimming", "IL2070", Justification = "Reflection mode isn't used in AOT scenarios")]
[UnconditionalSuppressMessage("Trimming", "IL2072", Justification = "Reflection mode isn't used in AOT scenarios")]
[UnconditionalSuppressMessage("Trimming", "IL2075", Justification = "Reflection mode isn't used in AOT scenarios")]
+[UnconditionalSuppressMessage("Trimming", "IL2111:Method with parameters or return value with `DynamicallyAccessedMembersAttribute` is accessed via reflection. Trimmer can\'t guarantee availability of the requirements of the method.")]
[UnconditionalSuppressMessage("AOT", "IL3000", Justification = "Reflection mode isn't used in AOT scenarios")]
[UnconditionalSuppressMessage("AOT", "IL3050", Justification = "Reflection mode isn't used in AOT scenarios")]
+[UnconditionalSuppressMessage("Trimming", "IL2055:Either the type on which the MakeGenericType is called can\'t be statically determined, or the type parameters to be used for generic arguments can\'t be statically determined.")]
+[UnconditionalSuppressMessage("Trimming", "IL2060:Call to \'System.Reflection.MethodInfo.MakeGenericMethod\' can not be statically analyzed. It\'s not possible to guarantee the availability of requirements of the generic method.")]
internal sealed class ReflectionTestDataCollector : ITestDataCollector
{
private static readonly ConcurrentDictionary _scannedAssemblies = new();
@@ -54,10 +59,6 @@ public static void ClearCaches()
}
}
- #if NET6_0_OR_GREATER
- [RequiresUnreferencedCode("Assembly scanning uses dynamic type discovery and reflection")]
- [RequiresDynamicCode("Generic test instantiation requires MakeGenericType")]
- #endif
private async Task> ProcessAssemblyAsync(Assembly assembly, SemaphoreSlim semaphore)
{
await semaphore.WaitAsync().ConfigureAwait(false);
@@ -85,10 +86,6 @@ private async Task> ProcessAssemblyAsync(Assembly assembly, S
}
}
- #if NET6_0_OR_GREATER
- [RequiresUnreferencedCode("Assembly scanning uses dynamic type discovery and reflection")]
- [RequiresDynamicCode("Generic test instantiation requires MakeGenericType")]
- #endif
public async Task> CollectTestsAsync(string testSessionId)
{
#if NET
@@ -144,10 +141,6 @@ public async Task> CollectTestsAsync(string testSessio
}
}
- #if NET6_0_OR_GREATER
- [RequiresUnreferencedCode("Reflection-based test discovery requires dynamic access to types, methods, and attributes")]
- [RequiresDynamicCode("Test discovery uses MakeGenericType and dynamic code generation")]
- #endif
public async IAsyncEnumerable CollectTestsStreamingAsync(
string testSessionId,
[EnumeratorCancellation] CancellationToken cancellationToken = default)
@@ -340,10 +333,6 @@ private static bool ShouldScanAssembly(Assembly assembly)
return true;
}
- #if NET6_0_OR_GREATER
- [RequiresUnreferencedCode("Assembly scanning uses dynamic type discovery and reflection")]
- [RequiresDynamicCode("Generic test instantiation requires MakeGenericType")]
- #endif
private static async Task> DiscoverTestsInAssembly(Assembly assembly)
{
var discoveredTests = new List(100);
@@ -442,10 +431,6 @@ private static async Task> DiscoverTestsInAssembly(Assembly a
return discoveredTests;
}
- #if NET6_0_OR_GREATER
- [RequiresUnreferencedCode("Assembly scanning uses dynamic type discovery and reflection")]
- [RequiresDynamicCode("Generic test instantiation requires MakeGenericType")]
- #endif
private static async IAsyncEnumerable DiscoverTestsInAssemblyStreamingAsync(
Assembly assembly,
[EnumeratorCancellation] CancellationToken cancellationToken = default)
@@ -585,10 +570,6 @@ private static async IAsyncEnumerable DiscoverTestsInAssemblyStrea
}
}
- #if NET6_0_OR_GREATER
- [RequiresUnreferencedCode("Generic type resolution requires reflection and dynamic type creation")]
- [RequiresDynamicCode("Generic type instantiation uses MakeGenericType")]
- #endif
private static async Task> DiscoverGenericTests(Type genericTypeDefinition)
{
var discoveredTests = new List(100);
@@ -678,10 +659,6 @@ private static async Task> DiscoverGenericTests(Type genericT
return discoveredTests;
}
- #if NET6_0_OR_GREATER
- [RequiresUnreferencedCode("Generic type resolution requires reflection and dynamic type creation")]
- [RequiresDynamicCode("Generic type instantiation uses MakeGenericType")]
- #endif
private static async IAsyncEnumerable DiscoverGenericTestsStreamingAsync(
Type genericTypeDefinition,
[EnumeratorCancellation] CancellationToken cancellationToken = default)
@@ -866,10 +843,6 @@ private static int CalculateInheritanceDepth(Type testClass, MethodInfo testMeth
return depth;
}
- #if NET6_0_OR_GREATER
- [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "BuildTestMetadata calls other reflection methods that are already annotated")]
- [UnconditionalSuppressMessage("AOT", "IL3050", Justification = "BuildTestMetadata calls CreateInstanceFactory and CreateTestInvoker which are properly annotated")]
- #endif
private static Task BuildTestMetadata(
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)]
Type testClass,
@@ -960,12 +933,6 @@ private static string GenerateTestName(Type testClass, MethodInfo testMethod)
return testMethod.Name;
}
-
-
- #if NET6_0_OR_GREATER
- [RequiresUnreferencedCode("Instance creation uses reflection and Activator.CreateInstance")]
- [RequiresDynamicCode("Generic type instantiation uses MakeGenericType and Activator")]
- #endif
private static Func CreateInstanceFactory([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type testClass)
{
// For generic types, we need to handle MakeGenericType
@@ -1024,27 +991,11 @@ private static string GenerateTestName(Type testClass, MethodInfo testMethod)
return (_, args) => factory(args);
}
-
- #if NET6_0_OR_GREATER
- [RequiresUnreferencedCode("Test invocation uses reflection and MethodInfo.Invoke")]
- [RequiresDynamicCode("Generic method instantiation uses MakeGenericMethod")]
- #endif
private static Func CreateTestInvoker(Type testClass, MethodInfo testMethod)
{
return CreateReflectionTestInvoker(testClass, testMethod);
}
-
- // Hook discovery has been separated into ReflectionHookDiscoveryService
-
- private static bool IsAsyncMethod(MethodInfo method)
- {
- return method.ReturnType == typeof(Task) ||
- method.ReturnType == typeof(ValueTask) ||
- (method.ReturnType.IsGenericType &&
- method.ReturnType.GetGenericTypeDefinition() == typeof(Task<>));
- }
-
private static bool IsCompilerGenerated([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] Type type)
{
// If the type is not marked as compiler-generated, it's not compiler-generated
@@ -1079,22 +1030,6 @@ private static bool HasTestMethods([DynamicallyAccessedMembers(DynamicallyAccess
}
}
- private static ParameterInfo[] GetParametersWithoutCancellationToken(MethodInfo method)
- {
- var parameters = method.GetParameters();
-
- // Check if last parameter is CancellationToken and exclude it
- if (parameters.Length > 0 && parameters[^1].ParameterType == typeof(CancellationToken))
- {
- // Optimize: Manual array copy instead of LINQ Take().ToArray()
- var result = new ParameterInfo[parameters.Length - 1];
- Array.Copy(parameters, result, parameters.Length - 1);
- return result;
- }
-
- return parameters;
- }
-
private static string? ExtractFilePath(MethodInfo method)
{
return method.GetCustomAttribute()?.File;
@@ -1105,10 +1040,6 @@ private static ParameterInfo[] GetParametersWithoutCancellationToken(MethodInfo
return method.GetCustomAttribute()?.Line;
}
- #if NET6_0_OR_GREATER
- [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "This is only called in error cases for assembly scanning failures")]
- [UnconditionalSuppressMessage("Trimming", "IL2111", Justification = "Methods are called directly, not via reflection")]
- #endif
private static TestMetadata CreateFailedTestMetadataForAssembly(Assembly assembly, Exception ex)
{
var testName = $"[ASSEMBLY SCAN FAILED] {assembly.GetName().Name}";
@@ -1140,9 +1071,6 @@ private static TestMetadata CreateFailedTestMetadataForAssembly(Assembly assembl
};
}
- #if NET6_0_OR_GREATER
- [RequiresUnreferencedCode("Failed test metadata creation uses ReflectionMetadataBuilder")]
- #endif
private static TestMetadata CreateFailedTestMetadata(
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods | DynamicallyAccessedMemberTypes.PublicProperties)]
Type type,
@@ -1241,10 +1169,6 @@ public override Func
/// Creates a reflection-based instance factory with proper AOT attribution
///
- #if NET6_0_OR_GREATER
- [RequiresUnreferencedCode("Reflection-based factory uses ConstructorInfo.Invoke")]
- [RequiresDynamicCode("Dynamic constructor invocation may require runtime code generation")]
- #endif
private static Func CreateReflectionInstanceFactory(ConstructorInfo ctor)
{
var isPrepared = false;
@@ -1443,9 +1367,6 @@ private static bool IsCovariantCompatible(Type paramType, [DynamicallyAccessedMe
///
/// Creates a reflection-based test invoker with proper AOT attribution
///
- #if NET6_0_OR_GREATER
- [UnconditionalSuppressMessage("Trimming", "IL2060", Justification = "Reflection-based test invoker is only used in reflection mode, not in AOT")]
- #endif
private static Func CreateReflectionTestInvoker(Type testClass, MethodInfo testMethod)
{
var isPrepared = false;
@@ -1694,10 +1615,6 @@ private static bool IsCovariantCompatible(Type paramType, [DynamicallyAccessedMe
};
}
- #if NET6_0_OR_GREATER
- [RequiresUnreferencedCode("Dynamic test discovery uses reflection to scan assemblies and types")]
- [RequiresDynamicCode("Dynamic test builders may use expression compilation")]
- #endif
private async Task> DiscoverDynamicTests(string testSessionId)
{
var dynamicTests = new List(50);
@@ -1787,10 +1704,6 @@ private async Task> DiscoverDynamicTests(string testSessionId
return dynamicTests;
}
- #if NET6_0_OR_GREATER
- [RequiresUnreferencedCode("Dynamic test discovery uses reflection to scan assemblies and types")]
- [RequiresDynamicCode("Dynamic test builders may use expression compilation")]
- #endif
private async IAsyncEnumerable DiscoverDynamicTestsStreamingAsync(
string testSessionId,
[EnumeratorCancellation] CancellationToken cancellationToken = default)
@@ -1852,10 +1765,6 @@ private async IAsyncEnumerable DiscoverDynamicTestsStreamingAsync(
}
}
- #if NET6_0_OR_GREATER
- [RequiresUnreferencedCode("Dynamic test builder execution uses Activator.CreateInstance and MethodInfo.Invoke")]
- [RequiresDynamicCode("Expression compilation is used for dynamic tests")]
- #endif
private async Task> ExecuteDynamicTestBuilder(Type testClass, MethodInfo builderMethod, string testSessionId)
{
var dynamicTests = new List(50);
@@ -1887,10 +1796,6 @@ private async Task> ExecuteDynamicTestBuilder(Type testClass,
return dynamicTests;
}
- #if NET6_0_OR_GREATER
- [RequiresUnreferencedCode("Dynamic test metadata creation uses reflection")]
- [RequiresDynamicCode("Expression compilation is used for dynamic test invocation")]
- #endif
private async Task> ConvertDynamicTestToMetadata(AbstractDynamicTest abstractDynamicTest)
{
var testMetadataList = new List();
@@ -1907,10 +1812,6 @@ private async Task> ConvertDynamicTestToMetadata(AbstractDyna
return testMetadataList;
}
- #if NET6_0_OR_GREATER
- [RequiresUnreferencedCode("Dynamic test builder execution uses Activator.CreateInstance and MethodInfo.Invoke")]
- [RequiresDynamicCode("Expression compilation is used for dynamic tests")]
- #endif
private async IAsyncEnumerable ExecuteDynamicTestBuilderStreamingAsync(
Type testClass, MethodInfo builderMethod, string testSessionId,
[EnumeratorCancellation] CancellationToken cancellationToken = default)
@@ -1992,10 +1893,6 @@ private async IAsyncEnumerable ExecuteDynamicTestBuilderStreamingA
}
}
- #if NET6_0_OR_GREATER
- [RequiresUnreferencedCode("Dynamic test metadata creation uses reflection")]
- [RequiresDynamicCode("Expression compilation is used for dynamic test invocation")]
- #endif
private Task CreateMetadataFromDynamicDiscoveryResult(DynamicDiscoveryResult result)
{
if (result.TestClassType == null || result.TestMethod == null)
@@ -2046,10 +1943,6 @@ private Task CreateMetadataFromDynamicDiscoveryResult(DynamicDisco
return Task.FromResult(metadata);
}
- #if NET6_0_OR_GREATER
- [RequiresUnreferencedCode("Dynamic instance factory uses Activator.CreateInstance")]
- [RequiresDynamicCode("Generic type construction uses MakeGenericType")]
- #endif
private static Func CreateDynamicInstanceFactory(Type testClass, object?[]? predefinedClassArgs)
{
// For dynamic tests, we always use the predefined args (or empty array if null)
@@ -2076,10 +1969,6 @@ private Task CreateMetadataFromDynamicDiscoveryResult(DynamicDisco
};
}
- #if NET6_0_OR_GREATER
- [RequiresUnreferencedCode("Dynamic test invoker uses expression compilation and reflection")]
- [RequiresDynamicCode("LambdaExpression.Compile requires dynamic code generation")]
- #endif
private static Func CreateDynamicTestInvoker(DynamicDiscoveryResult result)
{
return async (instance, args) =>
@@ -2135,10 +2024,6 @@ private Task CreateMetadataFromDynamicDiscoveryResult(DynamicDisco
};
}
- #if NET6_0_OR_GREATER
- [UnconditionalSuppressMessage("Trimming", "IL2077", Justification = "This is only called in error cases for dynamic source failures")]
- [UnconditionalSuppressMessage("Trimming", "IL2072", Justification = "Object.GetType() doesn't preserve annotations, but this is for error reporting only")]
- #endif
private static TestMetadata CreateFailedTestMetadataForDynamicSource(IDynamicTestSource source, Exception ex)
{
var testName = $"[DYNAMIC SOURCE FAILED] {source.GetType().Name}";
@@ -2159,9 +2044,6 @@ private static TestMetadata CreateFailedTestMetadataForDynamicSource(IDynamicTes
};
}
- #if NET6_0_OR_GREATER
- [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "This is only called in error cases for dynamic builder failures")]
- #endif
private static TestMetadata CreateFailedTestMetadataForDynamicBuilder(
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods | DynamicallyAccessedMemberTypes.PublicProperties)]
Type type,
diff --git a/TUnit.Engine/Framework/IRequestHandler.cs b/TUnit.Engine/Framework/IRequestHandler.cs
index 4704b0040a..ff653f3168 100644
--- a/TUnit.Engine/Framework/IRequestHandler.cs
+++ b/TUnit.Engine/Framework/IRequestHandler.cs
@@ -8,8 +8,5 @@ namespace TUnit.Engine.Framework;
///
internal interface IRequestHandler
{
- #if NET6_0_OR_GREATER
- [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Generic test instantiation requires MakeGenericType")]
- #endif
Task HandleRequestAsync(TestExecutionRequest request, TUnitServiceProvider serviceProvider, ExecuteRequestContext context, ITestExecutionFilter? testExecutionFilter);
}
diff --git a/TUnit.Engine/Framework/TUnitTestFramework.cs b/TUnit.Engine/Framework/TUnitTestFramework.cs
index 95f02e927c..9c68fcc49c 100644
--- a/TUnit.Engine/Framework/TUnitTestFramework.cs
+++ b/TUnit.Engine/Framework/TUnitTestFramework.cs
@@ -44,8 +44,6 @@ public Task CreateTestSessionAsync(CreateTestSessionCon
}
#if NET6_0_OR_GREATER
- [RequiresUnreferencedCode("Test data collector selection may use reflection-based discovery")]
- [RequiresDynamicCode("Reflection mode test discovery uses dynamic code generation")]
[UnconditionalSuppressMessage("Trimming", "IL2046", Justification = "Reflection mode is not used in AOT/trimmed scenarios")]
[UnconditionalSuppressMessage("AOT", "IL3051", Justification = "Reflection mode is not used in AOT scenarios")]
#endif
@@ -118,10 +116,6 @@ public async Task CloseTestSessionAsync(CloseTestSession
return new CloseTestSessionResult { IsSuccess = isSuccess };
}
- #if NET6_0_OR_GREATER
- [RequiresUnreferencedCode("Test data collector selection may use reflection-based discovery")]
- [RequiresDynamicCode("Reflection mode test discovery uses dynamic code generation")]
- #endif
private TUnitServiceProvider GetOrCreateServiceProvider(ExecuteRequestContext context)
{
return _serviceProvidersPerSession.GetOrAdd(
diff --git a/TUnit.Engine/Framework/TestRequestHandler.cs b/TUnit.Engine/Framework/TestRequestHandler.cs
index 15c75e9434..38e447c779 100644
--- a/TUnit.Engine/Framework/TestRequestHandler.cs
+++ b/TUnit.Engine/Framework/TestRequestHandler.cs
@@ -7,9 +7,6 @@ namespace TUnit.Engine.Framework;
internal sealed class TestRequestHandler : IRequestHandler
{
- #if NET6_0_OR_GREATER
- [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Generic test instantiation requires MakeGenericType")]
- #endif
public async Task HandleRequestAsync(TestExecutionRequest request, TUnitServiceProvider serviceProvider, ExecuteRequestContext context, ITestExecutionFilter? testExecutionFilter)
{
switch (request)
@@ -30,9 +27,6 @@ public async Task HandleRequestAsync(TestExecutionRequest request, TUnitServiceP
}
}
- #if NET6_0_OR_GREATER
- [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Generic test instantiation requires MakeGenericType")]
- #endif
private async Task HandleDiscoveryRequestAsync(
TUnitServiceProvider serviceProvider,
ExecuteRequestContext context,
@@ -57,9 +51,6 @@ private async Task HandleDiscoveryRequestAsync(
}
}
- #if NET6_0_OR_GREATER
- [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Generic test instantiation requires MakeGenericType")]
- #endif
private async Task HandleRunRequestAsync(
TUnitServiceProvider serviceProvider,
RunTestExecutionRequest request,
diff --git a/TUnit.Engine/Interfaces/ITestCoordinator.cs b/TUnit.Engine/Interfaces/ITestCoordinator.cs
index 33200b7dc0..fe91169564 100644
--- a/TUnit.Engine/Interfaces/ITestCoordinator.cs
+++ b/TUnit.Engine/Interfaces/ITestCoordinator.cs
@@ -9,8 +9,5 @@ namespace TUnit.Engine.Interfaces;
///
internal interface ITestCoordinator
{
- #if NET6_0_OR_GREATER
- [RequiresUnreferencedCode("Test execution involves reflection for hooks and initialization")]
- #endif
Task ExecuteTestAsync(AbstractExecutableTest test, CancellationToken cancellationToken);
}
diff --git a/TUnit.Engine/Services/ReflectionStaticPropertyInitializer.cs b/TUnit.Engine/Services/ReflectionStaticPropertyInitializer.cs
index bc652c4bd9..fa1bab8b5b 100644
--- a/TUnit.Engine/Services/ReflectionStaticPropertyInitializer.cs
+++ b/TUnit.Engine/Services/ReflectionStaticPropertyInitializer.cs
@@ -11,7 +11,6 @@ namespace TUnit.Engine.Services;
///
#if NET6_0_OR_GREATER
[RequiresUnreferencedCode("Uses reflection to discover and initialize static properties")]
-[RequiresDynamicCode("Static property initialization may require dynamic code generation")]
#endif
internal sealed class ReflectionStaticPropertyInitializer : IStaticPropertyInitializer
{
diff --git a/TUnit.Engine/Services/TestExecution/TestCoordinator.cs b/TUnit.Engine/Services/TestExecution/TestCoordinator.cs
index 1770eaba14..692bcf8481 100644
--- a/TUnit.Engine/Services/TestExecution/TestCoordinator.cs
+++ b/TUnit.Engine/Services/TestExecution/TestCoordinator.cs
@@ -46,18 +46,12 @@ public TestCoordinator(
_eventReceiverOrchestrator = eventReceiverOrchestrator;
}
- #if NET6_0_OR_GREATER
- [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("Test execution involves reflection for hooks and initialization")]
- #endif
public async Task ExecuteTestAsync(AbstractExecutableTest test, CancellationToken cancellationToken)
{
await _executionGuard.TryStartExecutionAsync(test.TestId,
() => ExecuteTestInternalAsync(test, cancellationToken));
}
- #if NET6_0_OR_GREATER
- [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("Test execution involves reflection for hooks and initialization")]
- #endif
private async Task ExecuteTestInternalAsync(AbstractExecutableTest test, CancellationToken cancellationToken)
{
try
diff --git a/TUnit.Engine/Services/TestRegistry.cs b/TUnit.Engine/Services/TestRegistry.cs
index ed58951ab4..131c9feb58 100644
--- a/TUnit.Engine/Services/TestRegistry.cs
+++ b/TUnit.Engine/Services/TestRegistry.cs
@@ -20,7 +20,6 @@ internal sealed class TestRegistry : ITestRegistry
private readonly CancellationToken _sessionCancellationToken;
private readonly string? _sessionId;
-
public TestRegistry(TestBuilderPipeline testBuilderPipeline,
ITestCoordinator testCoordinator,
string sessionId,
@@ -31,8 +30,8 @@ public TestRegistry(TestBuilderPipeline testBuilderPipeline,
_sessionId = sessionId;
_sessionCancellationToken = sessionCancellationToken;
}
- [RequiresDynamicCode("Adding dynamic tests requires runtime compilation and reflection which are not supported in native AOT scenarios.")]
- [RequiresUnreferencedCode("Dynamic test metadata creation uses reflection")]
+
+ [RequiresUnreferencedCode("Adding dynamic tests requires reflection which is not supported in native AOT scenarios.")]
public async Task AddDynamicTest<[DynamicallyAccessedMembers(
DynamicallyAccessedMemberTypes.PublicConstructors
| DynamicallyAccessedMemberTypes.NonPublicConstructors
@@ -66,10 +65,7 @@ public TestRegistry(TestBuilderPipeline testBuilderPipeline,
await ProcessPendingDynamicTests();
}
-#if NET6_0_OR_GREATER
- [RequiresDynamicCode("Dynamic test processing uses expression compilation")]
- [RequiresUnreferencedCode("Dynamic test metadata creation uses reflection")]
-#endif
+ [RequiresUnreferencedCode("Processing dynamic tests requires reflection which is not supported in native AOT scenarios.")]
private async Task ProcessPendingDynamicTests()
{
var testsToProcess = new List();
@@ -102,8 +98,7 @@ private async Task ProcessPendingDynamicTests()
}
}
- [RequiresDynamicCode("Dynamic tests require runtime compilation of lambda expressions and are not supported in native AOT scenarios.")]
- [RequiresUnreferencedCode("Method metadata creation uses reflection on parameters and types")]
+ [RequiresUnreferencedCode("Dynamic test metadata creation requires reflection which is not supported in native AOT scenarios.")]
private async Task CreateMetadataFromDynamicDiscoveryResult(DynamicDiscoveryResult result)
{
if (result.TestClassType == null || result.TestMethod == null)
@@ -152,7 +147,7 @@ private async Task CreateMetadataFromDynamicDiscoveryResult(Dynami
});
}
- [RequiresDynamicCode("Dynamic test instance creation requires Activator.CreateInstance which is not supported in native AOT scenarios.")]
+ [RequiresUnreferencedCode("Dynamic test instance creation requires Activator.CreateInstance which is not supported in native AOT scenarios.")]
[UnconditionalSuppressMessage("Trimming",
"IL2067:Target parameter argument does not satisfy 'DynamicallyAccessedMembersAttribute' in call",
Justification = "Dynamic tests require reflection")]
@@ -173,7 +168,7 @@ private async Task CreateMetadataFromDynamicDiscoveryResult(Dynami
};
}
- [RequiresDynamicCode("Dynamic test invocation requires LambdaExpression.Compile() which is not supported in native AOT scenarios.")]
+ [RequiresUnreferencedCode("Dynamic test invocation requires LambdaExpression.Compile() which is not supported in native AOT scenarios.")]
private static Func CreateRuntimeTestInvoker(DynamicDiscoveryResult result)
{
return async (instance, args) =>
diff --git a/TUnit.Engine/TestDiscoveryService.cs b/TUnit.Engine/TestDiscoveryService.cs
index aee72fda4a..0d11915400 100644
--- a/TUnit.Engine/TestDiscoveryService.cs
+++ b/TUnit.Engine/TestDiscoveryService.cs
@@ -48,9 +48,6 @@ public TestDiscoveryService(TestExecutor testExecutor, TestBuilderPipeline testB
_testFilterService = testFilterService;
}
- #if NET6_0_OR_GREATER
- [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Generic test instantiation requires MakeGenericType")]
- #endif
public async Task DiscoverTests(string testSessionId, ITestExecutionFilter? filter, CancellationToken cancellationToken, bool isForExecution)
{
await _testExecutor.ExecuteBeforeTestDiscoveryHooksAsync(cancellationToken).ConfigureAwait(false);
@@ -163,7 +160,7 @@ private async IAsyncEnumerable DiscoverTestsStreamAsync(
/// Simplified streaming test discovery without channels - matches source generation approach
///
#if NET6_0_OR_GREATER
- [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Generic test instantiation requires MakeGenericType")]
+ [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("Generic test instantiation requires MakeGenericType")]
#endif
public async IAsyncEnumerable DiscoverTestsFullyStreamingAsync(
string testSessionId,
diff --git a/TUnit.Engine/TestExecutor.cs b/TUnit.Engine/TestExecutor.cs
index 24b9521b8e..cba434941f 100644
--- a/TUnit.Engine/TestExecutor.cs
+++ b/TUnit.Engine/TestExecutor.cs
@@ -242,36 +242,15 @@ public IContextProvider GetContextProvider()
return _contextProvider;
}
-#if NET6_0_OR_GREATER
- [RequiresUnreferencedCode("Test disposal may use reflection on properties")]
-#endif
internal static async Task DisposeTestInstance(AbstractExecutableTest test)
{
// Dispose the test instance if it's disposable
- if (test.Context.TestDetails.ClassInstance != null && test.Context.TestDetails.ClassInstance is not SkippedTestInstance)
+ if (test.Context.TestDetails.ClassInstance is not SkippedTestInstance)
{
try
{
var instance = test.Context.TestDetails.ClassInstance;
- // Special handling for DisposalRegressionTests - dispose its properties
- if (instance.GetType().Name == "DisposalRegressionTests")
- {
- var injectedDataProperty = instance.GetType().GetProperty("InjectedData");
- if (injectedDataProperty != null)
- {
- var injectedData = injectedDataProperty.GetValue(instance);
- if (injectedData is IAsyncDisposable asyncDisposable)
- {
- await asyncDisposable.DisposeAsync().ConfigureAwait(false);
- }
- else if (injectedData is IDisposable disposable)
- {
- disposable.Dispose();
- }
- }
- }
-
switch (instance)
{
case IAsyncDisposable asyncDisposable:
diff --git a/TUnit.Engine/TestInitializer.cs b/TUnit.Engine/TestInitializer.cs
index 92e92f4c16..09e5859366 100644
--- a/TUnit.Engine/TestInitializer.cs
+++ b/TUnit.Engine/TestInitializer.cs
@@ -20,9 +20,6 @@ public TestInitializer(EventReceiverOrchestrator eventReceiverOrchestrator,
_objectTracker = objectTracker;
}
- #if NET6_0_OR_GREATER
- [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("Object tracking may use reflection on properties")]
- #endif
public async Task InitializeTest(AbstractExecutableTest test, CancellationToken cancellationToken)
{
var testClassInstance = test.Context.TestDetails.ClassInstance;
diff --git a/TUnit.Pipeline/Modules/PublishAOTModule.cs b/TUnit.Pipeline/Modules/PublishAOTModule.cs
deleted file mode 100644
index e9f4ade9bd..0000000000
--- a/TUnit.Pipeline/Modules/PublishAOTModule.cs
+++ /dev/null
@@ -1,54 +0,0 @@
-using System.Runtime.InteropServices;
-using ModularPipelines.Context;
-using ModularPipelines.DotNet.Extensions;
-using ModularPipelines.DotNet.Options;
-using ModularPipelines.Enums;
-using ModularPipelines.Extensions;
-using ModularPipelines.Git.Extensions;
-using ModularPipelines.Models;
-using ModularPipelines.Modules;
-
-namespace TUnit.Pipeline.Modules;
-
-public class PublishAOTModule : Module
-{
- protected override Task ShouldSkip(IPipelineContext context)
- {
- return Task.FromResult(EnvironmentVariables.IsNetFramework);
- }
-
- protected override async Task ExecuteAsync(IPipelineContext context, CancellationToken cancellationToken)
- {
- var testProject = context.Git().RootDirectory.FindFile(x => x.Name == "TUnit.TestProject.csproj").AssertExists();
-
- return await context.DotNet().Publish(new DotNetPublishOptions(testProject)
- {
- RuntimeIdentifier = GetRuntimeIdentifier(),
- Configuration = Configuration.Release,
- OutputDirectory = "TESTPROJECT_AOT",
- Properties = [new KeyValue("Aot", "true")],
- Framework = "net8.0",
- CommandLogging = CommandLogging.Input | CommandLogging.Error | CommandLogging.Duration | CommandLogging.ExitCode
- }, cancellationToken);
- }
-
- private string GetRuntimeIdentifier()
- {
- if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
- {
- return "linux-x64";
- }
-
- if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
- {
- return "win-x64";
- }
-
- if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
- {
- return "osx-arm64";
- }
-
- throw new ArgumentException("Unknown platform");
- }
-}
diff --git a/TUnit.Pipeline/Modules/PublishSingleFileModule.cs b/TUnit.Pipeline/Modules/PublishSingleFileModule.cs
deleted file mode 100644
index 459fb6a835..0000000000
--- a/TUnit.Pipeline/Modules/PublishSingleFileModule.cs
+++ /dev/null
@@ -1,56 +0,0 @@
-using System.Runtime.InteropServices;
-using ModularPipelines.Attributes;
-using ModularPipelines.Context;
-using ModularPipelines.DotNet.Extensions;
-using ModularPipelines.DotNet.Options;
-using ModularPipelines.Enums;
-using ModularPipelines.Extensions;
-using ModularPipelines.Git.Extensions;
-using ModularPipelines.Models;
-using ModularPipelines.Modules;
-
-namespace TUnit.Pipeline.Modules;
-
-[DependsOn]
-public class PublishSingleFileModule : Module
-{
- protected override Task ShouldSkip(IPipelineContext context)
- {
- return Task.FromResult(EnvironmentVariables.IsNetFramework);
- }
-
- protected override async Task ExecuteAsync(IPipelineContext context, CancellationToken cancellationToken)
- {
- var testProject = context.Git().RootDirectory.FindFile(x => x.Name == "TUnit.TestProject.csproj").AssertExists();
-
- return await context.DotNet().Publish(new DotNetPublishOptions(testProject)
- {
- RuntimeIdentifier = GetRuntimeIdentifier(),
- Configuration = Configuration.Release,
- OutputDirectory = "TESTPROJECT_SINGLEFILE",
- Properties = [new KeyValue("SingleFile", "true")],
- Framework = "net8.0",
- CommandLogging = CommandLogging.Input | CommandLogging.Error | CommandLogging.Duration | CommandLogging.ExitCode
- }, cancellationToken);
- }
-
- private string GetRuntimeIdentifier()
- {
- if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
- {
- return "linux-x64";
- }
-
- if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
- {
- return "win-x64";
- }
-
- if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
- {
- return "osx-arm64";
- }
-
- throw new ArgumentException("Unknown platform");
- }
-}
diff --git a/TUnit.Pipeline/Modules/RunEngineTestsModule.cs b/TUnit.Pipeline/Modules/RunEngineTestsModule.cs
index 44d482de3d..b21eb0f597 100644
--- a/TUnit.Pipeline/Modules/RunEngineTestsModule.cs
+++ b/TUnit.Pipeline/Modules/RunEngineTestsModule.cs
@@ -11,8 +11,6 @@
namespace TUnit.Pipeline.Modules;
[NotInParallel("DotNetTests")]
-[DependsOn]
-[DependsOn]
[DependsOn]
[DependsOn]
[DependsOn]
diff --git a/TUnit.PublicAPI/Tests.Assertions_Library_Has_No_API_Changes.DotNet10_0.verified.txt b/TUnit.PublicAPI/Tests.Assertions_Library_Has_No_API_Changes.DotNet10_0.verified.txt
index 770ced7811..adbd48b92a 100644
--- a/TUnit.PublicAPI/Tests.Assertions_Library_Has_No_API_Changes.DotNet10_0.verified.txt
+++ b/TUnit.PublicAPI/Tests.Assertions_Library_Has_No_API_Changes.DotNet10_0.verified.txt
@@ -1181,6 +1181,7 @@ namespace .Conditions
protected override .<.> CheckAsync(. metadata) { }
protected override string GetExpectation() { }
}
+ [.("Uses reflection for structural equivalency comparison")]
public class NotStructuralEquivalencyAssertion : .
{
public NotStructuralEquivalencyAssertion(. context, object? notExpected, string? notExpectedExpression = null) { }
@@ -1364,6 +1365,7 @@ namespace .Conditions
[.("IsNullOrWhiteSpace", CustomName="IsNotNullOrWhiteSpace", ExpectationMessage="be null, empty, or whitespace", NegateLogic=true)]
[.("IsNullOrWhiteSpace", ExpectationMessage="be null, empty, or whitespace")]
public static class StringStaticMethodAssertions { }
+ [.("Uses reflection to compare object properties and fields.")]
public class StructuralEquivalencyAssertion : .
{
public StructuralEquivalencyAssertion(. context, object? expected, string? expectedExpression = null) { }
@@ -1868,6 +1870,7 @@ namespace .Extensions
public static ..IsDefinedAssertion IsDefined(this . source)
where TEnum : struct, { }
public static . IsEqualTo(this . source, string? expected, comparison, [.("expected")] string? expression = null) { }
+ [.("Uses reflection to compare members")]
public static . IsEquivalentTo(this . source, object? expected, [.("expected")] string? expression = null) { }
public static . IsIn(this . source, params TValue[] collection) { }
public static . IsNegative(this . source)
@@ -1877,6 +1880,7 @@ namespace .Extensions
public static . IsNotAssignableTo(this . source) { }
public static ..IsNotDefinedAssertion IsNotDefined(this . source)
where TEnum : struct, { }
+ [.("Uses reflection to compare members")]
public static . IsNotEquivalentTo(this . source, object? expected, [.("expected")] string? expression = null) { }
public static . IsNotIn(this . source, params TValue[] collection) { }
public static . IsNotNull(this . source)
diff --git a/TUnit.PublicAPI/Tests.Assertions_Library_Has_No_API_Changes.DotNet8_0.verified.txt b/TUnit.PublicAPI/Tests.Assertions_Library_Has_No_API_Changes.DotNet8_0.verified.txt
index 5833d318fa..a2daeaf63b 100644
--- a/TUnit.PublicAPI/Tests.Assertions_Library_Has_No_API_Changes.DotNet8_0.verified.txt
+++ b/TUnit.PublicAPI/Tests.Assertions_Library_Has_No_API_Changes.DotNet8_0.verified.txt
@@ -1178,6 +1178,7 @@ namespace .Conditions
protected override .<.> CheckAsync(. metadata) { }
protected override string GetExpectation() { }
}
+ [.("Uses reflection for structural equivalency comparison")]
public class NotStructuralEquivalencyAssertion : .
{
public NotStructuralEquivalencyAssertion(. context, object? notExpected, string? notExpectedExpression = null) { }
@@ -1361,6 +1362,7 @@ namespace .Conditions
[.("IsNullOrWhiteSpace", CustomName="IsNotNullOrWhiteSpace", ExpectationMessage="be null, empty, or whitespace", NegateLogic=true)]
[.("IsNullOrWhiteSpace", ExpectationMessage="be null, empty, or whitespace")]
public static class StringStaticMethodAssertions { }
+ [.("Uses reflection to compare object properties and fields.")]
public class StructuralEquivalencyAssertion : .
{
public StructuralEquivalencyAssertion(. context, object? expected, string? expectedExpression = null) { }
@@ -1865,6 +1867,7 @@ namespace .Extensions
public static ..IsDefinedAssertion IsDefined(this . source)
where TEnum : struct, { }
public static . IsEqualTo(this . source, string? expected, comparison, [.("expected")] string? expression = null) { }
+ [.("Uses reflection to compare members")]
public static . IsEquivalentTo(this . source, object? expected, [.("expected")] string? expression = null) { }
public static . IsIn(this . source, params TValue[] collection) { }
public static . IsNegative(this . source)
@@ -1874,6 +1877,7 @@ namespace .Extensions
public static . IsNotAssignableTo(this . source) { }
public static ..IsNotDefinedAssertion IsNotDefined(this . source)
where TEnum : struct, { }
+ [.("Uses reflection to compare members")]
public static . IsNotEquivalentTo(this . source, object? expected, [.("expected")] string? expression = null) { }
public static . IsNotIn(this . source, params TValue[] collection) { }
public static . IsNotNull(this . source)
diff --git a/TUnit.PublicAPI/Tests.Assertions_Library_Has_No_API_Changes.DotNet9_0.verified.txt b/TUnit.PublicAPI/Tests.Assertions_Library_Has_No_API_Changes.DotNet9_0.verified.txt
index 99a13e4d9a..249067434e 100644
--- a/TUnit.PublicAPI/Tests.Assertions_Library_Has_No_API_Changes.DotNet9_0.verified.txt
+++ b/TUnit.PublicAPI/Tests.Assertions_Library_Has_No_API_Changes.DotNet9_0.verified.txt
@@ -1181,6 +1181,7 @@ namespace .Conditions
protected override .<.> CheckAsync(. metadata) { }
protected override string GetExpectation() { }
}
+ [.("Uses reflection for structural equivalency comparison")]
public class NotStructuralEquivalencyAssertion : .
{
public NotStructuralEquivalencyAssertion(. context, object? notExpected, string? notExpectedExpression = null) { }
@@ -1364,6 +1365,7 @@ namespace .Conditions
[.("IsNullOrWhiteSpace", CustomName="IsNotNullOrWhiteSpace", ExpectationMessage="be null, empty, or whitespace", NegateLogic=true)]
[.("IsNullOrWhiteSpace", ExpectationMessage="be null, empty, or whitespace")]
public static class StringStaticMethodAssertions { }
+ [.("Uses reflection to compare object properties and fields.")]
public class StructuralEquivalencyAssertion : .
{
public StructuralEquivalencyAssertion(. context, object? expected, string? expectedExpression = null) { }
@@ -1868,6 +1870,7 @@ namespace .Extensions
public static ..IsDefinedAssertion IsDefined(this . source)
where TEnum : struct, { }
public static . IsEqualTo(this . source, string? expected, comparison, [.("expected")] string? expression = null) { }
+ [.("Uses reflection to compare members")]
public static . IsEquivalentTo(this . source, object? expected, [.("expected")] string? expression = null) { }
public static . IsIn(this . source, params TValue[] collection) { }
public static . IsNegative(this . source)
@@ -1877,6 +1880,7 @@ namespace .Extensions
public static . IsNotAssignableTo(this . source) { }
public static ..IsNotDefinedAssertion IsNotDefined(this . source)
where TEnum : struct, { }
+ [.("Uses reflection to compare members")]
public static . IsNotEquivalentTo(this . source, object? expected, [.("expected")] string? expression = null) { }
public static . IsNotIn(this . source, params TValue[] collection) { }
public static . IsNotNull(this . source)
diff --git a/TUnit.PublicAPI/Tests.Core_Library_Has_No_API_Changes.DotNet10_0.verified.txt b/TUnit.PublicAPI/Tests.Core_Library_Has_No_API_Changes.DotNet10_0.verified.txt
index b0597cb32e..ac536d62a6 100644
--- a/TUnit.PublicAPI/Tests.Core_Library_Has_No_API_Changes.DotNet10_0.verified.txt
+++ b/TUnit.PublicAPI/Tests.Core_Library_Has_No_API_Changes.DotNet10_0.verified.txt
@@ -118,19 +118,14 @@ namespace
public static . Convert( action) { }
public static . Convert(<.> action) { }
public static . Convert(<.> action) { }
- [.("ConvertObject may require dynamic invocation for custom awaitable types. For AOT " +
- "compatibility, use Task or ValueTask directly.")]
- [.("ConvertObject uses reflection to handle custom awaitable types and F# async. For " +
- "AOT compatibility, use Task or ValueTask directly.")]
public static . ConvertObject(object? invoke) { }
- [.("Custom awaitable handling may require dynamic invocation. For AOT, use Task/Value" +
- "Task.")]
- [.("GetAwaiter pattern detection requires reflection for custom awaitable types. For " +
- "AOT, use Task/ValueTask.")]
+ [.("Trimming", "IL2075:\'this\' argument does not satisfy \'DynamicallyAccessedMembersAttribute\' in " +
+ "call to target method. The return value of the source method does not have match" +
+ "ing annotations.")]
public static bool TryGetAwaitableTask(object awaitable, [.(true)] out .? task) { }
}
[(.Class | .Method | .Property, AllowMultiple=true)]
- public abstract class AsyncDataSourceGeneratorAttribute<[.(..None | ..PublicParameterlessConstructor | ..PublicConstructors | ..PublicMethods)] T> : .TypedDataSourceAttribute
+ public abstract class AsyncDataSourceGeneratorAttribute<[.(..PublicConstructors)] T> : .TypedDataSourceAttribute
{
protected AsyncDataSourceGeneratorAttribute() { }
protected abstract .<<.>> GenerateDataSourcesAsync(.DataGeneratorMetadata dataGeneratorMetadata);
@@ -138,7 +133,7 @@ namespace
public override sealed .<<.>> GetTypedDataRowsAsync(.DataGeneratorMetadata dataGeneratorMetadata) { }
}
[(.Class | .Method, AllowMultiple=true)]
- public abstract class AsyncDataSourceGeneratorAttribute<[.(..None | ..PublicParameterlessConstructor | ..PublicConstructors | ..PublicMethods)] T1, [.(..None | ..PublicParameterlessConstructor | ..PublicConstructors | ..PublicMethods)] T2> : .TypedDataSourceAttribute<>
+ public abstract class AsyncDataSourceGeneratorAttribute<[.(..PublicConstructors)] T1, [.(..PublicConstructors)] T2> : .TypedDataSourceAttribute<>
{
protected AsyncDataSourceGeneratorAttribute() { }
protected abstract .<<.<>>> GenerateDataSourcesAsync(.DataGeneratorMetadata dataGeneratorMetadata);
@@ -146,7 +141,7 @@ namespace
public override sealed .<<.<>>> GetTypedDataRowsAsync(.DataGeneratorMetadata dataGeneratorMetadata) { }
}
[(.Class | .Method, AllowMultiple=true)]
- public abstract class AsyncDataSourceGeneratorAttribute<[.(..None | ..PublicParameterlessConstructor | ..PublicConstructors | ..PublicMethods)] T1, [.(..None | ..PublicParameterlessConstructor | ..PublicConstructors | ..PublicMethods)] T2, [.(..None | ..PublicParameterlessConstructor | ..PublicConstructors | ..PublicMethods)] T3> : .TypedDataSourceAttribute<>
+ public abstract class AsyncDataSourceGeneratorAttribute<[.(..PublicConstructors)] T1, [.(..PublicConstructors)] T2, [.(..PublicConstructors)] T3> : .TypedDataSourceAttribute<>
{
protected AsyncDataSourceGeneratorAttribute() { }
protected abstract .<<.<>>> GenerateDataSourcesAsync(.DataGeneratorMetadata dataGeneratorMetadata);
@@ -154,7 +149,7 @@ namespace
public override sealed .<<.<>>> GetTypedDataRowsAsync(.DataGeneratorMetadata dataGeneratorMetadata) { }
}
[(.Class | .Method, AllowMultiple=true)]
- public abstract class AsyncDataSourceGeneratorAttribute<[.(..None | ..PublicParameterlessConstructor | ..PublicConstructors | ..PublicMethods)] T1, [.(..None | ..PublicParameterlessConstructor | ..PublicConstructors | ..PublicMethods)] T2, [.(..None | ..PublicParameterlessConstructor | ..PublicConstructors | ..PublicMethods)] T3, [.(..None | ..PublicParameterlessConstructor | ..PublicConstructors | ..PublicMethods)] T4> : .TypedDataSourceAttribute<>
+ public abstract class AsyncDataSourceGeneratorAttribute<[.(..PublicConstructors)] T1, [.(..PublicConstructors)] T2, [.(..PublicConstructors)] T3, [.(..PublicConstructors)] T4> : .TypedDataSourceAttribute<>
{
protected AsyncDataSourceGeneratorAttribute() { }
protected abstract .<<.<>>> GenerateDataSourcesAsync(.DataGeneratorMetadata dataGeneratorMetadata);
@@ -162,7 +157,7 @@ namespace
public override .<<.<>>> GetTypedDataRowsAsync(.DataGeneratorMetadata dataGeneratorMetadata) { }
}
[(.Class | .Method, AllowMultiple=true)]
- public abstract class AsyncDataSourceGeneratorAttribute<[.(..None | ..PublicParameterlessConstructor | ..PublicConstructors | ..PublicMethods)] T1, [.(..None | ..PublicParameterlessConstructor | ..PublicConstructors | ..PublicMethods)] T2, [.(..None | ..PublicParameterlessConstructor | ..PublicConstructors | ..PublicMethods)] T3, [.(..None | ..PublicParameterlessConstructor | ..PublicConstructors | ..PublicMethods)] T4, [.(..None | ..PublicParameterlessConstructor | ..PublicConstructors | ..PublicMethods)] T5> : .TypedDataSourceAttribute<