274 lines
10 KiB
C#
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);
|
|
}
|
|
}
|
|
}
|