Skip to content

Commit ef3659b

Browse files
max-charlambCopilot
andcommitted
Refactor test Builder to use version-based contract resolution
TestPlaceholderTarget.Builder now defaults to CoreCLRContracts.Register and supports AddContract<T>(version) for version-based resolution and AddMockContract<T>(mock) for injecting test doubles. TestContractRegistry supports Register, SetVersion, and SetMock with proper version lookup. Migrated 5 Builder-pattern test files from factory lambdas to version-based contract resolution. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 715c53c commit ef3659b

File tree

11 files changed

+96
-58
lines changed

11 files changed

+96
-58
lines changed

src/native/managed/cdac/tests/AuxiliarySymbolsTests.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,8 +91,8 @@ private static Target CreateTarget(
9191
.AddGlobals(
9292
(Constants.Globals.AuxiliarySymbols, arrayAddress),
9393
(Constants.Globals.AuxiliarySymbolCount, countFragment.Address))
94-
.AddContract<IPlatformMetadata>(_ => Mock.Of<IPlatformMetadata>(p => p.GetCodePointerFlags() == default(CodePointerFlags)))
95-
.AddContract<IAuxiliarySymbols>(static target => (IAuxiliarySymbols)new AuxiliarySymbols_1(target))
94+
.AddMockContract<IPlatformMetadata>(Mock.Of<IPlatformMetadata>(p => p.GetCodePointerFlags() == default(CodePointerFlags)))
95+
.AddContract<IAuxiliarySymbols>(version: 1)
9696
.Build();
9797
}
9898

src/native/managed/cdac/tests/BuiltInCOMTests.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,6 @@ private static (string Name, ulong Value)[] CreateContractGlobals(MockBuiltInCom
7171
(Constants.Globals.RCWInterfaceCacheSize, MockRCW.InterfaceEntryCacheSize),
7272
];
7373

74-
7574
[Theory]
7675
[ClassData(typeof(MockTarget.StdArch))]
7776
public void GetSimpleComCallWrapperData_ReturnsRefCountMasked(MockTarget.Architecture arch)

src/native/managed/cdac/tests/CodeVersionsTests.cs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -158,12 +158,11 @@ internal Target CreateTarget(
158158
builder.Builder.GetMemoryContext().ReadFromTarget,
159159
CreateContractTypes(builder));
160160

161-
ContractRegistry reg = Mock.Of<ContractRegistry>(
162-
c => c.CodeVersions == (ICodeVersions)new CodeVersions_1(target)
163-
&& c.RuntimeTypeSystem == mockRuntimeTypeSystem.Object
164-
&& c.ExecutionManager == mockExecutionManager.Object
165-
&& c.Loader == mockLoader.Object);
166-
target.SetContracts(reg);
161+
var registry = target.SetupContractRegistry();
162+
registry.SetVersion<ICodeVersions>(1);
163+
registry.SetMock(mockRuntimeTypeSystem.Object);
164+
registry.SetMock(mockExecutionManager.Object);
165+
registry.SetMock(mockLoader.Object);
167166
return target;
168167
}
169168

src/native/managed/cdac/tests/DacStreamsTests.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,8 @@ private static unsafe void DacStreamsContractHelper(MockTarget.Architecture arch
4343
}
4444

4545
var target = new TestPlaceholderTarget(arch, builder.GetMemoryContext().ReadFromTarget, DacStreamsTypes, DacStreamsGlobals);
46-
target.SetContracts(Mock.Of<ContractRegistry>(
47-
c => c.DacStreams == (IDacStreams)new DacStreams_1(target)));
46+
var registry = target.SetupContractRegistry();
47+
registry.SetVersion<IDacStreams>(1);
4848

4949
testCase(target);
5050
}

src/native/managed/cdac/tests/DebuggerTests.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ private static TestPlaceholderTarget BuildTarget(
6565
builder.AddGlobals((Constants.Globals.MetadataUpdatesApplied, metadataFrag.Address));
6666
}
6767

68-
builder.AddContract<IDebugger>(target => (IDebugger)new Debugger_1(target));
68+
builder.AddContract<IDebugger>(version: 1);
6969

7070
return builder.Build();
7171
}
@@ -82,7 +82,7 @@ private static TestPlaceholderTarget BuildNullDebuggerTarget(MockTarget.Architec
8282
helpers.WritePointer(debuggerPtrFrag.Data, 0);
8383
memBuilder.AddHeapFragment(debuggerPtrFrag);
8484
builder.AddGlobals((Constants.Globals.Debugger, debuggerPtrFrag.Address));
85-
builder.AddContract<IDebugger>(target => (IDebugger)new Debugger_1(target));
85+
builder.AddContract<IDebugger>(version: 1);
8686

8787
return builder.Build();
8888
}

src/native/managed/cdac/tests/ExecutionManager/ExecutionManagerTests.cs

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -44,11 +44,9 @@ private static Target CreateTarget(MockExecutionManagerBuilder emBuilder)
4444
var arch = emBuilder.Builder.TargetTestHelpers.Arch;
4545
TestPlaceholderTarget.ReadFromTargetDelegate reader = emBuilder.Builder.GetMemoryContext().ReadFromTarget;
4646
var target = new TestPlaceholderTarget(arch, reader, CreateContractTypes(emBuilder), emBuilder.Globals);
47-
IExecutionManager emContract = CreateExecutionManager(target, emBuilder.Version);
48-
ContractRegistry reg = Mock.Of<ContractRegistry>(
49-
c => c.ExecutionManager == emContract
50-
&& c.PlatformMetadata == new Mock<IPlatformMetadata>().Object);
51-
target.SetContracts(reg);
47+
var registry = target.SetupContractRegistry();
48+
registry.SetVersion<IExecutionManager>(emBuilder.Version);
49+
registry.SetMock(Mock.Of<IPlatformMetadata>());
5250
return target;
5351
}
5452

@@ -80,16 +78,6 @@ private static void LinkHeapIntoAllCodeHeaps(MockExecutionManagerBuilder emBuild
8078
emBuilder.SetAllCodeHeaps(node.Address);
8179
}
8280

83-
private static IExecutionManager CreateExecutionManager(Target target, int version)
84-
{
85-
return version switch
86-
{
87-
1 => new ExecutionManager_1(target),
88-
2 => new ExecutionManager_2(target),
89-
_ => throw new NotImplementedException(),
90-
};
91-
}
92-
9381
[Theory]
9482
[MemberData(nameof(StdArchAllVersions))]
9583
public void GetCodeBlockHandle_Null(int version, MockTarget.Architecture arch)

src/native/managed/cdac/tests/GCMemoryRegionTests.cs

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,9 @@ private static IGC CreateGCContract(MockTarget.Architecture arch,
2626
TestPlaceholderTarget.ReadFromTargetDelegate readFromTarget)
2727
{
2828
var target = new TestPlaceholderTarget(arch, readFromTarget, types, globals, globalStrings);
29-
var gcContract = CreateGC(target);
30-
target.SetContracts(Mock.Of<ContractRegistry>(
31-
c => c.GC == gcContract));
32-
return gcContract;
33-
}
34-
35-
private static IGC CreateGC(Target target)
36-
{
37-
return new GC_1(target);
29+
var registry = target.SetupContractRegistry();
30+
registry.SetVersion<IGC>(1);
31+
return target.Contracts.GC;
3832
}
3933

4034
private static (string Name, ulong Value)[] BuildGlobals(

src/native/managed/cdac/tests/GCTests.cs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -142,8 +142,7 @@ public static TestPlaceholderTarget.Builder AddGCHeapWks(
142142
GCHeapBuilder config = new();
143143
configure(config);
144144
BuildWksHeap(targetBuilder, config);
145-
targetBuilder.AddContract<IGC>(static target =>
146-
(IGC)new GC_1(target));
145+
targetBuilder.AddContract<IGC>(version: 1);
147146
return targetBuilder;
148147
}
149148

@@ -155,8 +154,7 @@ public static TestPlaceholderTarget.Builder AddGCHeapSvr(
155154
GCHeapBuilder config = new();
156155
configure(config);
157156
heapAddress = BuildSvrHeap(targetBuilder, config);
158-
targetBuilder.AddContract<IGC>(static target =>
159-
(IGC)new GC_1(target));
157+
targetBuilder.AddContract<IGC>(version: 1);
160158
return targetBuilder;
161159
}
162160

src/native/managed/cdac/tests/ReJITTests.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,8 @@ private static ReJITContractContext CreateReJITContract(
4040
TestPlaceholderTarget target = targetBuilder
4141
.AddTypes(CreateContractTypes(rejitBuilder))
4242
.AddGlobals((nameof(Constants.Globals.ProfilerControlBlock), rejitBuilder.ProfilerControlBlockGlobalAddress))
43-
.AddContract<IReJIT>(static target => (IReJIT)new ReJIT_1(target))
44-
.AddContract<ICodeVersions>(_ => mockCodeVersions.Object)
43+
.AddContract<IReJIT>(version: 1)
44+
.AddMockContract<ICodeVersions>(mockCodeVersions.Object)
4545
.Build();
4646
return new ReJITContractContext(target.Contracts.ReJIT, target);
4747
}

src/native/managed/cdac/tests/TestPlaceholderTarget.cs

Lines changed: 74 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,21 @@ internal void SetContracts(ContractRegistry contracts)
4545
_contractRegistry = contracts;
4646
}
4747

48+
/// <summary>
49+
/// Creates a <see cref="TestContractRegistry"/> with <see cref="CoreCLRContracts.Register"/>
50+
/// applied, and sets it as the contract registry for this target. Returns the registry so
51+
/// callers can call <see cref="TestContractRegistry.SetVersion{TContract}"/> and
52+
/// <see cref="TestContractRegistry.SetMock{TContract}"/>.
53+
/// </summary>
54+
internal TestContractRegistry SetupContractRegistry(Action<ContractRegistry>? registrations = null)
55+
{
56+
var registry = new TestContractRegistry();
57+
registry.SetTarget(this);
58+
(registrations ?? CoreCLRContracts.Register)(registry);
59+
_contractRegistry = registry;
60+
return registry;
61+
}
62+
4863
/// <summary>
4964
/// Fluent builder for <see cref="TestPlaceholderTarget"/>. Accumulates types,
5065
/// globals, and contract factories from mock descriptors, then materializes the
@@ -57,7 +72,8 @@ internal class Builder
5772
private readonly Dictionary<DataType, Target.TypeInfo> _types = new();
5873
private readonly List<(string Name, ulong Value)> _globals = new();
5974
private readonly List<(string Name, string Value)> _globalStrings = new();
60-
private readonly List<(Type Type, Func<Target, IContract> Factory)> _contractFactories = new();
75+
private readonly List<Action<TestContractRegistry>> _contractSetups = new();
76+
private Action<ContractRegistry> _registrations = CoreCLRContracts.Register;
6177

6278
public Builder(MockTarget.Architecture arch)
6379
{
@@ -86,9 +102,21 @@ public Builder AddGlobalStrings(params (string Name, string Value)[] globalStrin
86102
return this;
87103
}
88104

89-
public Builder AddContract<TContract>(Func<Target, TContract> factory) where TContract : IContract
105+
public Builder UseRegistrations(Action<ContractRegistry> registrations)
90106
{
91-
_contractFactories.Add((typeof(TContract), target => factory(target)));
107+
_registrations = registrations;
108+
return this;
109+
}
110+
111+
public Builder AddContract<TContract>(int version) where TContract : IContract
112+
{
113+
_contractSetups.Add(registry => registry.SetVersion<TContract>(version));
114+
return this;
115+
}
116+
117+
public Builder AddMockContract<TContract>(TContract mock) where TContract : IContract
118+
{
119+
_contractSetups.Add(registry => registry.SetMock(mock));
92120
return this;
93121
}
94122

@@ -102,8 +130,12 @@ public TestPlaceholderTarget Build()
102130
_globalStrings.ToArray());
103131

104132
var registry = new TestContractRegistry();
105-
foreach (var (type, factory) in _contractFactories)
106-
registry.Add(type, new Lazy<IContract>(() => factory(target)));
133+
registry.SetTarget(target);
134+
_registrations(registry);
135+
136+
foreach (var setup in _contractSetups)
137+
setup(registry);
138+
107139
target.SetContracts(registry);
108140

109141
return target;
@@ -455,22 +487,50 @@ public void Clear()
455487
}
456488
}
457489

458-
private sealed class TestContractRegistry : ContractRegistry
490+
internal sealed class TestContractRegistry : ContractRegistry
459491
{
460-
private readonly Dictionary<Type, Lazy<IContract>> _contracts = new();
492+
private readonly Dictionary<(Type, int), Func<Target, IContract>> _creators = new();
493+
private readonly Dictionary<Type, int> _versions = new();
494+
private readonly Dictionary<Type, IContract> _mocks = new();
495+
private readonly Dictionary<Type, IContract> _resolved = new();
496+
private Target _target;
461497

462-
public void Add(Type type, Lazy<IContract> contract) => _contracts[type] = contract;
498+
public void SetTarget(Target target) => _target = target;
499+
500+
public void SetVersion<TContract>(int version) where TContract : IContract
501+
=> _versions[typeof(TContract)] = version;
502+
503+
public void SetMock<TContract>(TContract mock) where TContract : IContract
504+
=> _mocks[typeof(TContract)] = mock;
505+
506+
public override void Register<TContract>(int version, Func<Target, TContract> creator)
507+
=> _creators[(typeof(TContract), version)] = t => creator(t);
463508

464509
public override TContract GetContract<TContract>()
465510
{
466-
if (_contracts.TryGetValue(typeof(TContract), out var lazy))
467-
return (TContract)lazy.Value;
511+
if (_resolved.TryGetValue(typeof(TContract), out var cached))
512+
return (TContract)cached;
468513

469-
throw new NotImplementedException($"Contract {typeof(TContract).Name} is not registered.");
470-
}
514+
IContract contract;
515+
if (_mocks.TryGetValue(typeof(TContract), out var mock))
516+
{
517+
contract = mock;
518+
}
519+
else if (_versions.TryGetValue(typeof(TContract), out int version))
520+
{
521+
if (!_creators.TryGetValue((typeof(TContract), version), out var creator))
522+
throw new NotImplementedException($"No implementation registered for contract '{typeof(TContract).Name}' version {version}.");
471523

472-
public override void Register<TContract>(int version, Func<Target, TContract> creator)
473-
=> throw new NotSupportedException("TestContractRegistry does not support dynamic registration. Use SetContracts to configure contracts for testing.");
524+
contract = creator(_target);
525+
}
526+
else
527+
{
528+
throw new NotImplementedException($"Contract {typeof(TContract).Name} is not registered. Use AddContract<T>(version) or AddMockContract<T>(mock) on the Builder.");
529+
}
530+
531+
_resolved[typeof(TContract)] = contract;
532+
return (TContract)contract;
533+
}
474534

475535
public override void Flush() { }
476536
}

0 commit comments

Comments
 (0)