Skip to content

Commit

Permalink
[cdac] Simplify usage of TestPlaceholderTarget in tests (#109873)
Browse files Browse the repository at this point in the history
- Make `TestPlaceholderTarget` (mock target) constructor take type infos and global values
- Handle reading of global values in mock target implementation
- Use Moq for creating a `ContractRegistry` instead of our own explicit implementation
- Remove subclasses of `TestPlaceholderTarget`
  • Loading branch information
elinor-fung authored Nov 20, 2024
1 parent 1e3544e commit 2eb6a2a
Show file tree
Hide file tree
Showing 12 changed files with 168 additions and 318 deletions.
64 changes: 29 additions & 35 deletions src/native/managed/cdacreader/tests/CodeVersionsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using Microsoft.Diagnostics.DataContractReader.Contracts;
using Moq;
using Xunit;

namespace Microsoft.Diagnostics.DataContractReader.UnitTests;
Expand Down Expand Up @@ -226,43 +227,36 @@ TargetPointer ILoader.GetModuleLookupMapElement(TargetPointer tableAddress, uint
}
}

internal class CVTestTarget : TestPlaceholderTarget
internal static Target CreateTarget(
MockTarget.Architecture arch,
IReadOnlyCollection<MockMethodDesc> methodDescs = null,
IReadOnlyCollection<MockMethodTable> methodTables = null,
IReadOnlyCollection<MockCodeBlockStart> codeBlocks = null,
IReadOnlyCollection<MockModule> modules = null,
MockCodeVersions builder = null)
{
public static CVTestTarget FromBuilder(MockTarget.Architecture arch, IReadOnlyCollection<MockMethodDesc> methodDescs, IReadOnlyCollection<MockMethodTable> methodTables, IReadOnlyCollection<MockCodeBlockStart> codeBlocks, IReadOnlyCollection<MockModule> modules, MockCodeVersions builder)
{
builder.MarkCreated();
return new CVTestTarget(arch, reader: builder.Builder.GetReadContext().ReadFromTarget, typeInfoCache: builder.Types,
methodDescs: methodDescs, methodTables: methodTables, codeBlocks: codeBlocks, modules: modules);
}

public CVTestTarget(MockTarget.Architecture arch, IReadOnlyCollection<MockMethodDesc>? methodDescs = null,
IReadOnlyCollection<MockMethodTable>? methodTables = null,
IReadOnlyCollection<MockCodeBlockStart>? codeBlocks = null,
IReadOnlyCollection<MockModule>? modules = null,
ReadFromTargetDelegate reader = null,
Dictionary<DataType, TypeInfo>? typeInfoCache = null)
: base(arch, reader)
{
IExecutionManager mockExecutionManager = new MockExecutionManager(codeBlocks ?? []);
IRuntimeTypeSystem mockRuntimeTypeSystem = new MockRuntimeTypeSystem(this, methodDescs ?? [], methodTables ?? []);
ILoader loader = new MockLoader(modules ?? []);
if (typeInfoCache != null)
SetTypeInfoCache(typeInfoCache);
IContractFactory<ICodeVersions> cvfactory = new CodeVersionsFactory();
SetContracts(new TestRegistry() {
CodeVersionsContract = new (() => cvfactory.CreateContract(this, 1)),
ExecutionManagerContract = new (() => mockExecutionManager),
RuntimeTypeSystemContract = new (() => mockRuntimeTypeSystem),
LoaderContract = new (() => loader),
});
}
TestPlaceholderTarget target = builder != null
? new TestPlaceholderTarget(arch, builder.Builder.GetReadContext().ReadFromTarget, builder.Types)
: new TestPlaceholderTarget(arch, null);

IExecutionManager mockExecutionManager = new MockExecutionManager(codeBlocks ?? []);
IRuntimeTypeSystem mockRuntimeTypeSystem = new MockRuntimeTypeSystem(target, methodDescs ?? [], methodTables ?? []);
ILoader loader = new MockLoader(modules ?? []);
IContractFactory<ICodeVersions> cvfactory = new CodeVersionsFactory();
ContractRegistry reg = Mock.Of<ContractRegistry>(
c => c.CodeVersions == cvfactory.CreateContract(target, 1)
&& c.ExecutionManager == mockExecutionManager
&& c.RuntimeTypeSystem == mockRuntimeTypeSystem
&& c.Loader == loader);
target.SetContracts(reg);
return target;
}

[Theory]
[ClassData(typeof(MockTarget.StdArch))]
public void GetNativeCodeVersion_Null(MockTarget.Architecture arch)
{
var target = new CVTestTarget(arch);
var target = CreateTarget(arch);
var codeVersions = target.Contracts.CodeVersions;

Assert.NotNull(codeVersions);
Expand All @@ -286,7 +280,7 @@ public void GetNativeCodeVersion_OneVersion_NonVersionable(MockTarget.Architectu
MethodDesc = oneMethod,
};

var target = new CVTestTarget(arch, methodDescs: [oneMethod], codeBlocks: [oneBlock]);
var target = CreateTarget(arch, methodDescs: [oneMethod], codeBlocks: [oneBlock]);
var codeVersions = target.Contracts.CodeVersions;

Assert.NotNull(codeVersions);
Expand Down Expand Up @@ -320,7 +314,7 @@ public void GetNativeCodeVersion_OneVersion_Versionable(MockTarget.Architecture
};
builder.FillNativeCodeVersionNode(nativeCodeVersionNode, methodDesc: oneMethod.Address, nativeCode: codeBlockStart, next: TargetPointer.Null, isActive: false, ilVersionId: default);

var target = CVTestTarget.FromBuilder(arch, [oneMethod], [], [oneBlock], [], builder);
var target = CreateTarget(arch, [oneMethod], [], [oneBlock], [], builder);

// TEST

Expand Down Expand Up @@ -367,7 +361,7 @@ public void GetActiveNativeCodeVersion_DefaultCase(MockTarget.Architecture arch)
oneMethod.MethodTable = oneMethodTable;
oneMethod.RowId = methodRowId;

var target = CVTestTarget.FromBuilder(arch, [oneMethod], [oneMethodTable], [], [oneModule], builder);
var target = CreateTarget(arch, [oneMethod], [oneMethodTable], [], [oneModule], builder);

// TEST

Expand Down Expand Up @@ -430,7 +424,7 @@ private void GetActiveNativeCodeVersion_IterateVersionNodes_Impl(MockTarget.Arch
methodDesc.MethodTable = methodTable;
methodDesc.RowId = methodRowId;

var target = CVTestTarget.FromBuilder(arch, [methodDesc], [methodTable], [], [module], builder);
var target = CreateTarget(arch, [methodDesc], [methodTable], [], [module], builder);

// TEST

Expand Down Expand Up @@ -501,7 +495,7 @@ private void GetActiveNativeCodeVersion_ExplicitILCodeVersion_Impl(MockTarget.Ar
oneMethod.MethodTable = methodTable;
oneMethod.RowId = methodRowId;

var target = CVTestTarget.FromBuilder(arch, [oneMethod], [methodTable], [], [module], builder);
var target = CreateTarget(arch, [oneMethod], [methodTable], [], [module], builder);

// TEST

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Diagnostics.DataContractReader.ExecutionManagerHelpers;

using InteriorMapValue = Microsoft.Diagnostics.DataContractReader.ExecutionManagerHelpers.RangeSectionMap.InteriorMapValue;
Expand All @@ -13,6 +14,7 @@ internal class ExecutionManagerTestBuilder
{
public const ulong ExecutionManagerCodeRangeMapAddress = 0x000a_fff0;

const bool UseFunclets = true;
const int RealCodeHeaderSize = 0x08; // must be big enough for the offsets of RealCodeHeader size in ExecutionManagerTestTarget, below

public struct AllocationRange
Expand Down Expand Up @@ -159,10 +161,6 @@ public void InsertAddressRange(TargetCodePointer start, uint length, ulong value
cur = new TargetCodePointer(cur.Value + (ulong)BytesAtLastLevel); // FIXME: round ?
} while (cur.Value < end);
}
public void MarkCreated()
{
_builder.MarkCreated();
}

public MockMemorySpace.ReadContext GetReadContext()
{
Expand Down Expand Up @@ -259,6 +257,7 @@ public static RangeSectionMapTestBuilder CreateRangeSection(MockTarget.Architect

internal MockMemorySpace.Builder Builder { get; }
internal Dictionary<DataType, Target.TypeInfo> Types { get; }
internal (string Name, ulong Value)[] Globals { get; }

private readonly RangeSectionMapTestBuilder _rsmBuilder;

Expand Down Expand Up @@ -287,11 +286,19 @@ internal ExecutionManagerTestBuilder(int version, MockMemorySpace.Builder builde
CodeHeapListNodeFields,
RealCodeHeaderFields,
RuntimeFunctionFields,
MockDescriptors.HashMap.HashMapFields,
MockDescriptors.HashMap.BucketFields(Builder.TargetTestHelpers),
ReadyToRunInfoFields(Builder.TargetTestHelpers),
MockDescriptors.ModuleFields,
]);
]).Concat(MockDescriptors.HashMap.GetTypes(Builder.TargetTestHelpers))
.ToDictionary();
Globals =
[
(nameof(Constants.Globals.ExecutionManagerCodeRangeMapAddress), ExecutionManagerCodeRangeMapAddress),
(nameof(Constants.Globals.StubCodeBlockLast), 0x0Fu),
(nameof(Constants.Globals.FeatureEHFunclets), UseFunclets ? 1 : 0),
];
Globals = Globals
.Concat(MockDescriptors.HashMap.GetGlobals(Builder.TargetTestHelpers))
.ToArray();
}
internal NibbleMapTestBuilderBase CreateNibbleMap(ulong codeRangeStart, uint codeRangeSize)
Expand Down Expand Up @@ -469,6 +476,4 @@ public TargetPointer AddReadyToRunModule(TargetPointer r2rInfo)

return r2rModule.Address;
}

public void MarkCreated() => Builder.MarkCreated();
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,75 +11,25 @@ namespace Microsoft.Diagnostics.DataContractReader.UnitTests.ExecutionManager;

public class ExecutionManagerTests
{
internal class ExecutionManagerTestTarget : TestPlaceholderTarget
private static Target CreateTarget(ExecutionManagerTestBuilder emBuilder)
{
private readonly ulong _topRangeSectionMap;

public static ExecutionManagerTestTarget FromBuilder(ExecutionManagerTestBuilder emBuilder)
{
var arch = emBuilder.Builder.TargetTestHelpers.Arch;
ReadFromTargetDelegate reader = emBuilder.Builder.GetReadContext().ReadFromTarget;
var topRangeSectionMap = ExecutionManagerTestBuilder.ExecutionManagerCodeRangeMapAddress;
var typeInfo = emBuilder.Types;
return new ExecutionManagerTestTarget(emBuilder.Version, arch, reader, topRangeSectionMap, typeInfo);
}

public ExecutionManagerTestTarget(int version, MockTarget.Architecture arch, ReadFromTargetDelegate dataReader, TargetPointer topRangeSectionMap, Dictionary<DataType, TypeInfo> typeInfoCache)
: base(arch, dataReader)
{
_topRangeSectionMap = topRangeSectionMap;
SetTypeInfoCache(typeInfoCache);
IContractFactory<IExecutionManager> emfactory = new ExecutionManagerFactory();
SetContracts(new TestRegistry() {
ExecutionManagerContract = new (() => emfactory.CreateContract(this, version)),
PlatformMetadataContract = new (() => new Mock<IPlatformMetadata>().Object)
});
}
public override TargetPointer ReadGlobalPointer(string global)
{
switch (global)
{
case Constants.Globals.ExecutionManagerCodeRangeMapAddress:
return new TargetPointer(_topRangeSectionMap);
default:
return base.ReadGlobalPointer(global);
}
}

public override T ReadGlobal<T>(string name)
{
switch (name)
{
case Constants.Globals.StubCodeBlockLast:
if (typeof(T) == typeof(byte))
return (T)(object)(byte)0x0Fu;
break;
case Constants.Globals.FeatureEHFunclets:
if (typeof(T) == typeof(byte))
return (T)(object)(byte)1;
break;
case Constants.Globals.HashMapValueMask:
if (typeof(T) == typeof(ulong))
return (T)(object)(PointerSize == 4 ? 0x7FFFFFFFu : 0x7FFFFFFFFFFFFFFFu);
break;
case Constants.Globals.HashMapSlotsPerBucket:
if (typeof(T) == typeof(uint))
return (T)(object)4u;
break;
default:
break;
}
return base.ReadGlobal<T>(name);
}
var arch = emBuilder.Builder.TargetTestHelpers.Arch;
TestPlaceholderTarget.ReadFromTargetDelegate reader = emBuilder.Builder.GetReadContext().ReadFromTarget;
var target = new TestPlaceholderTarget(arch, reader, emBuilder.Types, emBuilder.Globals);
IContractFactory<IExecutionManager> emfactory = new ExecutionManagerFactory();
ContractRegistry reg = Mock.Of<ContractRegistry>(
c => c.ExecutionManager == emfactory.CreateContract(target, emBuilder.Version)
&& c.PlatformMetadata == new Mock<IPlatformMetadata>().Object);
target.SetContracts(reg);
return target;
}

[Theory]
[MemberData(nameof(StdArchAllVersions))]
public void GetCodeBlockHandle_Null(int version, MockTarget.Architecture arch)
{
ExecutionManagerTestBuilder emBuilder = new (version, arch, ExecutionManagerTestBuilder.DefaultAllocationRange);
emBuilder.MarkCreated();
var target = ExecutionManagerTestTarget.FromBuilder (emBuilder);
var target = CreateTarget(emBuilder);

var em = target.Contracts.ExecutionManager;
Assert.NotNull(em);
Expand All @@ -92,8 +42,7 @@ public void GetCodeBlockHandle_Null(int version, MockTarget.Architecture arch)
public void GetCodeBlockHandle_NoRangeSections(int version, MockTarget.Architecture arch)
{
ExecutionManagerTestBuilder emBuilder = new (version, arch, ExecutionManagerTestBuilder.DefaultAllocationRange);
emBuilder.MarkCreated();
var target = ExecutionManagerTestTarget.FromBuilder (emBuilder);
var target = CreateTarget(emBuilder);

var em = target.Contracts.ExecutionManager;
Assert.NotNull(em);
Expand Down Expand Up @@ -125,9 +74,7 @@ public void GetMethodDesc_OneRangeOneMethod(int version, MockTarget.Architecture
TargetPointer rangeSectionAddress = emBuilder.AddRangeSection(jittedCode, jitManagerAddress: jitManagerAddress, codeHeapListNodeAddress: codeHeapListNodeAddress);
TargetPointer rangeSectionFragmentAddress = emBuilder.AddRangeSectionFragment(jittedCode, rangeSectionAddress);

emBuilder.MarkCreated();

var target = ExecutionManagerTestTarget.FromBuilder(emBuilder);
var target = CreateTarget(emBuilder);

var em = target.Contracts.ExecutionManager;
Assert.NotNull(em);
Expand Down Expand Up @@ -169,9 +116,7 @@ public void GetCodeBlockHandle_OneRangeZeroMethod(int version, MockTarget.Archit
TargetPointer rangeSectionAddress = emBuilder.AddRangeSection(jittedCode, jitManagerAddress: jitManagerAddress, codeHeapListNodeAddress: codeHeapListNodeAddress);
TargetPointer rangeSectionFragmentAddress = emBuilder.AddRangeSectionFragment(jittedCode, rangeSectionAddress);

emBuilder.MarkCreated();

var target = ExecutionManagerTestTarget.FromBuilder(emBuilder);
var target = CreateTarget(emBuilder);

var em = target.Contracts.ExecutionManager;
Assert.NotNull(em);
Expand Down Expand Up @@ -212,9 +157,7 @@ public void GetCodeBlockHandle_R2R_NoRuntimeFunctionMatch(int version, MockTarge
TargetPointer rangeSectionAddress = emBuilder.AddReadyToRunRangeSection(jittedCode, jitManagerAddress, r2rModule);
_ = emBuilder.AddRangeSectionFragment(jittedCode, rangeSectionAddress);

emBuilder.MarkCreated();

Target target = ExecutionManagerTestTarget.FromBuilder(emBuilder);
Target target = CreateTarget(emBuilder);

IExecutionManager em = target.Contracts.ExecutionManager;
Assert.NotNull(em);
Expand Down Expand Up @@ -249,9 +192,7 @@ public void GetMethodDesc_R2R_OneRuntimeFunction(int version, MockTarget.Archite
TargetPointer rangeSectionAddress = emBuilder.AddReadyToRunRangeSection(jittedCode, jitManagerAddress, r2rModule);
_ = emBuilder.AddRangeSectionFragment(jittedCode, rangeSectionAddress);

emBuilder.MarkCreated();

Target target = ExecutionManagerTestTarget.FromBuilder(emBuilder);
Target target = CreateTarget(emBuilder);

IExecutionManager em = target.Contracts.ExecutionManager;
Assert.NotNull(em);
Expand Down Expand Up @@ -300,9 +241,7 @@ public void GetMethodDesc_R2R_MultipleRuntimeFunctions(int version, MockTarget.A
TargetPointer rangeSectionAddress = emBuilder.AddReadyToRunRangeSection(jittedCode, jitManagerAddress, r2rModule);
_ = emBuilder.AddRangeSectionFragment(jittedCode, rangeSectionAddress);

emBuilder.MarkCreated();

Target target = ExecutionManagerTestTarget.FromBuilder(emBuilder);
Target target = CreateTarget(emBuilder);

IExecutionManager em = target.Contracts.ExecutionManager;
Assert.NotNull(em);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,29 +9,6 @@ namespace Microsoft.Diagnostics.DataContractReader.UnitTests.ExecutionManager;

public class HashMapTests
{
internal class HashMapTestTarget : TestPlaceholderTarget
{
private readonly (string Name, ulong Value, string? Type)[] _globals;

public HashMapTestTarget(MockTarget.Architecture arch, MockMemorySpace.ReadContext readContext, MockDescriptors.HashMap hashMap)
: base (arch, readContext.ReadFromTarget)
{
_globals = hashMap.Globals;
SetTypeInfoCache(hashMap.Types);
}

public override T ReadGlobal<T>(string name)
{
foreach (var global in _globals)
{
if (global.Name == name)
return T.CreateChecked(global.Value);
}

return base.ReadGlobal<T>(name);
}
}

[Theory]
[ClassData(typeof(MockTarget.StdArch))]
public void GetValue(MockTarget.Architecture arch)
Expand All @@ -47,9 +24,8 @@ public void GetValue(MockTarget.Architecture arch)
];
TargetPointer mapAddress = hashMap.CreateMap(entries);
TargetPointer ptrMapAddress = hashMap.CreatePtrMap(entries);
builder.MarkCreated();

Target target = new HashMapTestTarget(arch, builder.GetReadContext(), hashMap);
Target target = new TestPlaceholderTarget(builder.TargetTestHelpers.Arch, builder.GetReadContext().ReadFromTarget, hashMap.Types, hashMap.Globals);

var lookup = HashMapLookup.Create(target);
var ptrLookup = PtrHashMapLookup.Create(target);
Expand Down Expand Up @@ -84,9 +60,8 @@ public void GetValue_Collision(MockTarget.Architecture arch)
];
TargetPointer mapAddress = hashMap.CreateMap(entries);
TargetPointer ptrMapAddress = hashMap.CreatePtrMap(entries);
builder.MarkCreated();

Target target = new HashMapTestTarget(arch, builder.GetReadContext(), hashMap);
Target target = new TestPlaceholderTarget(builder.TargetTestHelpers.Arch, builder.GetReadContext().ReadFromTarget, hashMap.Types, hashMap.Globals);

var lookup = HashMapLookup.Create(target);
var ptrLookup = PtrHashMapLookup.Create(target);
Expand All @@ -110,9 +85,8 @@ public void GetValue_NoMatch(MockTarget.Architecture arch)
(TargetPointer Key, TargetPointer Value)[] entries = [(0x100, 0x010)];
TargetPointer mapAddress = hashMap.CreateMap(entries);
TargetPointer ptrMapAddress = hashMap.CreatePtrMap(entries);
builder.MarkCreated();

Target target = new HashMapTestTarget(arch, builder.GetReadContext(), hashMap);
Target target = new TestPlaceholderTarget(builder.TargetTestHelpers.Arch, builder.GetReadContext().ReadFromTarget, hashMap.Types, hashMap.Globals);

{
var lookup = HashMapLookup.Create(target);
Expand Down
Loading

0 comments on commit 2eb6a2a

Please sign in to comment.