diff options
Diffstat (limited to 'src/Godosa.Core.Tests/Diagnostics')
4 files changed, 247 insertions, 0 deletions
diff --git a/src/Godosa.Core.Tests/Diagnostics/DebugFlagsTests.cs b/src/Godosa.Core.Tests/Diagnostics/DebugFlagsTests.cs new file mode 100644 index 0000000..c67042e --- /dev/null +++ b/src/Godosa.Core.Tests/Diagnostics/DebugFlagsTests.cs @@ -0,0 +1,59 @@ +using Godosa.Core.Diagnostics; +using Xunit; + +namespace Godosa.Core.Tests.Diagnostics; + +public class DebugFlagsTests +{ + [Fact] + public void Categories_AreCommaSeparated_Trimmed_CaseInsensitive() + { + var flags = new DebugFlags(" input , AUDIO"); + Assert.True(flags.On("input")); + Assert.True(flags.On("audio")); + Assert.False(flags.On("frames")); + } + + [Fact] + public void All_TurnsOnEveryCategory_ButNotExactSwitches() + { + var flags = new DebugFlags("all"); + Assert.True(flags.On("script")); + Assert.False(flags.Exact("nohud")); + } + + [Fact] + public void Exact_MatchesOnlyTheNamedSwitch() + { + var flags = new DebugFlags("frames,NoHud"); + Assert.True(flags.Exact("nohud")); + Assert.False(flags.Exact("input")); + } + + [Fact] + public void MissingOrEmpty_TurnsNothingOn() + { + Assert.False(new DebugFlags(null).On("input")); + Assert.False(new DebugFlags(" , ,").On("input")); + } + + [Fact] + public void EngineDebug_Apply_SetsEachLibCategory() + { + try + { + EngineDebug.Apply(new DebugFlags("script")); + Assert.True(EngineDebug.Script); + Assert.False(EngineDebug.Input); + Assert.False(EngineDebug.Audio); + EngineDebug.Apply(new DebugFlags("input,audio")); + Assert.True(EngineDebug.Input); + Assert.True(EngineDebug.Audio); + Assert.False(EngineDebug.Script); + } + finally + { + EngineDebug.Apply(new DebugFlags(null)); + } + } +} 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); + } +} diff --git a/src/Godosa.Core.Tests/Diagnostics/GpuAndCsvTests.cs b/src/Godosa.Core.Tests/Diagnostics/GpuAndCsvTests.cs new file mode 100644 index 0000000..46cf5ed --- /dev/null +++ b/src/Godosa.Core.Tests/Diagnostics/GpuAndCsvTests.cs @@ -0,0 +1,53 @@ +using System.Globalization; +using System.Threading; +using Godosa.Core.Diagnostics; +using Xunit; + +namespace Godosa.Core.Tests.Diagnostics; + +public class GpuAndCsvTests +{ + [Fact] + public void GpuSamples_AverageSeparatelyFromFrames() + { + var stats = new FrameTimeStats(capacity: 4); + stats.Record(0.016, 0.002, 1, 0.0001); + Assert.Equal(0, stats.Summarize().GpuSamples); + Assert.Equal(0.0, stats.Summarize().AvgGpuMs); + + stats.RecordGpu(0.001); + stats.RecordGpu(0.003); + + var s = stats.Summarize(); + Assert.Equal(2, s.GpuSamples); + Assert.Equal(2.0, s.AvgGpuMs, precision: 9); + } + + [Fact] + public void GpuSamples_RollOverTheWindow() + { + var stats = new FrameTimeStats(capacity: 2); + stats.RecordGpu(0.010); + stats.RecordGpu(0.001); + stats.RecordGpu(0.003); + + Assert.Equal(2.0, stats.Summarize().AvgGpuMs, precision: 9); + } + + [Fact] + public void CsvRow_IsInvariantCulture_MsWithThreeDecimals_GpuBlankWhenUnknown() + { + var previous = Thread.CurrentThread.CurrentCulture; + Thread.CurrentThread.CurrentCulture = new CultureInfo("de-DE"); // comma decimals must not leak in + try + { + Assert.Equal("t_s,frame_ms,work_ms,sim_ticks,sim_ms,gpu_ms", PerfCsv.Header()); + Assert.Equal("12.500,16.667,2.000,1,0.100,1.250", PerfCsv.Row(12.5, 0.0166667, 0.002, 1, 0.0001, 0.00125)); + Assert.Equal("0.000,16.667,2.000,0,0.000,", PerfCsv.Row(0, 0.0166667, 0.002, 0, 0, null)); + } + finally + { + Thread.CurrentThread.CurrentCulture = previous; + } + } +} diff --git a/src/Godosa.Core.Tests/Diagnostics/PerfCsvTests.cs b/src/Godosa.Core.Tests/Diagnostics/PerfCsvTests.cs new file mode 100644 index 0000000..fd64778 --- /dev/null +++ b/src/Godosa.Core.Tests/Diagnostics/PerfCsvTests.cs @@ -0,0 +1,38 @@ +using System.Globalization; +using Godosa.Core.Diagnostics; +using Xunit; + +namespace Godosa.Core.Tests.Diagnostics; + +public class PerfCsvTests +{ + [Fact] + public void Header_AppendsCallerColumnsInOrder() + { + Assert.Equal("t_s,frame_ms,work_ms,sim_ticks,sim_ms,gpu_ms", PerfCsv.Header()); + Assert.Equal("t_s,frame_ms,work_ms,sim_ticks,sim_ms,gpu_ms,draw_calls,triangles", + PerfCsv.Header("draw_calls", "triangles")); + } + + [Fact] + public void Row_AppendsCallerValuesInOrder() + { + Assert.Equal("1.500,16.000,4.000,2,1.000,,12,2.500,", + PerfCsv.Row(1.5, 0.016, 0.004, 2, 0.001, null, "12", PerfCsv.Ms(0.0025), PerfCsv.Ms(null))); + } + + [Fact] + public void Ms_IsInvariant_ThreeDecimals() + { + var previous = Thread.CurrentThread.CurrentCulture; + try + { + Thread.CurrentThread.CurrentCulture = CultureInfo.GetCultureInfo("de-DE"); + Assert.Equal("12.500", PerfCsv.Ms(0.0125)); + } + finally + { + Thread.CurrentThread.CurrentCulture = previous; + } + } +} |
