Implement engine-bridge PoC v1 (#9)

- Add Recordingtest.EngineBridge library (IEngineSnapshot, HmEgSnapshot
  skeleton, MetadataLoader, CandidateFinder, CatalogWriter).
- Add Recordingtest.EngineBridge.Probe console exe that dumps
  hmeg-types.json and hmeg-candidates.json to docs/engine-catalog.
- Add Recordingtest.EngineBridge.Tests (xUnit, 6 tests).
- Add probe design doc with plugin-masquerade recommendation.
- Static analysis only; SUT is never executed.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
minsung
2026-04-07 15:48:58 +09:00
parent 13dc4109d8
commit 2a4f1d3fa4
16 changed files with 127677 additions and 0 deletions

View File

@@ -0,0 +1,87 @@
using System.Reflection;
using Recordingtest.EngineBridge;
namespace Recordingtest.EngineBridge.Probe;
internal static class Program
{
private static readonly string[] DefaultAssemblyPatterns =
{
"HmEG.dll",
"HmGeometry.dll",
"HmGeometry.V2.dll",
"HmTriangle.dll",
"EditorCore.dll",
"Editor*.dll",
};
internal static int Main(string[] args)
{
string sutRoot = "EG-BIM Modeler";
string outDir = Path.Combine("docs", "engine-catalog");
for (int i = 0; i < args.Length; i++)
{
switch (args[i])
{
case "--sut" when i + 1 < args.Length:
sutRoot = args[++i];
break;
case "--out" when i + 1 < args.Length:
outDir = args[++i];
break;
}
}
if (!Directory.Exists(sutRoot))
{
Console.Error.WriteLine($"SUT path not found: {sutRoot}");
return 2;
}
var assemblyNames = ResolveAssemblyNames(sutRoot);
Console.WriteLine($"Loading {assemblyNames.Count} assemblies from {sutRoot}");
using var loader = new MetadataLoader(sutRoot);
var types = loader.LoadTypes(assemblyNames).ToList();
var byAsm = types
.GroupBy(t => t.Assembly.GetName().Name ?? "?")
.OrderBy(g => g.Key, StringComparer.Ordinal);
foreach (var g in byAsm)
{
Console.WriteLine($" {g.Key}: {g.Count()} types");
}
var candidates = CandidateFinder.Find(types);
var byCat = candidates
.GroupBy(c => c.Category)
.OrderBy(g => g.Key, StringComparer.Ordinal);
Console.WriteLine("Candidate categories:");
foreach (var g in byCat)
{
Console.WriteLine($" {g.Key}: {g.Count()}");
}
var typesPath = Path.Combine(outDir, "hmeg-types.json").Replace('\\', '/');
var candPath = Path.Combine(outDir, "hmeg-candidates.json").Replace('\\', '/');
CatalogWriter.WriteTypes(typesPath, types);
CatalogWriter.WriteCandidates(candPath, candidates);
Console.WriteLine($"Wrote {typesPath}");
Console.WriteLine($"Wrote {candPath}");
return 0;
}
private static List<string> ResolveAssemblyNames(string sutRoot)
{
var set = new SortedSet<string>(StringComparer.Ordinal);
foreach (var pat in DefaultAssemblyPatterns)
{
foreach (var f in Directory.EnumerateFiles(sutRoot, pat, SearchOption.TopDirectoryOnly))
{
set.Add(Path.GetFileName(f));
}
}
return set.ToList();
}
}

View File

@@ -0,0 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<AssemblyName>Recordingtest.EngineBridge.Probe</AssemblyName>
<RootNamespace>Recordingtest.EngineBridge.Probe</RootNamespace>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Recordingtest.EngineBridge\Recordingtest.EngineBridge.csproj" />
</ItemGroup>
</Project>