From c644776a67485ff12ba3c951efc63c75ca4eb5d2 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Wed, 1 Jul 2026 12:15:49 +0200 Subject: [PATCH] Assert debug-step replay highlights are precise, not just present The replay tests only checked DebugStepHighlight was non-null, which the ancestor fallback satisfies unconditionally -- a regression widening every highlight to the enclosing method would have passed. Assert instead that the range lies in bounds, does not span the whole document, and (unless it is a zero-length removal caret) covers non-whitespace rendered code. Assisted-by: Claude:claude-opus-4-8:Claude Code --- ILSpy.Tests/Views/DebugStepsTests.cs | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/ILSpy.Tests/Views/DebugStepsTests.cs b/ILSpy.Tests/Views/DebugStepsTests.cs index 8e0dc076c..7f7ddff13 100644 --- a/ILSpy.Tests/Views/DebugStepsTests.cs +++ b/ILSpy.Tests/Views/DebugStepsTests.cs @@ -177,13 +177,13 @@ public class DebugStepsTests await tab.RestartDecompileWithStepLimit(replayStep.BeginStep, isDebug: false, replayStep.BeginStep); tab.Text.Should().NotBeNullOrWhiteSpace("C# replay before a selected AST mutation step must still emit code"); - tab.DebugStepHighlight.Should().NotBeNull("C# replay before a selected AST mutation step must locate the changed node"); + AssertPreciseHighlight(tab, "C# replay before a selected AST mutation step must locate the changed node"); debugStepsVm.Steps.Should().BeSameAs(collectedSteps, "a step-limited C# replay must not replace the full step tree shown by the pane"); await tab.RestartDecompileWithStepLimit(replayStep.EndStep, isDebug: false, replayStep.BeginStep); tab.Text.Should().NotBeNullOrWhiteSpace("C# replay after a selected AST mutation step must still emit code"); - tab.DebugStepHighlight.Should().NotBeNull("C# replay after a selected AST mutation step must locate the changed node"); + AssertPreciseHighlight(tab, "C# replay after a selected AST mutation step must locate the changed node"); debugStepsVm.Steps.Should().BeSameAs(collectedSteps, "a step-limited C# replay must preserve the current full-run step tree and selection context"); @@ -229,13 +229,13 @@ public class DebugStepsTests await tab.RestartDecompileWithStepLimit(replayStep!.BeginStep, isDebug: false, replayStep.BeginStep); tab.Text.Should().NotBeNullOrWhiteSpace("ILAst replay before a selected step must still emit IL"); - tab.DebugStepHighlight.Should().NotBeNull("ILAst replay before a selected step must locate the changed instruction"); + AssertPreciseHighlight(tab, "ILAst replay before a selected step must locate the changed instruction"); debugStepsVm.Steps.Should().BeSameAs(collectedSteps, "a step-limited ILAst replay must not replace the full step tree shown by the pane"); await tab.RestartDecompileWithStepLimit(replayStep.EndStep, isDebug: false, replayStep.BeginStep); tab.Text.Should().NotBeNullOrWhiteSpace("ILAst replay after a selected step must still emit IL"); - tab.DebugStepHighlight.Should().NotBeNull("ILAst replay after a selected step must locate the changed instruction"); + AssertPreciseHighlight(tab, "ILAst replay after a selected step must locate the changed instruction"); // The first leaf step that acts on a concrete instruction; a step whose Position is null // (e.g. an empty transform group) has nothing to highlight and is not what a user replays. @@ -391,6 +391,26 @@ public class DebugStepsTests return Task.CompletedTask; } + // A replay highlight must land on the changed node, not merely be non-null: in bounds, not a + // flood of the whole document, and (unless it is a zero-length removal caret) on rendered code + // rather than whitespace. This is what keeps the ancestor fallback from silently widening every + // highlight to the enclosing method undetected. + static void AssertPreciseHighlight(DecompilerTabPageModel tab, string because) + { + tab.DebugStepHighlight.Should().NotBeNull(because); + var range = tab.DebugStepHighlight!.Value; + var text = tab.Text!; + (range.Start >= 0 && range.Start + range.Length <= text.Length).Should() + .BeTrue("the debug-step highlight must lie within the emitted document"); + range.Length.Should().BeLessThan(text.Length, + "the highlight must mark a specific node, not flood the whole document"); + if (range.Length > 0) + { + text.Substring(range.Start, range.Length).Trim().Should() + .NotBeEmpty("a non-caret highlight must cover rendered code, not just whitespace"); + } + } + [AvaloniaTest] public Task MarkNodeStart_Excludes_Leading_Indentation() {