@@ -4,44 +4,119 @@ namespace TUnit.Engine.Extensions;
44
55internal static class TestContextExtensions
66{
7- private static object ? [ ] GetInternal ( TestContext testContext )
7+ public static IEnumerable < object > GetEligibleEventObjects ( this TestContext testContext )
88 {
9- var testClassArgs = testContext . Metadata . TestDetails . TestClassArguments ;
10- var attributes = testContext . Metadata . TestDetails . GetAllAttributes ( ) ;
11- var testMethodArgs = testContext . Metadata . TestDetails . TestMethodArguments ;
12- var injectedProps = testContext . Metadata . TestDetails . TestClassInjectedPropertyArguments ;
13-
14- // Pre-calculate capacity to avoid reallocations
15- var capacity = 3 + testClassArgs . Length + attributes . Count + testMethodArgs . Length + injectedProps . Count ;
16- var result = new List < object ? > ( capacity ) ;
17-
18- result . Add ( testContext . ClassConstructor ) ;
19- result . Add ( testContext . Events ) ;
20- result . AddRange ( testClassArgs ) ;
21- result . Add ( testContext . Metadata . TestDetails . ClassInstance ) ;
22- result . AddRange ( attributes ) ;
23- result . AddRange ( testMethodArgs ) ;
24-
25- // Manual loop instead of .Select() to avoid LINQ allocation
26- foreach ( var prop in injectedProps )
9+ // Return cached result if available
10+ if ( testContext . CachedEligibleEventObjects != null )
2711 {
28- result . Add ( prop . Value ) ;
12+ return testContext . CachedEligibleEventObjects ;
2913 }
3014
31- return result . ToArray ( ) ;
15+ // Build result directly with single allocation
16+ var result = BuildEligibleEventObjects ( testContext ) ;
17+ testContext . CachedEligibleEventObjects = result ;
18+ return result ;
3219 }
3320
34- public static IEnumerable < object > GetEligibleEventObjects ( this TestContext testContext )
21+ private static object [ ] BuildEligibleEventObjects ( TestContext testContext )
3522 {
36- // Return cached result if available
37- if ( testContext . CachedEligibleEventObjects != null )
23+ var details = testContext . Metadata . TestDetails ;
24+ var testClassArgs = details . TestClassArguments ;
25+ var attributes = details . GetAllAttributes ( ) ;
26+ var testMethodArgs = details . TestMethodArguments ;
27+ var injectedProps = details . TestClassInjectedPropertyArguments ;
28+
29+ // Count non-null items first to allocate exact size
30+ var count = CountNonNull ( testContext . ClassConstructor )
31+ + CountNonNull ( testContext . Events )
32+ + CountNonNullInArray ( testClassArgs )
33+ + CountNonNull ( details . ClassInstance )
34+ + attributes . Count // Attributes are never null
35+ + CountNonNullInArray ( testMethodArgs )
36+ + CountNonNullValues ( injectedProps ) ;
37+
38+ if ( count == 0 )
3839 {
39- return testContext . CachedEligibleEventObjects ;
40+ return [ ] ;
41+ }
42+
43+ // Single allocation with exact size
44+ var result = new object [ count ] ;
45+ var index = 0 ;
46+
47+ // Add items, skipping nulls
48+ if ( testContext . ClassConstructor is { } constructor )
49+ {
50+ result [ index ++ ] = constructor ;
51+ }
52+
53+ if ( testContext . Events is { } events )
54+ {
55+ result [ index ++ ] = events ;
56+ }
57+
58+ foreach ( var arg in testClassArgs )
59+ {
60+ if ( arg is { } nonNullArg )
61+ {
62+ result [ index ++ ] = nonNullArg ;
63+ }
64+ }
65+
66+ if ( details . ClassInstance is { } classInstance )
67+ {
68+ result [ index ++ ] = classInstance ;
69+ }
70+
71+ foreach ( var attr in attributes )
72+ {
73+ result [ index ++ ] = attr ;
74+ }
75+
76+ foreach ( var arg in testMethodArgs )
77+ {
78+ if ( arg is { } nonNullArg )
79+ {
80+ result [ index ++ ] = nonNullArg ;
81+ }
82+ }
83+
84+ foreach ( var prop in injectedProps )
85+ {
86+ if ( prop . Value is { } value )
87+ {
88+ result [ index ++ ] = value ;
89+ }
4090 }
4191
42- // Materialize and cache the result
43- var result = GetInternal ( testContext ) . OfType < object > ( ) . ToArray ( ) ;
44- testContext . CachedEligibleEventObjects = result ;
4592 return result ;
4693 }
94+
95+ private static int CountNonNull ( object ? obj ) => obj != null ? 1 : 0 ;
96+
97+ private static int CountNonNullInArray ( object ? [ ] array )
98+ {
99+ var count = 0 ;
100+ foreach ( var item in array )
101+ {
102+ if ( item != null )
103+ {
104+ count ++ ;
105+ }
106+ }
107+ return count ;
108+ }
109+
110+ private static int CountNonNullValues ( IDictionary < string , object ? > props )
111+ {
112+ var count = 0 ;
113+ foreach ( var prop in props )
114+ {
115+ if ( prop . Value != null )
116+ {
117+ count ++ ;
118+ }
119+ }
120+ return count ;
121+ }
47122}
0 commit comments