XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

XFESystem

【Rust】XFE系统,基于Rust和汇编

公开
关注 0 Fork 0 Star 0
UTF-8
using System.Diagnostics;
using XFESystem.Kernel;
using Xunit;

namespace XFESystem.Desktop.Platform.Tests;

public sealed class NativePointerCaptureTests
{
    private static void Drain() { NativePointerCapture.Prepare(); while (NativePointerCapture.TryRead(out _)) { } }
    private static void Observe(int x, int y, bool down, int wheel = 0, long ticks = 0) => NativePointerCapture.Observe(x, y, down, false, false, wheel, ticks);

    [Fact]
    public void SlowFramePreservesBothMouseMakeAndBreakBeforeTheNextDrain()
    {
        Drain(); NativePointerCapture.Prepare(); long now = NativePointerCapture.ReadTimestamp();
        Observe(300, 200, true, ticks: now); Observe(300, 200, false, ticks: now + Stopwatch.Frequency * 80 / 1000);
        Assert.True(NativePointerCapture.TryRead(out var make)); Assert.True(make.LeftButton); Assert.False(make.LostReports);
        Assert.True(NativePointerCapture.TryRead(out var released)); Assert.False(released.LeftButton); Assert.True(released.TimestampTicks > make.TimestampTicks);
        Assert.Equal((300, 200), (make.X, make.Y)); Assert.False(NativePointerCapture.TryRead(out _));
    }

    [Fact]
    public void MotionWheelAndZeroWheelPacketsRemainOrderedWithoutWheelOverwrite()
    {
        Drain(); Observe(100, 200, false); Observe(120, 240, false, 3); Observe(122, 245, false, 0); Observe(122, 245, false, -1);
        var actual = new List<NativePointerReport>(); while (NativePointerCapture.TryRead(out var report)) actual.Add(report);
        Assert.Equal(new[] { 100, 120, 122, 122 }, actual.Select(r => r.X)); Assert.Equal(new[] { 0, 3, 0, -1 }, actual.Select(r => r.Wheel));
        Assert.Equal(2, actual.Sum(r => r.Wheel));
    }

    [Fact]
    public void MultipleButtonsAndEachTransitionArePreserved()
    {
        Drain(); NativePointerCapture.Observe(1, 2, true, true, false, 0, 11);
        NativePointerCapture.Observe(1, 2, false, true, true, 0, 12);
        Assert.True(NativePointerCapture.TryRead(out var first)); Assert.True(first.LeftButton); Assert.True(first.RightButton); Assert.False(first.MiddleButton);
        Assert.True(NativePointerCapture.TryRead(out var second)); Assert.False(second.LeftButton); Assert.True(second.RightButton); Assert.True(second.MiddleButton);
    }

    [Fact]
    public void OverflowDiscardsAmbiguousBatchAndReturnsLossWithoutFabricatingRelease()
    {
        Drain(); Observe(3, 4, true);
        for (int i = 0; i < NativePointerCapture.Capacity; i++) Observe(i, i, true, 1, i);
        Observe(5, 6, false);
        Assert.True(NativePointerCapture.TryRead(out var loss)); Assert.True(loss.LostReports); Assert.Equal(0, loss.Wheel); Assert.Equal(0, loss.TimestampTicks);
        Assert.False(NativePointerCapture.TryRead(out _));
        Observe(7, 8, true, 0, 100); Observe(7, 8, false, 0, 101);
        Assert.True(NativePointerCapture.TryRead(out var make)); Assert.False(make.LostReports); Assert.True(make.LeftButton);
        Assert.True(NativePointerCapture.TryRead(out var released)); Assert.False(released.LostReports); Assert.False(released.LeftButton);
    }

    [Fact]
    public void RingWrapRetainsButtonsAndPacketTimestamps()
    {
        Drain();
        for (int i = 0; i < NativePointerCapture.Capacity * 3; i++)
        {
            Observe(i, i + 1, (i & 1) == 0, i % 3 - 1, i);
            Assert.True(NativePointerCapture.TryRead(out var report)); Assert.Equal(i, report.X); Assert.Equal(i + 1, report.Y);
            Assert.Equal((i & 1) == 0, report.LeftButton); Assert.Equal(i, report.TimestampTicks); Assert.False(report.LostReports);
        }
    }

    [Fact]
    public void InitializedObserveAndDrainAllocateNoManagedObjects()
    {
        Drain(); NativePointerCapture.Prepare(); Observe(1, 1, false); NativePointerCapture.TryRead(out _);
        long before = GC.GetAllocatedBytesForCurrentThread();
        for (int i = 0; i < 1000; i++) { Observe(i, i, (i & 1) == 0); NativePointerCapture.TryRead(out _); }
        Assert.Equal(before, GC.GetAllocatedBytesForCurrentThread());
    }
    [Fact]
    public void UnpreparedInterruptObservationAndTimestampDoNotInitializeOrAllocateTheRing()
    {
        Drain(); var field = typeof(NativePointerCapture).GetField("reports", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static)!;
        var buffer = field.GetValue(null); field.SetValue(null, null);
        long before = GC.GetAllocatedBytesForCurrentThread();
        for (int i = 0; i < 1000; i++) { Observe(i, i, true); _ = NativePointerCapture.ReadTimestamp(); NativePointerCapture.TryRead(out _); }
        long after = GC.GetAllocatedBytesForCurrentThread(); Assert.Equal(before, after); Assert.Null(field.GetValue(null)); Assert.Equal(0, NativePointerCapture.ReadTimestamp());
        field.SetValue(null, buffer);
    }
}