From 7d2fd1ab65c20505869c6673b0dd446168b184fe Mon Sep 17 00:00:00 2001 From: Jon Kazama Date: Tue, 16 Jun 2026 19:53:39 +0200 Subject: [PATCH] selftest: add on-disk SelfTestReport log; report PASS/FAIL details from steps --- .../SelfTests/GlobalStyleScopeAllocStep.cs | 24 +++++++----- HellionChat/SelfTests/SelfTestReport.cs | 39 +++++++++++++++++++ 2 files changed, 53 insertions(+), 10 deletions(-) create mode 100644 HellionChat/SelfTests/SelfTestReport.cs diff --git a/HellionChat/SelfTests/GlobalStyleScopeAllocStep.cs b/HellionChat/SelfTests/GlobalStyleScopeAllocStep.cs index 06df8cf..94fe490 100644 --- a/HellionChat/SelfTests/GlobalStyleScopeAllocStep.cs +++ b/HellionChat/SelfTests/GlobalStyleScopeAllocStep.cs @@ -45,16 +45,20 @@ internal sealed class GlobalStyleScopeAllocStep : ISelfTestStep GlobalStyleScope.Push(theme, registry, opacity).Dispose(); var delta = GC.GetAllocatedBytesForCurrentThread() - before; - if (delta > AllocBudgetBytes) - { - ImGui.Text( - $"GlobalStyleScope.Push allocated {delta} bytes/cycle " - + $"(budget {AllocBudgetBytes}) — StackHandle is not GC-free." - ); - return SelfTestStepResult.Fail; - } - - return SelfTestStepResult.Pass; + // Report the measured figure on BOTH outcomes (Flo's request: don't just + // show Pass) — the byte delta is the whole point of the GC-reserve probe. + var ok = delta <= AllocBudgetBytes; + var status = ok ? "PASS" : "FAIL"; + SelfTestReport.Append( + Name, + status, + new[] { $"Push/Dispose allocated {delta} bytes/cycle (budget {AllocBudgetBytes})" } + ); + ImGui.Text( + $"GlobalStyleScope.Push allocated {delta} bytes/cycle " + + $"(budget {AllocBudgetBytes}) — {status}." + ); + return ok ? SelfTestStepResult.Pass : SelfTestStepResult.Fail; } public void CleanUp() { } diff --git a/HellionChat/SelfTests/SelfTestReport.cs b/HellionChat/SelfTests/SelfTestReport.cs new file mode 100644 index 0000000..07d7bc5 --- /dev/null +++ b/HellionChat/SelfTests/SelfTestReport.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Text; + +namespace HellionChat.SelfTests; + +// Shared report sink so manual self-test steps leave a readable trace on disk, +// not just a green Pass that flashes by for a single frame. Each call appends a +// timestamped block to selftest-report.log in the plugin ConfigDirectory; the +// human reads the tail after running the self-test runner. Same idea as the B5 +// perf-baseline.json (durable, copy-pasteable output) but as one shared append +// log so a full run leaves every reporting step's findings in one place. +// Append (not atomic tmp+move) is fine: the runner is single-threaded on the +// draw thread and a torn trailing line on a crash is acceptable for a debug log. +internal static class SelfTestReport +{ + internal static string Append(string stepName, string status, IReadOnlyList details) + { + var path = Path.Join(Plugin.Interface.ConfigDirectory.FullName, "selftest-report.log"); + + var stamp = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture); + var sb = new StringBuilder(); + sb.Append("=== ") + .Append(stamp) + .Append(" | ") + .Append(stepName) + .Append(" | ") + .Append(status) + .Append(" ===\n"); + foreach (var line in details) + sb.Append(" ").Append(line).Append('\n'); + sb.Append('\n'); + + File.AppendAllText(path, sb.ToString()); + return path; + } +}