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

XFEEDToolBox

【WinUI】XFE精英危险工具箱

公开
关注 0 Fork 0 Star 0
UTF-8
using XFEEDToolBox.Core.Navigation;
using XFEExtension.NetCore.XUnit.Attributes;

namespace XFEEDToolBox.Test;

[TestFixture]
public class ScoHeatSampleHistoryTests
{
    private static void Near(double expected,double actual)=>Check.That(Math.Abs(expected-actual)<1e-7);

    [Test]public void FastCallbacksAccumulateEnoughRealTimeForAHeatSlope()
    {
        var history=new ScoHeatSampleHistory();
        Check.That(!history.Observe(1000,40)&&double.IsNaN(history.Slope));
        Check.That(!history.Observe(1004,40.04)&&double.IsNaN(history.Slope));
        Check.That(!history.Observe(1008,40.08)&&double.IsNaN(history.Slope));
        Check.That(history.Observe(1012,40.12));Near(10,history.Slope);
        Check.That(!history.Observe(1016,40.16));Near(10,history.Slope);
        Check.That(!history.Observe(1020,40.20));
        Check.That(history.Observe(1024,40.24));Near(10,history.Slope);
    }

    [Test]public void DuplicateTimestampDoesNotReplaceTheDerivativeBaseline()
    {
        var history=new ScoHeatSampleHistory();history.Observe(1000,40);
        Check.That(!history.Observe(1000,41));
        Check.That(history.Observe(1010,40.2));Near(20,history.Slope);
        Check.That(!history.Observe(1010,50));Near(20,history.Slope);
        Check.That(history.Observe(1030,40.4));Near(10,history.Slope);
    }

    [Test]public void ClockRollbackRequiresANewValidInterval()
    {
        var history=new ScoHeatSampleHistory();history.Observe(1000,40);history.Observe(1020,40.2);
        Check.That(!history.Observe(1019,41)&&double.IsNaN(history.Slope));
        Check.That(!history.Observe(1024,41.05)&&double.IsNaN(history.Slope));
        Check.That(history.Observe(1029,41.1));Near(10,history.Slope);
    }

    [Test]public void LongGapCannotReuseThePreviousSlope()
    {
        var history=new ScoHeatSampleHistory();history.Observe(1000,40);history.Observe(1020,40.2);
        Check.That(!history.Observe(2021,50)&&double.IsNaN(history.Slope));
        Check.That(history.Observe(2031,50.1));Near(10,history.Slope);
        Check.That(history.Observe(3031,49.1));Near(-1,history.Slope);
    }

    [Test]public void InvalidHeatAndExplicitResetRequireFreshSamples()
    {
        foreach(var heat in new[]{double.NaN,double.PositiveInfinity,-1d})
        {
            var history=new ScoHeatSampleHistory();history.Observe(1000,40);history.Observe(1020,40.2);
            Check.That(!history.Observe(1021,heat)&&double.IsNaN(history.Slope));
            Check.That(!history.Observe(1030,40.3)&&double.IsNaN(history.Slope));
            Check.That(history.Observe(1040,40.4));Near(10,history.Slope);
            history.Reset();Check.That(double.IsNaN(history.Slope));
            Check.That(!history.Observe(1050,40.5));
        }
    }

    [Test]public void DerivativePreservesRapidHeatingAndCoolingForTheGuard()
    {
        var history=new ScoHeatSampleHistory();history.Observe(1000,40);
        Check.That(history.Observe(1010,42));Near(200,history.Slope);
        Check.That(history.Observe(1030,41));Near(-50,history.Slope);
    }

    [Test]public void LowerPositiveSlopesCannotKeepAnOldPeakAliveBeyondItsWindow()
    {
        var history=new ScoHeatSampleHistory();history.Observe(1000,40);history.Observe(1010,40.2);
        Near(20,history.ConservativeSlope(1010));Check.Equal(1260ul,history.RecentPeakExpiresAt);
        history.Observe(1030,40.4);history.Observe(1240,42.5);
        Near(10,history.Slope);Near(20,history.ConservativeSlope(1240));Check.Equal(1260ul,history.RecentPeakExpiresAt);
        history.Observe(1261,42.71);Near(10,history.ConservativeSlope(1261));Check.Equal(1511ul,history.RecentPeakExpiresAt);
    }

    [Test]public void SamePhaseFrameIntervalsMeasureTheWholeSimulationHeatStep()
    {
        // A 0.63 point update across 63 ms is 10 points/s. A second read only
        // 10 ms away from an update must not be mixed into this phase's history.
        var history=new ScoHeatSampleHistory();history.Observe(1000,40);
        history.Observe(1063,40.63);history.Observe(1126,41.26);
        Near(10,history.Slope);Check.Equal(1063ul,history.SlopeStartAt);Check.Equal(1126ul,history.SlopeEndAt);
        Near(40.63,history.SlopeStartHeat);Near(41.26,history.SlopeEndHeat);
        Near(64.26,41.26+Math.Max(0,history.ConservativeSlope(1126))*2+3);
    }

    [Test]public void ExpiringHighestPeakPreservesTheNextPeakStillInsideTheWindow()
    {
        var history=new ScoHeatSampleHistory();history.Observe(1000,40);history.Observe(1010,40.2);
        history.Observe(1110,41.7);history.Observe(1210,42.7);history.Observe(1270,43.3);
        Near(15,history.ConservativeSlope(1270));Check.Equal(1360ul,history.RecentPeakExpiresAt);
        Near(10,history.ConservativeSlope(1361));
    }

    [Test]public void ResetAndClockDiscontinuityDiscardThePeakAndItsEvidence()
    {
        var history=new ScoHeatSampleHistory();history.Observe(1000,40);history.Observe(1010,42);
        Near(200,history.ConservativeSlope(1010));
        history.Observe(1009,42);
        Check.That(double.IsNaN(history.ConservativeSlope(1009))&&double.IsNaN(history.SlopeStartHeat));
        Check.Equal(0ul,history.RecentPeakExpiresAt);Check.Equal(0ul,history.SlopeEndAt);
    }
}