Fensalir/tests/Aquarium.LocalCast.Tests/LocalCastNativeVisualFrameSourceTests.cs
2026-05-19 10:50:43 +01:00

141 lines
4.7 KiB
C#

using System.Numerics;
using Aquarium.LocalCast;
namespace Aquarium.LocalCast.Tests;
public sealed class LocalCastNativeVisualFrameSourceTests
{
[Fact]
public void ReadsLatestRenderPacketFromNativeRuntimeThroughInjectedDecoder()
{
var sample = new LocalCastNativeSampleHandle
{
SensorIdHash = 11,
TimestampNs = 1_000,
ArrivalNs = 1_010,
Sequence = 7,
PayloadHandle = 99,
Flags = LocalCastNativeSampleFlags.None,
};
var runtime = new FakeNativeRuntime(sample, renderPacketCount: 2);
var source = new LocalCastNativeVisualFrameSource(
runtime,
(LocalCastNativeSampleHandle handle, out LocalCastVisualFrame frame) =>
{
frame = FrameFromPayload(handle.PayloadHandle, handle.TimestampNs);
return true;
});
Assert.True(source.TryReadLatest(out var frame));
Assert.Equal(99, frame.FrameId);
Assert.Equal(1_000, frame.SourceTimeMaxNs);
Assert.Equal(1, runtime.ViewReadCount);
}
[Fact]
public void RejectsMissingOrNonLiveRenderPacket()
{
var diagnostic = new LocalCastNativeSampleHandle
{
SensorIdHash = 11,
TimestampNs = 1_000,
ArrivalNs = 1_010,
Sequence = 7,
PayloadHandle = 99,
Flags = LocalCastNativeSampleFlags.Diagnostic,
};
var source = new LocalCastNativeVisualFrameSource(
new FakeNativeRuntime(diagnostic, renderPacketCount: 1),
(LocalCastNativeSampleHandle _, out LocalCastVisualFrame frame) =>
{
frame = FrameFromPayload(1, 1);
return true;
});
Assert.False(source.TryReadLatest(out _));
var empty = new LocalCastNativeVisualFrameSource(
new FakeNativeRuntime(diagnostic, renderPacketCount: 0),
(LocalCastNativeSampleHandle _, out LocalCastVisualFrame frame) =>
{
frame = FrameFromPayload(1, 1);
return true;
});
Assert.False(empty.TryReadLatest(out _));
}
private static LocalCastVisualFrame FrameFromPayload(ulong payloadHandle, ulong timestampNs)
{
return new LocalCastVisualFrame
{
SchemaVersion = LocalCastVisualStateReader.RenderFrameSchemaId,
FrameId = (long)payloadHandle,
CreatedMonotonicNs = (long)timestampNs,
SourceTimeMinNs = (long)timestampNs,
SourceTimeMaxNs = (long)timestampNs,
PresentTimeNs = (long)timestampNs,
AudioAlignmentTimeNs = (long)timestampNs,
SpoutSenderName = "LocalCastBridge Point Cloud",
TargetWidth = 1920,
TargetHeight = 1080,
Points =
[
new LocalCastVisualPoint(
"native:point",
new Vector3(0.1f, 0.2f, 1.2f),
0.03f,
new Vector4(0.7f, 0.6f, 0.5f, 0.9f),
0.8f,
(long)timestampNs),
],
};
}
private sealed class FakeNativeRuntime : ILocalCastNativeRuntime
{
private readonly LocalCastNativeSampleHandle sample;
private readonly UIntPtr renderPacketCount;
public FakeNativeRuntime(LocalCastNativeSampleHandle sample, int renderPacketCount)
{
this.sample = sample;
this.renderPacketCount = (UIntPtr)renderPacketCount;
}
public int ViewReadCount { get; private set; }
public bool TryGetStatus(out LocalCastNativeRuntimeStatus status)
{
status = new LocalCastNativeRuntimeStatus
{
TotalSampleCount = renderPacketCount,
RenderPacketCount = renderPacketCount,
};
return true;
}
public bool TryReadSample(nuint index, out LocalCastNativeSampleKind kind, out LocalCastNativeSampleHandle sample)
{
kind = LocalCastNativeSampleKind.RenderPacket;
sample = this.sample;
return true;
}
public bool TryReadViewSample(LocalCastNativeSampleKind kind, nuint index, out LocalCastNativeSampleHandle sample)
{
ViewReadCount++;
sample = this.sample;
return kind == LocalCastNativeSampleKind.RenderPacket && index + 1 == renderPacketCount;
}
public bool TryReadLatestForSensor(LocalCastNativeSampleKind kind, ulong sensorIdHash, out LocalCastNativeSampleHandle sample)
{
sample = this.sample;
return true;
}
public void Dispose()
{
}
}
}