Summary
Follow-up to the lazy registration optimization (which defers per-class .cctor JIT from module init to discovery time).
Currently, when the engine needs to filter tests by method name/category, it must resolve the lazy factory for each matching class — which triggers the full .cctor (creating ClassMetadata, MethodMetadata, delegate captures, TestEntry[] arrays).
The idea is to emit filter data in a separate nested class with its own lightweight .cctor, so that discovery filtering only JITs the filter data — not the full test entries.
Proposed approach
Split the generated __TestSource class into two parts:
internal static class TUnit_TestProject_BasicTests__TestSource
{
// Separate class = separate .cctor (just strings and booleans, no delegates)
internal static class Filter
{
public static readonly int Count = 3;
public static readonly TestEntryFilterData[] Data = new[]
{
new TestEntryFilterData { MethodName = "SynchronousTest", ClassName = "BasicTests", ... },
new TestEntryFilterData { MethodName = "AsynchronousTest", ClassName = "BasicTests", ... },
new TestEntryFilterData { MethodName = "ValueTaskAsynchronousTest", ClassName = "BasicTests", ... },
};
}
// Full entries — only accessed when tests actually need to execute
public static readonly TestEntry<BasicTests>[] Entries = /* ... existing ... */;
}
Registration becomes:
SourceRegistrar.RegisterLazy<BasicTests>(
TUnit_TestProject_BasicTests__TestSource.Filter.Data, // ← cheap, separate .cctor
static () => TUnit_TestProject_BasicTests__TestSource.Entries); // ← deferred until execution
Benefit
During discovery:
- Type filter — zero materialization (dict key check only)
- Method/category filter — only
Filter..cctor JIT'd (just string arrays, ~microseconds)
- Execution — full
__TestSource..cctor JIT'd (delegates, metadata) only for tests that will run
Context
Summary
Follow-up to the lazy registration optimization (which defers per-class
.cctorJIT from module init to discovery time).Currently, when the engine needs to filter tests by method name/category, it must resolve the lazy factory for each matching class — which triggers the full
.cctor(creatingClassMetadata,MethodMetadata, delegate captures,TestEntry[]arrays).The idea is to emit filter data in a separate nested class with its own lightweight
.cctor, so that discovery filtering only JITs the filter data — not the full test entries.Proposed approach
Split the generated
__TestSourceclass into two parts:Registration becomes:
Benefit
During discovery:
Filter..cctorJIT'd (just string arrays, ~microseconds)__TestSource..cctorJIT'd (delegates, metadata) only for tests that will runContext