diff options
| author | godosa <godosa@godosa.eu> | 2026-10-06 23:39:36 +0200 |
|---|---|---|
| committer | godosa <godosa@godosa.eu> | 2026-10-06 23:39:36 +0200 |
| commit | 39066900773e7857faf2d02e7ed51b71d219d97d (patch) | |
| tree | ffab8e4ddd971626776c3b1f4ce1da2c186c2187 /src/Godosa.Core.Tests/Diagnostics/FrameTimeStatsTests.cs | |
| download | godosa-engine-39066900773e7857faf2d02e7ed51b71d219d97d.tar.gz godosa-engine-39066900773e7857faf2d02e7ed51b71d219d97d.zip | |
godosa-engine: initial public history
Diffstat (limited to 'src/Godosa.Core.Tests/Diagnostics/FrameTimeStatsTests.cs')
| -rw-r--r-- | src/Godosa.Core.Tests/Diagnostics/FrameTimeStatsTests.cs | 97 |
1 files changed, 97 insertions, 0 deletions
diff --git a/src/Godosa.Core.Tests/Diagnostics/FrameTimeStatsTests.cs b/src/Godosa.Core.Tests/Diagnostics/FrameTimeStatsTests.cs new file mode 100644 index 0000000..44b6758 --- /dev/null +++ b/src/Godosa.Core.Tests/Diagnostics/FrameTimeStatsTests.cs @@ -0,0 +1,97 @@ +using System; +using Godosa.Core.Diagnostics; +using Xunit; + +namespace Godosa.Core.Tests.Diagnostics; + +public class FrameTimeStatsTests +{ + /// <summary>99 smooth 10 ms frames plus one 100 ms hitch.</summary> + private static FrameTimeStats OneHitch() + { + var stats = new FrameTimeStats(capacity: 100); + for (int i = 0; i < 99; i++) + stats.Record(0.010, workSeconds: 0.002, simTicks: 0, simSeconds: 0); + stats.Record(0.100, workSeconds: 0.002, simTicks: 0, simSeconds: 0); + return stats; + } + + [Fact] + public void Fps_IsFramesOverTotalTime_NotMeanOfInstantaneousFps() + { + var s = OneHitch().Summarize(); + + Assert.Equal(100, s.Frames); + // 100 frames / 1.09 s. (Mean of per-frame FPS would be (99*100 + 10)/100 = 99.1.) + Assert.Equal(91.743, s.Fps, 3); + Assert.Equal(10.9, s.AvgMs, 6); + } + + [Fact] + public void Percentiles_UseNearestRank_AndMaxCatchesTheHitch() + { + var s = OneHitch().Summarize(); + + Assert.Equal(10.0, s.P50Ms, 6); + Assert.Equal(10.0, s.P95Ms, 6); + Assert.Equal(10.0, s.P99Ms, 6); // rank ceil(0.99*100) = 99th of 100 + Assert.Equal(100.0, s.MaxMs, 6); + } + + [Fact] + public void OnePercentLow_AveragesTheSlowestOnePercentOfFrames() + { + var s = OneHitch().Summarize(); + + Assert.Equal(10.0, s.OnePercentLowFps, 6); // worst 1 of 100 frames = 100 ms + } + + [Fact] + public void Hitches_CountFramesOverTwiceTheMedian() + { + var stats = new FrameTimeStats(capacity: 10); + foreach (var ms in new[] { 10, 10, 10, 10, 10, 20, 21, 35 }) + stats.Record(ms / 1000.0, 0, 0, 0); + + Assert.Equal(2, stats.Summarize().Hitches); // median 10 → 21 and 35 (20 is not > 20) + } + + [Fact] + public void Window_DropsOldestFrames() + { + var stats = new FrameTimeStats(capacity: 4); + foreach (var ms in new[] { 100, 100, 3, 4, 5, 6 }) + stats.Record(ms / 1000.0, 0, 0, 0); + + Span<float> intervals = stackalloc float[4]; + int n = stats.CopyIntervalsMs(intervals); + + Assert.Equal(4, n); + Assert.Equal(new float[] { 3, 4, 5, 6 }, intervals.ToArray()); + Assert.Equal(6.0, stats.Summarize().MaxMs, 4); + } + + [Fact] + public void Sim_ReportsAchievedTickRateAndCostPerTick() + { + var stats = new FrameTimeStats(capacity: 60); + for (int i = 0; i < 60; i++) // 1 s at 60 fps; a tick every other frame, 1 ms each + stats.Record(1.0 / 60.0, workSeconds: 0.004, simTicks: i % 2, simSeconds: (i % 2) * 0.001); + + var s = stats.Summarize(); + + Assert.Equal(30.0, s.SimTickRate, 3); + Assert.Equal(1.0, s.AvgTickMs, 6); + Assert.Equal(4.0, s.AvgWorkMs, 6); + } + + [Fact] + public void Empty_SummarizesToZerosWithoutDividingByZero() + { + var s = new FrameTimeStats().Summarize(); + + Assert.Equal(0, s.Frames); + Assert.Equal(0.0, s.Fps); + Assert.Equal(0.0, s.AvgTickMs); + } +} |
