using System.Reflection; using System.Text.Json; using Recordingtest.EngineBridge; using Xunit; namespace Recordingtest.EngineBridge.Tests; public sealed class EngineBridgeTests { private static readonly Lazy SutRootLazy = new(FindSutRoot); private static string? SutRoot => SutRootLazy.Value; private static bool SutAvailable => SutRoot != null; private static readonly string[] TargetAssemblies = { "HmEG.dll", "HmGeometry.dll", "HmGeometry.V2.dll", "HmTriangle.dll", "EditorCore.dll", "Editor02.HmEGAppManager.dll", "Editor03.PluginInterface.dll", }; private static string? FindSutRoot() { var dir = new DirectoryInfo(AppContext.BaseDirectory); for (int i = 0; i < 10 && dir != null; i++) { var candidate = Path.Combine(dir.FullName, "EG-BIM Modeler"); if (Directory.Exists(candidate) && File.Exists(Path.Combine(candidate, "HmEG.dll"))) return candidate; dir = dir.Parent; } return null; } [Fact] public void MetadataLoader_LoadsHmegAssembly_WithoutExecution() { if (!SutAvailable) { Assert.True(true, "SUT not available — skipped"); return; } // MetadataLoadContext is pure metadata: it never runs type initializers // or any user code from the loaded assembly. The fact that we can load // HmEG.dll (which would otherwise need SharpDX/WPF at runtime) and // enumerate DefinedTypes without a TypeInitializationException is the // observable evidence of that guarantee. using var loader = new MetadataLoader(SutRoot!); var types = loader.LoadTypes(new[] { "HmEG.dll" }).ToList(); Assert.True(types.Count > 0); } [Fact] public void CandidateFinder_FindsSelectionRelatedTypes() { if (!SutAvailable) { Assert.True(true, "SUT not available — skipped"); return; } using var loader = new MetadataLoader(SutRoot!); var types = loader.LoadTypes(TargetAssemblies).ToList(); var candidates = CandidateFinder.Find(types); Assert.Contains(candidates, c => c.Category == "select"); } [Fact] public void CatalogSerializer_OutputsSorted_Idempotent() { if (!SutAvailable) { Assert.True(true, "SUT not available — skipped"); return; } using var loader = new MetadataLoader(SutRoot!); var types = loader.LoadTypes(new[] { "HmEG.dll" }).ToList(); var candidates = CandidateFinder.Find(types); var tmp1 = Path.Combine(Path.GetTempPath(), "eb-cand-1-" + Guid.NewGuid().ToString("N") + ".json"); var tmp2 = Path.Combine(Path.GetTempPath(), "eb-cand-2-" + Guid.NewGuid().ToString("N") + ".json"); try { CatalogWriter.WriteCandidates(tmp1, candidates); CatalogWriter.WriteCandidates(tmp2, candidates); Assert.Equal(File.ReadAllBytes(tmp1), File.ReadAllBytes(tmp2)); } finally { File.Delete(tmp1); File.Delete(tmp2); } } [Fact] public void HmEgSnapshot_DefaultInstance_ThrowsNotImplemented() { var s = new HmEgSnapshot(); Assert.Throws(() => _ = s.SelectedObjectIds); Assert.Throws(() => _ = s.Camera); Assert.Throws(() => _ = s.Scene); Assert.Throws(() => _ = s.IsRenderComplete); } [Fact] public void CandidateCategories_AllFourPresent() { if (!SutAvailable) { Assert.True(true, "SUT not available — skipped"); return; } using var loader = new MetadataLoader(SutRoot!); var types = loader.LoadTypes(TargetAssemblies).ToList(); var candidates = CandidateFinder.Find(types); foreach (var cat in new[] { "select", "camera", "scene", "render" }) { Assert.True(candidates.Any(c => c.Category == cat), $"category '{cat}' missing"); } } [Fact] public void HmEgSnapshot_Constants_MatchCatalog() { if (!SutAvailable) { Assert.True(true, "SUT not available — skipped"); return; } using var loader = new MetadataLoader(SutRoot!); var types = loader.LoadTypes(TargetAssemblies).ToList(); var candidates = CandidateFinder.Find(types); // HmEgAssemblyHint ("HmEG") should be a prefix of at least one loaded assembly. var asmNames = types.Select(t => t.Assembly.GetName().Name ?? "").Distinct().ToList(); Assert.Contains(asmNames, a => a.StartsWith(HmEgSnapshot.HmEgAssemblyHint, StringComparison.Ordinal)); // EditorManagerTypeHint ("HmEGAppManager") should appear in at least one candidate TypeName OR type entry. var appearsInCandidates = candidates.Any(c => c.TypeName.Contains(HmEgSnapshot.EditorManagerTypeHint, StringComparison.Ordinal)); var appearsInTypes = types.Any(t => (t.FullName ?? t.Name).Contains(HmEgSnapshot.EditorManagerTypeHint, StringComparison.Ordinal) || (t.Assembly.GetName().Name ?? "").Contains(HmEgSnapshot.EditorManagerTypeHint, StringComparison.Ordinal)); Assert.True(appearsInCandidates || appearsInTypes, $"EditorManagerTypeHint '{HmEgSnapshot.EditorManagerTypeHint}' not found in catalog"); } }