1- using System . Collections . Concurrent ;
2- using System . Diagnostics . CodeAnalysis ;
1+ using System . Diagnostics . CodeAnalysis ;
32using System . Reflection ;
43using System . Runtime . ExceptionServices ;
54using TUnit . Core . Data ;
6- using TUnit . Core . PropertyInjection ;
75
86namespace TUnit . Core ;
97
@@ -45,11 +43,11 @@ private string GetKey(int index, SharedType[] sharedTypes, string[] keys)
4543 {
4644 return sharedType switch
4745 {
48- SharedType . None => Create < T > ( dataGeneratorMetadata ) ,
49- SharedType . PerTestSession => ( T ) TestDataContainer . GetGlobalInstance ( typeof ( T ) , _ => Create ( typeof ( T ) , dataGeneratorMetadata ) ) ! ,
50- SharedType . PerClass => ( T ) TestDataContainer . GetInstanceForClass ( testClassType , typeof ( T ) , _ => Create ( typeof ( T ) , dataGeneratorMetadata ) ) ! ,
51- SharedType . Keyed => ( T ) TestDataContainer . GetInstanceForKey ( key , typeof ( T ) , _ => Create ( typeof ( T ) , dataGeneratorMetadata ) ) ! ,
52- SharedType . PerAssembly => ( T ) TestDataContainer . GetInstanceForAssembly ( testClassType . Assembly , typeof ( T ) , _ => Create ( typeof ( T ) , dataGeneratorMetadata ) ) ! ,
46+ SharedType . None => Create < T > ( ) ,
47+ SharedType . PerTestSession => ( T ) TestDataContainer . GetGlobalInstance ( typeof ( T ) , _ => Create ( typeof ( T ) ) ) ! ,
48+ SharedType . PerClass => ( T ) TestDataContainer . GetInstanceForClass ( testClassType , typeof ( T ) , _ => Create ( typeof ( T ) ) ) ! ,
49+ SharedType . Keyed => ( T ) TestDataContainer . GetInstanceForKey ( key , typeof ( T ) , _ => Create ( typeof ( T ) ) ) ! ,
50+ SharedType . PerAssembly => ( T ) TestDataContainer . GetInstanceForAssembly ( testClassType . Assembly , typeof ( T ) , _ => Create ( typeof ( T ) ) ) ! ,
5351 _ => throw new ArgumentOutOfRangeException ( )
5452 } ;
5553 }
@@ -58,43 +56,27 @@ private string GetKey(int index, SharedType[] sharedTypes, string[] keys)
5856 {
5957 return sharedType switch
6058 {
61- SharedType . None => Create ( type , dataGeneratorMetadata ) ,
62- SharedType . PerTestSession => TestDataContainer . GetGlobalInstance ( type , _ => Create ( type , dataGeneratorMetadata ) ) ,
63- SharedType . PerClass => TestDataContainer . GetInstanceForClass ( testClassType , type , _ => Create ( type , dataGeneratorMetadata ) ) ,
64- SharedType . Keyed => TestDataContainer . GetInstanceForKey ( key ! , type , _ => Create ( type , dataGeneratorMetadata ) ) ,
65- SharedType . PerAssembly => TestDataContainer . GetInstanceForAssembly ( testClassType . Assembly , type , _ => Create ( type , dataGeneratorMetadata ) ) ,
59+ SharedType . None => Create ( type ) ,
60+ SharedType . PerTestSession => TestDataContainer . GetGlobalInstance ( type , _ => Create ( type ) ) ,
61+ SharedType . PerClass => TestDataContainer . GetInstanceForClass ( testClassType , type , _ => Create ( type ) ) ,
62+ SharedType . Keyed => TestDataContainer . GetInstanceForKey ( key ! , type , _ => Create ( type ) ) ,
63+ SharedType . PerAssembly => TestDataContainer . GetInstanceForAssembly ( testClassType . Assembly , type , _ => Create ( type ) ) ,
6664 _ => throw new ArgumentOutOfRangeException ( )
6765 } ;
6866 }
6967
7068 [ return : NotNull ]
71- private static T Create < [ DynamicallyAccessedMembers ( DynamicallyAccessedMemberTypes . PublicConstructors | DynamicallyAccessedMemberTypes . NonPublicConstructors | DynamicallyAccessedMemberTypes . PublicProperties | DynamicallyAccessedMemberTypes . NonPublicProperties ) ] T > ( DataGeneratorMetadata dataGeneratorMetadata )
69+ private static T Create < [ DynamicallyAccessedMembers ( DynamicallyAccessedMemberTypes . PublicConstructors | DynamicallyAccessedMemberTypes . NonPublicConstructors ) ] T > ( )
7270 {
73- return ( ( T ) Create ( typeof ( T ) , dataGeneratorMetadata ) ) ! ;
71+ return ( ( T ) Create ( typeof ( T ) ) ) ! ;
7472 }
7573
76- private static object Create ( [ DynamicallyAccessedMembers ( DynamicallyAccessedMemberTypes . PublicConstructors | DynamicallyAccessedMemberTypes . NonPublicConstructors | DynamicallyAccessedMemberTypes . PublicProperties | DynamicallyAccessedMemberTypes . NonPublicProperties ) ] Type type , DataGeneratorMetadata dataGeneratorMetadata )
74+ private static object Create ( [ DynamicallyAccessedMembers ( DynamicallyAccessedMemberTypes . PublicConstructors | DynamicallyAccessedMemberTypes . NonPublicConstructors ) ] Type type )
7775 {
78- return Create ( type , dataGeneratorMetadata , recursionDepth : 0 ) ;
79- }
80-
81- private const int MaxRecursionDepth = 10 ;
82-
83- private static object Create ( [ DynamicallyAccessedMembers ( DynamicallyAccessedMemberTypes . PublicConstructors | DynamicallyAccessedMemberTypes . NonPublicConstructors | DynamicallyAccessedMemberTypes . PublicProperties | DynamicallyAccessedMemberTypes . NonPublicProperties ) ] Type type , DataGeneratorMetadata dataGeneratorMetadata , int recursionDepth )
84- {
85- if ( recursionDepth >= MaxRecursionDepth )
86- {
87- throw new InvalidOperationException ( $ "Maximum recursion depth ({ MaxRecursionDepth } ) exceeded when creating nested ClassDataSource dependencies. This may indicate a circular dependency.") ;
88- }
89-
9076 try
9177 {
92- var instance = Activator . CreateInstance ( type ) ! ;
93-
94- // Inject properties into the created instance
95- InjectPropertiesSync ( instance , type , dataGeneratorMetadata , recursionDepth ) ;
96-
97- return instance ;
78+ // Just create the instance - initialization happens in the Engine
79+ return Activator . CreateInstance ( type ) ! ;
9880 }
9981 catch ( TargetInvocationException targetInvocationException )
10082 {
@@ -106,197 +88,4 @@ private static object Create([DynamicallyAccessedMembers(DynamicallyAccessedMemb
10688 throw ;
10789 }
10890 }
109-
110- /// <summary>
111- /// Injects properties into an instance synchronously.
112- /// Used when creating instances via ClassDataSource for nested data source dependencies.
113- /// </summary>
114- [ UnconditionalSuppressMessage ( "Trimming" , "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code" , Justification = "Type is already annotated with DynamicallyAccessedMembers" ) ]
115- [ UnconditionalSuppressMessage ( "AOT" , "IL3050:Calling members annotated with 'RequiresDynamicCodeAttribute' may break functionality when AOT compiling" , Justification = "Fallback to reflection mode when source-gen not available" ) ]
116- private static void InjectPropertiesSync (
117- object instance ,
118- [ DynamicallyAccessedMembers ( DynamicallyAccessedMemberTypes . PublicProperties | DynamicallyAccessedMemberTypes . NonPublicProperties ) ] Type type ,
119- DataGeneratorMetadata dataGeneratorMetadata ,
120- int recursionDepth )
121- {
122- // Get the injection plan for this type
123- var plan = PropertyInjectionPlanBuilder . Build ( type ) ;
124- if ( ! plan . HasProperties )
125- {
126- return ;
127- }
128-
129- // Handle source-generated properties
130- foreach ( var metadata in plan . SourceGeneratedProperties )
131- {
132- var dataSource = metadata . CreateDataSource ( ) ;
133- var propertyMetadata = CreatePropertyMetadata ( type , metadata . PropertyName , metadata . PropertyType ) ;
134-
135- var propertyDataGeneratorMetadata = DataGeneratorMetadataCreator . CreateForPropertyInjection (
136- propertyMetadata ,
137- dataGeneratorMetadata . TestInformation ,
138- dataSource ,
139- testContext : null ,
140- testClassInstance : instance ,
141- events : new TestContextEvents ( ) ,
142- objectBag : new ConcurrentDictionary < string , object ? > ( ) ) ;
143-
144- var value = ResolveDataSourceValueSync ( dataSource , propertyDataGeneratorMetadata , recursionDepth + 1 ) ;
145- if ( value != null )
146- {
147- metadata . SetProperty ( instance , value ) ;
148- }
149- }
150-
151- // Handle reflection-mode properties
152- foreach ( var ( property , dataSource ) in plan . ReflectionProperties )
153- {
154- var propertyMetadata = CreatePropertyMetadataFromPropertyInfo ( property ) ;
155-
156- var propertyDataGeneratorMetadata = DataGeneratorMetadataCreator . CreateForPropertyInjection (
157- propertyMetadata ,
158- dataGeneratorMetadata . TestInformation ,
159- dataSource ,
160- testContext : null ,
161- testClassInstance : instance ,
162- events : new TestContextEvents ( ) ,
163- objectBag : new ConcurrentDictionary < string , object ? > ( ) ) ;
164-
165- var value = ResolveDataSourceValueSync ( dataSource , propertyDataGeneratorMetadata , recursionDepth + 1 ) ;
166- if ( value != null )
167- {
168- SetPropertyValue ( property , instance , value ) ;
169- }
170- }
171- }
172-
173- [ UnconditionalSuppressMessage ( "Trimming" , "IL2067:Target parameter argument does not satisfy 'DynamicallyAccessedMembersAttribute' in call to target method" , Justification = "Type is already annotated in caller" ) ]
174- [ UnconditionalSuppressMessage ( "Trimming" , "IL2070:Target method return value does not satisfy 'DynamicallyAccessedMembersAttribute'" , Justification = "Type is already annotated in caller" ) ]
175- private static PropertyMetadata CreatePropertyMetadata (
176- [ DynamicallyAccessedMembers ( DynamicallyAccessedMemberTypes . PublicProperties ) ] Type containingType ,
177- string propertyName ,
178- [ DynamicallyAccessedMembers ( DynamicallyAccessedMemberTypes . PublicConstructors | DynamicallyAccessedMemberTypes . NonPublicConstructors | DynamicallyAccessedMemberTypes . PublicProperties ) ] Type propertyType )
179- {
180- return new PropertyMetadata
181- {
182- Name = propertyName ,
183- Type = propertyType ,
184- IsStatic = false ,
185- ClassMetadata = GetClassMetadataForType ( containingType ) ,
186- ContainingTypeMetadata = GetClassMetadataForType ( containingType ) ,
187- ReflectionInfo = containingType . GetProperty ( propertyName ) ! ,
188- Getter = parent => containingType . GetProperty ( propertyName ) ? . GetValue ( parent )
189- } ;
190- }
191-
192- [ UnconditionalSuppressMessage ( "Trimming" , "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code" , Justification = "PropertyInfo already obtained" ) ]
193- [ UnconditionalSuppressMessage ( "Trimming" , "IL2072:'value' argument does not satisfy 'DynamicallyAccessedMembersAttribute' in call to target method" , Justification = "PropertyInfo already obtained with type annotations" ) ]
194- private static PropertyMetadata CreatePropertyMetadataFromPropertyInfo ( PropertyInfo property )
195- {
196- var containingType = property . DeclaringType ! ;
197- return new PropertyMetadata
198- {
199- Name = property . Name ,
200- Type = property . PropertyType ,
201- IsStatic = property . GetMethod ? . IsStatic ?? false ,
202- ClassMetadata = GetClassMetadataForType ( containingType ) ,
203- ContainingTypeMetadata = GetClassMetadataForType ( containingType ) ,
204- ReflectionInfo = property ,
205- Getter = parent => property . GetValue ( parent )
206- } ;
207- }
208-
209- [ UnconditionalSuppressMessage ( "Trimming" , "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code" , Justification = "Type is already annotated" ) ]
210- [ UnconditionalSuppressMessage ( "Trimming" , "IL2070:Target method return value does not satisfy 'DynamicallyAccessedMembersAttribute'" , Justification = "Type is already annotated" ) ]
211- [ UnconditionalSuppressMessage ( "Trimming" , "IL2072:Target parameter argument does not satisfy 'DynamicallyAccessedMembersAttribute' in call to target method" , Justification = "Type is already annotated" ) ]
212- [ UnconditionalSuppressMessage ( "Trimming" , "IL2067:Target parameter argument does not satisfy 'DynamicallyAccessedMembersAttribute' in call to target method" , Justification = "Type is already annotated" ) ]
213- private static ClassMetadata GetClassMetadataForType ( Type type )
214- {
215- return ClassMetadata . GetOrAdd ( type . FullName ?? type . Name , ( ) =>
216- {
217- var constructors = type . GetConstructors ( BindingFlags . Public | BindingFlags . Instance ) ;
218- var constructor = constructors . FirstOrDefault ( ) ;
219-
220- var constructorParameters = constructor ? . GetParameters ( ) . Select ( ( p , i ) => new ParameterMetadata ( p . ParameterType )
221- {
222- Name = p . Name ?? $ "param{ i } ",
223- TypeInfo = new ConcreteType ( p . ParameterType ) ,
224- ReflectionInfo = p
225- } ) . ToArray ( ) ?? [ ] ;
226-
227- return new ClassMetadata
228- {
229- Type = type ,
230- TypeInfo = new ConcreteType ( type ) ,
231- Name = type . Name ,
232- Namespace = type . Namespace ?? string . Empty ,
233- Assembly = AssemblyMetadata . GetOrAdd ( type . Assembly . GetName ( ) . Name ?? type . Assembly . GetName ( ) . FullName ?? "Unknown" , ( ) => new AssemblyMetadata
234- {
235- Name = type . Assembly . GetName ( ) . Name ?? type . Assembly . GetName ( ) . FullName ?? "Unknown"
236- } ) ,
237- Properties = [ ] ,
238- Parameters = constructorParameters ,
239- Parent = type . DeclaringType != null ? GetClassMetadataForType ( type . DeclaringType ) : null
240- } ;
241- } ) ;
242- }
243-
244- /// <summary>
245- /// Resolves a data source value synchronously by running the async enumerable.
246- /// </summary>
247- private static object ? ResolveDataSourceValueSync ( IDataSourceAttribute dataSource , DataGeneratorMetadata metadata , int recursionDepth )
248- {
249- var dataRows = dataSource . GetDataRowsAsync ( metadata ) ;
250-
251- // Get the first value from the async enumerable synchronously
252- var enumerator = dataRows . GetAsyncEnumerator ( ) ;
253- try
254- {
255- if ( enumerator . MoveNextAsync ( ) . AsTask ( ) . GetAwaiter ( ) . GetResult ( ) )
256- {
257- var factory = enumerator . Current ;
258- var args = factory ( ) . GetAwaiter ( ) . GetResult ( ) ;
259- if ( args is { Length : > 0 } )
260- {
261- var value = args [ 0 ] ;
262-
263- // Initialize the value if it implements IAsyncInitializer
264- ObjectInitializer . InitializeAsync ( value ) . AsTask ( ) . GetAwaiter ( ) . GetResult ( ) ;
265-
266- return value ;
267- }
268- }
269- }
270- finally
271- {
272- enumerator . DisposeAsync ( ) . AsTask ( ) . GetAwaiter ( ) . GetResult ( ) ;
273- }
274-
275- return null ;
276- }
277-
278- /// <summary>
279- /// Sets a property value, handling init-only properties via backing field if necessary.
280- /// </summary>
281- [ UnconditionalSuppressMessage ( "Trimming" , "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code" , Justification = "PropertyInfo already obtained" ) ]
282- [ UnconditionalSuppressMessage ( "Trimming" , "IL2075:'this' argument does not satisfy 'DynamicallyAccessedMembersAttribute' in call to target method" , Justification = "PropertyInfo already obtained with type annotations" ) ]
283- private static void SetPropertyValue ( PropertyInfo property , object instance , object ? value )
284- {
285- if ( property . CanWrite && property . SetMethod != null )
286- {
287- property . SetValue ( instance , value ) ;
288- return ;
289- }
290-
291- // Try to set via backing field for init-only properties
292- var backingFieldName = $ "<{ property . Name } >k__BackingField";
293- var backingField = property . DeclaringType ? . GetField (
294- backingFieldName ,
295- BindingFlags . Instance | BindingFlags . NonPublic ) ;
296-
297- if ( backingField != null )
298- {
299- backingField . SetValue ( instance , value ) ;
300- }
301- }
30291}
0 commit comments