Files
MBN_STOCK_WEBVIEW/tests/MBN_STOCK_WEBVIEW.Playout.Tests/K3dRegistrationProbeTests.cs

274 lines
10 KiB
C#

using MBN_STOCK_WEBVIEW.Playout.Interop;
using MBN_STOCK_WEBVIEW.Playout.Registration;
namespace MBN_STOCK_WEBVIEW.Playout.Tests;
public sealed class K3dRegistrationProbeTests
{
private const string TypeLibraryPath = "C:\\vendor-secret\\typelib-x64.dll";
private const string EnginePath = "C:\\vendor-secret\\engine-x64.dll";
private const string EventHandlerPath = "C:\\vendor-secret\\events-x64.dll";
[Fact]
public void Probe_WithReciprocalAmd64MachineRegistration_IsReady()
{
var binaries = ReadyBinaries();
var probe = CreateProbe(ReadySnapshot(), binaries);
var report = probe.Probe();
Assert.True(report.IsReady);
Assert.Equal(K3dRegistrationIssue.None, report.Issues);
Assert.True(report.IsEngineClassReady);
Assert.True(report.IsEventHandlerClassReady);
Assert.Equal(
new[] { TypeLibraryPath, EnginePath, EventHandlerPath },
binaries.InspectedPaths);
}
[Fact]
public void Probe_WhenEitherDirectionOfReciprocalMappingDiffers_FailsClosed()
{
var snapshot = ReadySnapshot() with
{
Engine = ReadySnapshot().Engine with
{
ClassProgId = "Unexpected.Engine.1",
ProgIdClassId = K3dComConstants.KaEventHandlerClassId
},
EventHandler = ReadySnapshot().EventHandler with
{
ProgIdClassId = K3dComConstants.KaEngineClassId
}
};
var probe = CreateProbe(snapshot, ReadyBinaries());
var report = probe.Probe();
Assert.False(report.IsReady);
Assert.True(report.Issues.HasFlag(K3dRegistrationIssue.EngineProgIdMismatch));
Assert.True(report.Issues.HasFlag(K3dRegistrationIssue.EngineProgIdClassIdMismatch));
Assert.True(report.Issues.HasFlag(K3dRegistrationIssue.EventHandlerProgIdClassIdMismatch));
Assert.False(report.IsEngineClassReady);
Assert.False(report.IsEventHandlerClassReady);
AssertSafe(report);
}
[Fact]
public void Probe_WhenEitherClassServerIsNotAmd64_FailsClosed()
{
var binaries = ReadyBinaries();
binaries.Set(EnginePath, exists: true, isAmd64: false);
binaries.Set(EventHandlerPath, exists: true, isAmd64: false);
var probe = CreateProbe(ReadySnapshot(), binaries);
var report = probe.Probe();
Assert.False(report.IsReady);
Assert.True(report.Issues.HasFlag(K3dRegistrationIssue.EngineServerIsNotAmd64));
Assert.True(report.Issues.HasFlag(K3dRegistrationIssue.EventHandlerServerIsNotAmd64));
Assert.False(report.IsEngineClassReady);
Assert.False(report.IsEventHandlerClassReady);
AssertSafe(report);
}
[Fact]
public void Probe_WhenCurrentUserOverrideExists_FailsClosedWithoutDetails()
{
var probe = CreateProbe(
ReadySnapshot() with { CurrentUserOverridePresent = true },
ReadyBinaries());
var report = probe.Probe();
Assert.False(report.IsReady);
Assert.True(report.Issues.HasFlag(K3dRegistrationIssue.CurrentUserOverridePresent));
Assert.False(report.IsEngineClassReady);
Assert.False(report.IsEventHandlerClassReady);
Assert.Contains("재정의", report.Message, StringComparison.Ordinal);
AssertSafe(report);
}
[Fact]
public void CurrentUserOverrideBoundary_CoversBothClassesProgIdsAndTypeLibrary()
{
Assert.Equal(
new[]
{
$"CLSID\\{K3dComConstants.KaEngineClassId}",
$"CLSID\\{K3dComConstants.KaEventHandlerClassId}",
K3dComConstants.KaEngineProgId,
K3dComConstants.KaEventHandlerProgId,
$"TypeLib\\{K3dComConstants.TypeLibraryId}"
},
WindowsK3dRegistrySnapshotSource.CurrentUserOverridePaths);
}
[Fact]
public void Probe_WhenRegistrySnapshotCannotBeRead_ReturnsSafeFailure()
{
var probe = new K3dRegistrationProbe(
new ThrowingSnapshotSource(),
ReadyBinaries());
var report = probe.Probe();
Assert.False(report.IsReady);
Assert.Equal(K3dRegistrationIssue.RegistryReadFailed, report.Issues);
AssertSafe(report);
}
[Fact]
public void Probe_WhenProcessOrTypeLibraryIsNotX64_FailsClosed()
{
var snapshot = ReadySnapshot() with
{
Is64BitProcess = false,
Win64TypeLibraryPath = null
};
var probe = CreateProbe(snapshot, ReadyBinaries());
var report = probe.Probe();
Assert.False(report.IsReady);
Assert.True(report.Issues.HasFlag(K3dRegistrationIssue.ProcessIsNot64Bit));
Assert.True(report.Issues.HasFlag(K3dRegistrationIssue.TypeLibraryMissing));
Assert.True(report.Issues.HasFlag(K3dRegistrationIssue.Win64BinaryMissing));
AssertSafe(report);
}
[Fact]
public void Probe_WhenClassThreadingAndTypeLibraryDiffer_FailsClosed()
{
var snapshot = ReadySnapshot() with
{
Engine = ReadySnapshot().Engine with
{
ThreadingModel = "Both",
TypeLibraryId = K3dComConstants.KaEventHandlerClassId
},
EventHandler = ReadySnapshot().EventHandler with
{
ThreadingModel = "Free",
TypeLibraryId = K3dComConstants.KaEngineClassId
}
};
var probe = CreateProbe(snapshot, ReadyBinaries());
var report = probe.Probe();
Assert.False(report.IsReady);
Assert.True(report.Issues.HasFlag(K3dRegistrationIssue.EngineThreadingModelMismatch));
Assert.True(report.Issues.HasFlag(K3dRegistrationIssue.EngineTypeLibraryMismatch));
Assert.True(report.Issues.HasFlag(K3dRegistrationIssue.EventHandlerThreadingModelMismatch));
Assert.True(report.Issues.HasFlag(K3dRegistrationIssue.EventHandlerTypeLibraryMismatch));
Assert.False(report.IsEngineClassReady);
Assert.False(report.IsEventHandlerClassReady);
AssertSafe(report);
}
[Fact]
public void Probe_WhenEitherClassRegistrationIsMissing_FailsClosed()
{
var snapshot = ReadySnapshot() with
{
Engine = ReadySnapshot().Engine with { ClassKeyPresent = false },
EventHandler = ReadySnapshot().EventHandler with { ClassKeyPresent = false }
};
var probe = CreateProbe(snapshot, ReadyBinaries());
var report = probe.Probe();
Assert.False(report.IsReady);
Assert.True(report.Issues.HasFlag(K3dRegistrationIssue.EngineClassMissing));
Assert.True(report.Issues.HasFlag(K3dRegistrationIssue.EventHandlerClassMissing));
Assert.False(report.IsEngineClassReady);
Assert.False(report.IsEventHandlerClassReady);
AssertSafe(report);
}
private static K3dRegistrationProbe CreateProbe(
K3dRegistrySnapshot snapshot,
FakeBinaryInspector binaries) =>
new(new FakeSnapshotSource(snapshot), binaries);
private static K3dRegistrySnapshot ReadySnapshot() => new(
Is64BitProcess: true,
Win64TypeLibraryPath: TypeLibraryPath,
Engine: new K3dClassRegistrationSnapshot(
ClassKeyPresent: true,
ClassProgId: K3dComConstants.KaEngineProgId,
ProgIdClassId: K3dComConstants.KaEngineClassId,
InprocServerPath: EnginePath,
ThreadingModel: K3dComConstants.ApartmentThreadingModel,
TypeLibraryId: K3dComConstants.TypeLibraryId),
EventHandler: new K3dClassRegistrationSnapshot(
ClassKeyPresent: true,
ClassProgId: K3dComConstants.KaEventHandlerProgId,
ProgIdClassId: K3dComConstants.KaEventHandlerClassId,
InprocServerPath: EventHandlerPath,
ThreadingModel: K3dComConstants.ApartmentThreadingModel,
TypeLibraryId: K3dComConstants.TypeLibraryId),
CurrentUserOverridePresent: false);
private static FakeBinaryInspector ReadyBinaries() => new FakeBinaryInspector()
.Set(TypeLibraryPath, exists: true, isAmd64: true)
.Set(EnginePath, exists: true, isAmd64: true)
.Set(EventHandlerPath, exists: true, isAmd64: true);
private static void AssertSafe(K3dRegistrationReport report)
{
Assert.DoesNotContain(TypeLibraryPath, report.Message, StringComparison.OrdinalIgnoreCase);
Assert.DoesNotContain(EnginePath, report.Message, StringComparison.OrdinalIgnoreCase);
Assert.DoesNotContain(EventHandlerPath, report.Message, StringComparison.OrdinalIgnoreCase);
Assert.DoesNotContain("HKEY", report.Message, StringComparison.OrdinalIgnoreCase);
Assert.DoesNotContain("CLSID\\", report.Message, StringComparison.OrdinalIgnoreCase);
}
private sealed class FakeSnapshotSource : IK3dRegistrySnapshotSource
{
private readonly K3dRegistrySnapshot _snapshot;
public FakeSnapshotSource(K3dRegistrySnapshot snapshot)
{
_snapshot = snapshot;
}
public K3dRegistrySnapshot Capture() => _snapshot;
}
private sealed class ThrowingSnapshotSource : IK3dRegistrySnapshotSource
{
public K3dRegistrySnapshot Capture() =>
throw new UnauthorizedAccessException("HKEY secret registry detail");
}
private sealed class FakeBinaryInspector : IPeBinaryInspector
{
private readonly Dictionary<string, PeBinaryInspection> _inspections =
new(StringComparer.OrdinalIgnoreCase);
private readonly List<string> _inspectedPaths = [];
public IReadOnlyList<string> InspectedPaths => _inspectedPaths;
public FakeBinaryInspector Set(string path, bool exists, bool isAmd64)
{
_inspections[path] = new PeBinaryInspection(exists, isAmd64);
return this;
}
public PeBinaryInspection Inspect(string? path)
{
if (path is null)
{
return new PeBinaryInspection(false, false);
}
_inspectedPaths.Add(path);
return _inspections.TryGetValue(path, out var inspection)
? inspection
: new PeBinaryInspection(false, false);
}
}
}