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
To audit what each UI test actually exercises, every step now snapshots the
live window to <TestFixtureName>/<TestName>_<NN>_<ShortDescription>.png: a
booted frame (emitted automatically by TestHarness.BootAsync), one after each
state-changing action, and one before each assertion. Flip ILSPY_TESTS_VISIBLE=1
to render the filmstrip; it lands under %TEMP%/ilspy-test-captures (overridable
via ILSPY_TEST_CAPTURES).
The step number and fixture/test name are derived automatically so inserting a
breakpoint never renumbers the rest. The identity is recorded up front from the
real ITest in an ITestAction hook rather than read live: NUnit's
TestContext.CurrentContext does not flow onto async continuations, so a capture
after an await would otherwise collide under the ad-hoc context. And when
rendering is off the whole call is a true no-op -- not even a dispatcher pump --
so instrumenting a test can never perturb the navigation/tab timing it asserts
on. Full headless suite stays green.
Nearly every headless UI test opened with the same four-line prologue
(resolve the shared MainWindow, show it, cast its DataContext, wait for the
assembly list), then repeated the corelib lookup, the EnsureLazyChildren +
Children.OfType<T>().Single() drill, the registry single-by-header lookups,
and the open-an-assembly-and-wait dance. The duplication made the intent of
each test hard to see and every signature tweak a suite-wide edit.
Collapse those into TestHarness (BootAsync, OpenAssemblyAsync, GetCommand,
GetEntry) and TreeNavigation extensions (FindCoreLib, GetChild<T>, Expand<T>),
then apply them across the suite. Net ~865 lines of boilerplate removed with
no change in behaviour; the full headless suite stays green.
Every_ExportToolbarCommand_Resolves_An_Icon used GetField against
ILSpy.Avalonia.Images.Images to verify every [ExportToolbarCommand]'s
ToolbarIcon resolves to a live IImage. Images.Images currently
declares its icons as static readonly fields, but a future refactor
could lazify them into properties (we explored that path earlier in
this session before reverting). Accept either FieldInfo or
PropertyInfo so the test keeps holding under that refactor.
Assisted-by: Claude:claude-opus-4-7:Claude Code
Imports the three SVG assets that were missing from `ILSpy.Avalonia/Assets/Icons/`
and registers them in `Images/Images.cs`, so the MEF-injected toolbar buttons
for "Reload all assemblies", "Sort assembly list by name", and "Collapse all
tree nodes" now render their icons instead of falling back to the tooltip text
(`MainToolBar.axaml.cs:226` `ResolveIcon` returned null for these). Adds a
regression test asserting every `[ExportToolbarCommand]`'s `ToolbarIcon`
resolves to a real `IImage` field on the registry.
Two missing toolbar buttons (Manage Assembly Lists, Show Search) and an
incorrect button order; the layout now mirrors WPF MainToolBar.xaml +
InitToolbar exactly. Also fixes an InitializeButtons no-op under
Avalonia.Headless (Loaded never fired; switched to AttachedToVisualTree).
Assisted-by: Claude:claude-opus-4-7:Claude Code