The headless UI tests synchronized with the application by pumping a fixed
number of frames (39 loops of RunJobs/Delay across 19 files) and by pressing
at a point computed once from a control's bounds. Both encode how fast the
machine that wrote the test was: on the loaded Windows Debug CI agent the
frame count comes up short and the point goes stale, which is the recurring
timeout in the tree context-menu tests and the reason each such failure was
repaired one test at a time.
Waiters.WaitForIdleAsync replaces the frame loops. It observes the actual
precondition - no dispatcher job queued at Background priority or above, no
assembly still loading in the background sweep, a frame rendered - and
requires it on two consecutive polls so a thread-pool continuation about to
post back is caught as well.
Window.ClickAsync replaces element-targeted MouseDown/MouseUp pairs. It
re-resolves the target on every poll and presses only once the window's hit
test at the click point answers with that target, reporting the point and
what was hit instead on timeout. That diagnostic exposed one vacuous test:
User_Click_On_Visible_Row_Does_Not_Recentre_Viewport clicked the centre of a
row wider than the tree viewport, which lies under the decompiler text view,
so its assertion held without the row ever being clicked. It now clamps the
point to the viewport like the other tree-row clicks.
Clicks at text positions and press-only gutter clicks stay raw; they do not
target an element.
Assisted-by: Claude:claude-fable-5:Claude Code
Hit testing for synthesized input is answered from the rendered scene, not from
the visual tree, and Dispatcher.UIThread.RunJobs() does not render one. The
context-menu gesture helpers pumped dispatcher jobs alone, so after Escape closed
a menu the next right-click could still be routed to the light-dismiss overlay of
the frame that was on screen: no ContextRequested was raised, no row became the
context target, and the assertion two lines later reported an unhighlighted row.
The macOS CI runner lost that race roughly once in forty gestures; a probe build
repeating the gesture caught a right-click that produced neither a pointer-over
nor a menu, and the fix survived 120 gestures on the same runner.
Avalonia's own headless input helpers pump the dispatcher and the render timer
together for this reason.
Assisted-by: Claude:claude-opus-5[1m]:Claude Code
The global:: prefixes existed because the test project's namespace
ICSharpCode.ILSpy.Tests used to shadow the app's old top-level ILSpy
namespace. With the UI code back under ICSharpCode.ILSpy there is
nothing left to shadow, so plain fully qualified names resolve fine.
Assisted-by: Claude:claude-fable-5:Claude Code
The Avalonia port had placed the UI app in an ILSpy.* namespace tree,
while the csproj RootNamespace and every prior release (through 10.1)
use ICSharpCode.ILSpy.*. Restoring the historical namespace reduces the
public API diff against release/10.1 for plugin authors and removes the
shadowing that forced global:: qualifiers in the test project. The
Images class and AccessOverlayIcon enum move back into the root
namespace (as in 10.1), since an ICSharpCode.ILSpy.Images namespace
would shadow the Images class for all code inside ICSharpCode.ILSpy.
Assisted-by: Claude:claude-fable-5:Claude Code
Keep the headless UI tests from timing out on the slow Windows runner:
decompile a small CoreLib type (System.Object) in the folding and
token-history tests instead of a large one, and raise the shared
decompile-wait timeout so a cold first decompile (JIT plus building a
CoreLib-scale type system) finishes in time.
Assisted-by: Claude:claude-opus-4-8:Claude Code
Tree-node selection now updates the inner Content of one persistent
ContentTabPage instead of swapping the dockable in the dock. The
wrapper view (ContentTabPageView) keeps both inner views — the
decompiler text editor and the metadata grid — pre-realised in the
visual tree from construction time and toggles which is visible based
on Content's runtime type.
Assisted-by: Claude:claude-opus-4-7:Claude Code
DOS / COFF / Optional / DataDirectories / DebugDirectory now expose
their entries through CreateTab() instead of writing a fixed-width text
table from Decompile. The grid view shows each entry's columns
reflected directly off the row shape — Phase 1's text table was a
stand-in until this view shipped. The existing PE-header tests are
rewritten to assert against MetadataTablePageModel.Items / Columns;
adds a Waiters.WaitForMetadataTabAsync helper used by every grid-tab
test.
Assisted-by: Claude:claude-opus-4-7:Claude Code
DecompileAsEnumerableTest exercises the full path: open mscorlib, navigate
to Enumerable.AsEnumerable, render the decompilation, verify text and
verify the row centred in the assembly tree. Writing it surfaced two
pre-existing bugs the manual smoke-test missed: