Browse Source

Adapt DecompilerViewTests to the shared test helpers

This fixture was missed by both the helper-extraction and visual-breakpoint
sweeps, so it still carried 16 copies of the boot prologue and no captures.
Route it through TestHarness.BootAsync / TreeNavigation and add a Step after
each decompile, matching every other headless fixture.

Assisted-by: Claude:claude-opus-4-8:Claude Code
pull/3755/head
Siegfried Pammer 1 month ago
parent
commit
be4625fbf6
  1. 225
      ILSpy.Tests/Editor/DecompilerViewTests.cs

225
ILSpy.Tests/Editor/DecompilerViewTests.cs

@ -50,22 +50,19 @@ public class DecompilerViewTests
// resulting C# (signature + body) must land in the document tab. Also verifies the // resulting C# (signature + body) must land in the document tab. Also verifies the
// row scrolls into view (CenteredInView assertion) so the user sees the selection. // row scrolls into view (CenteredInView assertion) so the user sees the selection.
// Arrange — boot the window, wait for assemblies, expand Enumerable, locate AsEnumerable. // Arrange — boot, expand Enumerable, locate AsEnumerable.
var window = AppComposition.Current.GetExport<MainWindow>(); var (_, vm) = await TestHarness.BootAsync(3);
window.Show();
var vm = (MainWindowViewModel)window.DataContext!;
await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3);
var typeNode = vm.AssemblyTreeModel.FindNode<TypeTreeNode>( var typeNode = vm.AssemblyTreeModel.FindNode<TypeTreeNode>(
"System.Linq", "System.Linq", "System.Linq.Enumerable"); "System.Linq", "System.Linq", "System.Linq.Enumerable").Expand();
typeNode.IsExpanded = true; var methodNode = typeNode.GetChild<MethodTreeNode>(m => m.MethodDefinition.Name == "AsEnumerable");
var methodNode = typeNode.Children.OfType<MethodTreeNode>()
.Single(m => m.MethodDefinition.Name == "AsEnumerable");
methodNode.MethodDefinition.IsExtensionMethod.Should().BeTrue(); methodNode.MethodDefinition.IsExtensionMethod.Should().BeTrue();
// Act — select the method node and wait for the decompile. // Act — select the method node and wait for the decompile.
vm.AssemblyTreeModel.SelectNode(methodNode); vm.AssemblyTreeModel.SelectNode(methodNode);
TestCapture.Step("selected-asenumerable");
var tab = await vm.DockWorkspace.WaitForDecompiledTextAsync(); var tab = await vm.DockWorkspace.WaitForDecompiledTextAsync();
TestCapture.Step("asenumerable-decompiled");
// Assert — signature + body fragments are present and the row centred in the tree. // Assert — signature + body fragments are present and the row centred in the tree.
tab.Text.Should().Contain("AsEnumerable"); tab.Text.Should().Contain("AsEnumerable");
@ -82,19 +79,18 @@ public class DecompilerViewTests
// summary listing (target framework, runtime pack, referenced assemblies, load log) // summary listing (target framework, runtime pack, referenced assemblies, load log)
// rather than decompiled C#. // rather than decompiled C#.
// Arrange — boot, wait for assemblies, expand the System.Linq assembly node. // Arrange — boot, expand the System.Linq assembly node, grab its References folder.
var window = AppComposition.Current.GetExport<MainWindow>(); var (_, vm) = await TestHarness.BootAsync(3);
window.Show();
var vm = (MainWindowViewModel)window.DataContext!;
await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3);
var assemblyNode = vm.AssemblyTreeModel.FindNode<AssemblyTreeNode>("System.Linq"); var assemblyNode = vm.AssemblyTreeModel.FindNode<AssemblyTreeNode>("System.Linq").Expand();
assemblyNode.IsExpanded = true; TestCapture.Step("expanded-system-linq");
var refFolder = assemblyNode.Children.OfType<ReferenceFolderTreeNode>().Single(); var refFolder = assemblyNode.GetChild<ReferenceFolderTreeNode>();
// Act — select the References folder and wait for its decompile output. // Act — select the References folder and wait for its decompile output.
vm.AssemblyTreeModel.SelectNode(refFolder); vm.AssemblyTreeModel.SelectNode(refFolder);
TestCapture.Step("selected-references");
var tab = await vm.DockWorkspace.WaitForDecompiledTextAsync(); var tab = await vm.DockWorkspace.WaitForDecompiledTextAsync();
TestCapture.Step("references-listing");
// Assert — every header and a representative referenced assembly land in the listing. // Assert — every header and a representative referenced assembly land in the listing.
tab.Text.Should().Contain("Detected TargetFramework-Id:"); tab.Text.Should().Contain("Detected TargetFramework-Id:");
@ -110,19 +106,17 @@ public class DecompilerViewTests
// Selecting a TypeTreeNode (System.Version) must decompile the full class declaration // Selecting a TypeTreeNode (System.Version) must decompile the full class declaration
// including representative members. // including representative members.
// Arrange — boot, wait for assemblies, find System.Version. // Arrange — boot, find System.Version.
var window = AppComposition.Current.GetExport<MainWindow>(); var (_, vm) = await TestHarness.BootAsync(3);
window.Show();
var vm = (MainWindowViewModel)window.DataContext!;
await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3);
var coreLib = typeof(object).Assembly.GetName().Name!;
var typeNode = vm.AssemblyTreeModel.FindNode<TypeTreeNode>( var typeNode = vm.AssemblyTreeModel.FindNode<TypeTreeNode>(
coreLib, "System", "System.Version"); TreeNavigation.CoreLibName, "System", "System.Version");
// Act — select the type node, wait for the decompile. // Act — select the type node, wait for the decompile.
vm.AssemblyTreeModel.SelectNode(typeNode); vm.AssemblyTreeModel.SelectNode(typeNode);
TestCapture.Step("selected-version");
var tab = await vm.DockWorkspace.WaitForDecompiledTextAsync(); var tab = await vm.DockWorkspace.WaitForDecompiledTextAsync();
TestCapture.Step("version-class");
// Assert — class declaration + representative property names land in the output. // Assert — class declaration + representative property names land in the output.
tab.Text.Should().Contain("class Version"); tab.Text.Should().Contain("class Version");
@ -136,22 +130,18 @@ public class DecompilerViewTests
// PropertyTreeNode → just the single property's declaration (with type prefix), not // PropertyTreeNode → just the single property's declaration (with type prefix), not
// the full enclosing class. // the full enclosing class.
// Arrange — boot, wait for assemblies, expand System.Version, grab Major. // Arrange — boot, expand System.Version, grab Major.
var window = AppComposition.Current.GetExport<MainWindow>(); var (_, vm) = await TestHarness.BootAsync(3);
window.Show();
var vm = (MainWindowViewModel)window.DataContext!;
await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3);
var coreLib = typeof(object).Assembly.GetName().Name!;
var typeNode = vm.AssemblyTreeModel.FindNode<TypeTreeNode>( var typeNode = vm.AssemblyTreeModel.FindNode<TypeTreeNode>(
coreLib, "System", "System.Version"); TreeNavigation.CoreLibName, "System", "System.Version").Expand();
typeNode.IsExpanded = true; var propertyNode = typeNode.GetChild<PropertyTreeNode>(p => p.PropertyDefinition.Name == "Major");
var propertyNode = typeNode.Children.OfType<PropertyTreeNode>()
.Single(p => p.PropertyDefinition.Name == "Major");
// Act — select the property, wait for the decompile. // Act — select the property, wait for the decompile.
vm.AssemblyTreeModel.SelectNode(propertyNode); vm.AssemblyTreeModel.SelectNode(propertyNode);
TestCapture.Step("selected-major");
var tab = await vm.DockWorkspace.WaitForDecompiledTextAsync(); var tab = await vm.DockWorkspace.WaitForDecompiledTextAsync();
TestCapture.Step("major-property");
// Assert — property declaration lands in the output. // Assert — property declaration lands in the output.
tab.Text.Should().Contain("public int Major"); tab.Text.Should().Contain("public int Major");
@ -163,22 +153,18 @@ public class DecompilerViewTests
// FieldTreeNode (System.Math.PI) → decompiled field declaration with its constant // FieldTreeNode (System.Math.PI) → decompiled field declaration with its constant
// initializer. // initializer.
// Arrange — boot, wait for assemblies, expand System.Math, grab PI. // Arrange — boot, expand System.Math, grab PI.
var window = AppComposition.Current.GetExport<MainWindow>(); var (_, vm) = await TestHarness.BootAsync(3);
window.Show();
var vm = (MainWindowViewModel)window.DataContext!;
await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3);
var coreLib = typeof(object).Assembly.GetName().Name!;
var typeNode = vm.AssemblyTreeModel.FindNode<TypeTreeNode>( var typeNode = vm.AssemblyTreeModel.FindNode<TypeTreeNode>(
coreLib, "System", "System.Math"); TreeNavigation.CoreLibName, "System", "System.Math").Expand();
typeNode.IsExpanded = true; var fieldNode = typeNode.GetChild<FieldTreeNode>(f => f.FieldDefinition.Name == "PI");
var fieldNode = typeNode.Children.OfType<FieldTreeNode>()
.Single(f => f.FieldDefinition.Name == "PI");
// Act — select the field, wait for the decompile. // Act — select the field, wait for the decompile.
vm.AssemblyTreeModel.SelectNode(fieldNode); vm.AssemblyTreeModel.SelectNode(fieldNode);
TestCapture.Step("selected-pi");
var tab = await vm.DockWorkspace.WaitForDecompiledTextAsync(); var tab = await vm.DockWorkspace.WaitForDecompiledTextAsync();
TestCapture.Step("pi-field");
// Assert — declaration and constant value both land. // Assert — declaration and constant value both land.
tab.Text.Should().Contain("public const double PI"); tab.Text.Should().Contain("public const double PI");
@ -190,22 +176,18 @@ public class DecompilerViewTests
{ {
// EventTreeNode (AppDomain.ProcessExit) → decompiled event declaration. // EventTreeNode (AppDomain.ProcessExit) → decompiled event declaration.
// Arrange — boot, wait for assemblies, expand AppDomain, grab ProcessExit. // Arrange — boot, expand AppDomain, grab ProcessExit.
var window = AppComposition.Current.GetExport<MainWindow>(); var (_, vm) = await TestHarness.BootAsync(3);
window.Show();
var vm = (MainWindowViewModel)window.DataContext!;
await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3);
var coreLib = typeof(object).Assembly.GetName().Name!;
var typeNode = vm.AssemblyTreeModel.FindNode<TypeTreeNode>( var typeNode = vm.AssemblyTreeModel.FindNode<TypeTreeNode>(
coreLib, "System", "System.AppDomain"); TreeNavigation.CoreLibName, "System", "System.AppDomain").Expand();
typeNode.IsExpanded = true; var eventNode = typeNode.GetChild<EventTreeNode>(e => e.EventDefinition.Name == "ProcessExit");
var eventNode = typeNode.Children.OfType<EventTreeNode>()
.Single(e => e.EventDefinition.Name == "ProcessExit");
// Act — select the event, wait for the decompile. // Act — select the event, wait for the decompile.
vm.AssemblyTreeModel.SelectNode(eventNode); vm.AssemblyTreeModel.SelectNode(eventNode);
TestCapture.Step("selected-processexit");
var tab = await vm.DockWorkspace.WaitForDecompiledTextAsync(); var tab = await vm.DockWorkspace.WaitForDecompiledTextAsync();
TestCapture.Step("processexit-event");
// Assert — event keyword + name land in the output. // Assert — event keyword + name land in the output.
tab.Text.Should().Contain("event EventHandler"); tab.Text.Should().Contain("event EventHandler");
@ -217,26 +199,27 @@ public class DecompilerViewTests
{ {
// Under the IL disassembler, selecting a NamespaceTreeNode lists the namespace's // Under the IL disassembler, selecting a NamespaceTreeNode lists the namespace's
// types — ILLanguage overrides DecompileNamespace to disassemble them, matching the // types — ILLanguage overrides DecompileNamespace to disassemble them, matching the
// WPF app where IL mode disassembles the namespace. // app's IL mode that disassembles the whole namespace.
// Arrange — boot, wait for assemblies, switch to IL, locate System.Runtime.Versioning. // Arrange — boot, switch to IL, locate System.Runtime.Versioning.
var window = AppComposition.Current.GetExport<MainWindow>(); var (_, vm) = await TestHarness.BootAsync(3);
window.Show();
var vm = (MainWindowViewModel)window.DataContext!;
await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3);
var languageService = AppComposition.Current.GetExport<LanguageService>(); var languageService = AppComposition.Current.GetExport<LanguageService>();
languageService.CurrentLanguage = languageService.GetLanguage("IL"); languageService.CurrentLanguage = languageService.GetLanguage("IL");
TestCapture.Step("select-il-language");
var coreLib = typeof(object).Assembly.GetName().Name!;
var namespaceNode = vm.AssemblyTreeModel.FindNode<NamespaceTreeNode>( var namespaceNode = vm.AssemblyTreeModel.FindNode<NamespaceTreeNode>(
coreLib, "System.Runtime.Versioning"); TreeNavigation.CoreLibName, "System.Runtime.Versioning");
// Act — select the namespace, wait for the decompile. // Act — select the namespace, wait for the decompile.
vm.AssemblyTreeModel.SelectNode(namespaceNode); vm.AssemblyTreeModel.SelectNode(namespaceNode);
TestCapture.Step("selected-namespace");
var tab = await vm.DockWorkspace.WaitForDecompiledTextAsync(); var tab = await vm.DockWorkspace.WaitForDecompiledTextAsync();
TestCapture.Step("namespace-il-listing");
// Assert — representative type names land in the IL output. // Assert — the document was produced by the IL language, and representative type
// names land in the IL output.
tab.Language.Name.Should().Be("IL");
tab.Text.Should().Contain("TargetFrameworkAttribute"); tab.Text.Should().Contain("TargetFrameworkAttribute");
tab.Text.Should().Contain("SupportedOSPlatformAttribute"); tab.Text.Should().Contain("SupportedOSPlatformAttribute");
} }
@ -246,25 +229,24 @@ public class DecompilerViewTests
{ {
// The C# language does NOT override DecompileNamespace, so selecting a namespace node // The C# language does NOT override DecompileNamespace, so selecting a namespace node
// falls through to the base Language.DecompileNamespace, which writes just a // falls through to the base Language.DecompileNamespace, which writes just a
// "// <namespace>" comment — not the contained types. Matches WPF, and keeps a single // "// <namespace>" comment — not the contained types. Keeps a single click from
// click from triggering a multi-second decompile of the whole namespace. // triggering a multi-second decompile of the whole namespace.
// Arrange — boot, wait for assemblies, ensure C#, locate System.Runtime.Versioning. // Arrange — boot, ensure C#, locate System.Runtime.Versioning.
var window = AppComposition.Current.GetExport<MainWindow>(); var (_, vm) = await TestHarness.BootAsync(3);
window.Show();
var vm = (MainWindowViewModel)window.DataContext!;
await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3);
var languageService = AppComposition.Current.GetExport<LanguageService>(); var languageService = AppComposition.Current.GetExport<LanguageService>();
languageService.CurrentLanguage = languageService.GetLanguage("C#"); languageService.CurrentLanguage = languageService.GetLanguage("C#");
TestCapture.Step("select-csharp-language");
var coreLib = typeof(object).Assembly.GetName().Name!;
var namespaceNode = vm.AssemblyTreeModel.FindNode<NamespaceTreeNode>( var namespaceNode = vm.AssemblyTreeModel.FindNode<NamespaceTreeNode>(
coreLib, "System.Runtime.Versioning"); TreeNavigation.CoreLibName, "System.Runtime.Versioning");
// Act — select the namespace, wait for the decompile. // Act — select the namespace, wait for the decompile.
vm.AssemblyTreeModel.SelectNode(namespaceNode); vm.AssemblyTreeModel.SelectNode(namespaceNode);
TestCapture.Step("selected-namespace");
var tab = await vm.DockWorkspace.WaitForDecompiledTextAsync(); var tab = await vm.DockWorkspace.WaitForDecompiledTextAsync();
TestCapture.Step("namespace-csharp-comment");
// Assert — only the namespace comment, none of the contained types. // Assert — only the namespace comment, none of the contained types.
tab.Text.Should().Contain("System.Runtime.Versioning"); tab.Text.Should().Contain("System.Runtime.Versioning");
@ -279,29 +261,27 @@ public class DecompilerViewTests
// the full "(version, tfm)" suffix wired up. The tab title used to lock in the bare // the full "(version, tfm)" suffix wired up. The tab title used to lock in the bare
// ShortName at that moment; it must instead update once Text becomes available. // ShortName at that moment; it must instead update once Text becomes available.
// Arrange — boot, wait for assemblies. // Arrange — boot.
var window = AppComposition.Current.GetExport<MainWindow>(); var (_, vm) = await TestHarness.BootAsync(3);
window.Show();
var vm = (MainWindowViewModel)window.DataContext!;
await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3);
// Act 1 — fire OpenCommand on a new path. OpenCommand selects the new node before its // Act 1 — fire OpenCommand on a new path. OpenCommand selects the new node before its
// rich Text (with version + tfm) settles, exercising the late-update path. // rich Text (with version + tfm) settles, exercising the late-update path. We can't use
// OpenAssemblyAsync here: it awaits the load, which would skip the pre-settle window.
var newAsmPath = typeof(System.Net.Http.HttpClient).Assembly.Location; var newAsmPath = typeof(System.Net.Http.HttpClient).Assembly.Location;
var registry = AppComposition.Current.GetExport<MainMenuCommandRegistry>(); var openCommand = AppComposition.Current.GetExport<MainMenuCommandRegistry>()
var openCommand = registry.Commands .GetCommand(nameof(Resources._Open));
.Single(c => c.Metadata.Header == nameof(Resources._Open))
.CreateExport().Value;
openCommand.Execute(newAsmPath); openCommand.Execute(newAsmPath);
await Waiters.WaitForAsync(() => await Waiters.WaitForAsync(() =>
vm.AssemblyTreeModel.SelectedItem is AssemblyTreeNode n vm.AssemblyTreeModel.SelectedItem is AssemblyTreeNode n
&& string.Equals(n.LoadedAssembly.FileName, newAsmPath, System.StringComparison.OrdinalIgnoreCase)); && string.Equals(n.LoadedAssembly.FileName, newAsmPath, System.StringComparison.OrdinalIgnoreCase));
TestCapture.Step("assembly-selected");
// Act 2 — wait for the rich form of LoadedAssembly.Text to materialise. // Act 2 — wait for the rich form of LoadedAssembly.Text to materialise.
var node = (AssemblyTreeNode)vm.AssemblyTreeModel.SelectedItem!; var node = (AssemblyTreeNode)vm.AssemblyTreeModel.SelectedItem!;
await node.LoadedAssembly.GetLoadResultAsync(); await node.LoadedAssembly.GetLoadResultAsync();
var tab = await vm.DockWorkspace.WaitForDecompiledTextAsync(); var tab = await vm.DockWorkspace.WaitForDecompiledTextAsync();
TestCapture.Step("opened-assembly");
// Assert — once Text is rich, the tab title catches up. Sanity-check the form is rich // Assert — once Text is rich, the tab title catches up. Sanity-check the form is rich
// (otherwise the test isn't exercising the late-update path). // (otherwise the test isn't exercising the late-update path).
@ -318,28 +298,29 @@ public class DecompilerViewTests
// spinner glyphs so the user sees activity. The editor's Text must NOT receive the // spinner glyphs so the user sees activity. The editor's Text must NOT receive the
// glyph (only the title bar). Once decompilation finishes, the glyph clears. // glyph (only the title bar). Once decompilation finishes, the glyph clears.
// Arrange — boot, wait for assemblies, run an initial decompile so the second selection // Arrange — boot, run an initial decompile so the second selection produces a real
// produces a real "in flight" period (not the cold-start one). // "in flight" period (not the cold-start one).
var window = AppComposition.Current.GetExport<MainWindow>(); var (_, vm) = await TestHarness.BootAsync(3);
window.Show();
var vm = (MainWindowViewModel)window.DataContext!;
await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3);
var firstNode = vm.AssemblyTreeModel.FindNode<AssemblyTreeNode>("System.Linq"); var firstNode = vm.AssemblyTreeModel.FindNode<AssemblyTreeNode>("System.Linq");
vm.AssemblyTreeModel.SelectNode(firstNode); vm.AssemblyTreeModel.SelectNode(firstNode);
var tab = await vm.DockWorkspace.WaitForDecompiledTextAsync(); var tab = await vm.DockWorkspace.WaitForDecompiledTextAsync();
TestCapture.Step("first-decompile");
var titles = new List<string>(); var titles = new List<string>();
var editorTexts = new List<string>(); var editorTexts = new List<string>();
tab.PropertyChanged += OnTabPropertyChanged; tab.PropertyChanged += OnTabPropertyChanged;
// Act — kick off a second decompile and observe Title/Text changes throughout. // Act — kick off a second decompile and observe Title/Text changes throughout. No
var coreLib = typeof(object).Assembly.GetName().Name!; // breakpoint inside the observation window: a capture's RunJobs could perturb the
var second = vm.AssemblyTreeModel.FindNode<TypeTreeNode>(coreLib, "System", "System.Version"); // in-flight title/text transitions this test is asserting on.
var second = vm.AssemblyTreeModel.FindNode<TypeTreeNode>(
TreeNavigation.CoreLibName, "System", "System.Version");
vm.AssemblyTreeModel.SelectNode(second); vm.AssemblyTreeModel.SelectNode(second);
await vm.DockWorkspace.WaitForDecompiledTextAsync(); await vm.DockWorkspace.WaitForDecompiledTextAsync();
tab.PropertyChanged -= OnTabPropertyChanged; tab.PropertyChanged -= OnTabPropertyChanged;
TestCapture.Step("after-second-decompile");
// Assert — at least one captured title contained a spinner glyph; no editor Text ever // Assert — at least one captured title contained a spinner glyph; no editor Text ever
// did; the final title is glyph-free. // did; the final title is glyph-free.
@ -366,15 +347,11 @@ public class DecompilerViewTests
// Selecting several nodes via Ctrl+Click feeds them all into one decompile. The tab // Selecting several nodes via Ctrl+Click feeds them all into one decompile. The tab
// title must list every selected node's display text comma-joined. // title must list every selected node's display text comma-joined.
// Arrange — boot, wait for assemblies, expand Enumerable, grab two methods. // Arrange — boot, expand Enumerable, grab two methods.
var window = AppComposition.Current.GetExport<MainWindow>(); var (_, vm) = await TestHarness.BootAsync(3);
window.Show();
var vm = (MainWindowViewModel)window.DataContext!;
await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3);
var typeNode = vm.AssemblyTreeModel.FindNode<TypeTreeNode>( var typeNode = vm.AssemblyTreeModel.FindNode<TypeTreeNode>(
"System.Linq", "System.Linq", "System.Linq.Enumerable"); "System.Linq", "System.Linq", "System.Linq.Enumerable").Expand();
typeNode.IsExpanded = true;
var asEnumerable = typeNode.Children.OfType<MethodTreeNode>() var asEnumerable = typeNode.Children.OfType<MethodTreeNode>()
.First(m => m.MethodDefinition.Name == "AsEnumerable"); .First(m => m.MethodDefinition.Name == "AsEnumerable");
var empty = typeNode.Children.OfType<MethodTreeNode>() var empty = typeNode.Children.OfType<MethodTreeNode>()
@ -384,8 +361,10 @@ public class DecompilerViewTests
vm.AssemblyTreeModel.SelectedItems.Clear(); vm.AssemblyTreeModel.SelectedItems.Clear();
vm.AssemblyTreeModel.SelectedItems.Add(asEnumerable); vm.AssemblyTreeModel.SelectedItems.Add(asEnumerable);
vm.AssemblyTreeModel.SelectedItems.Add(empty); vm.AssemblyTreeModel.SelectedItems.Add(empty);
TestCapture.Step("multi-selected");
var tab = await vm.DockWorkspace.WaitForDecompiledTextAsync(); var tab = await vm.DockWorkspace.WaitForDecompiledTextAsync();
TestCapture.Step("multi-selection-title");
// Assert — title is the two display texts joined with ", ". // Assert — title is the two display texts joined with ", ".
tab.Title.Should().Be($"{asEnumerable.Text}, {empty.Text}"); tab.Title.Should().Be($"{asEnumerable.Text}, {empty.Text}");
@ -397,15 +376,11 @@ public class DecompilerViewTests
// Multi-selection isn't just a title concern — both methods' bodies must actually be // Multi-selection isn't just a title concern — both methods' bodies must actually be
// decompiled into one combined document. // decompiled into one combined document.
// Arrange — boot, wait for assemblies, expand Enumerable, grab two methods. // Arrange — boot, expand Enumerable, grab two methods.
var window = AppComposition.Current.GetExport<MainWindow>(); var (_, vm) = await TestHarness.BootAsync(3);
window.Show();
var vm = (MainWindowViewModel)window.DataContext!;
await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3);
var typeNode = vm.AssemblyTreeModel.FindNode<TypeTreeNode>( var typeNode = vm.AssemblyTreeModel.FindNode<TypeTreeNode>(
"System.Linq", "System.Linq", "System.Linq.Enumerable"); "System.Linq", "System.Linq", "System.Linq.Enumerable").Expand();
typeNode.IsExpanded = true;
var asEnumerable = typeNode.Children.OfType<MethodTreeNode>() var asEnumerable = typeNode.Children.OfType<MethodTreeNode>()
.First(m => m.MethodDefinition.Name == "AsEnumerable"); .First(m => m.MethodDefinition.Name == "AsEnumerable");
var empty = typeNode.Children.OfType<MethodTreeNode>() var empty = typeNode.Children.OfType<MethodTreeNode>()
@ -415,8 +390,10 @@ public class DecompilerViewTests
vm.AssemblyTreeModel.SelectedItems.Clear(); vm.AssemblyTreeModel.SelectedItems.Clear();
vm.AssemblyTreeModel.SelectedItems.Add(asEnumerable); vm.AssemblyTreeModel.SelectedItems.Add(asEnumerable);
vm.AssemblyTreeModel.SelectedItems.Add(empty); vm.AssemblyTreeModel.SelectedItems.Add(empty);
TestCapture.Step("multi-selected");
var tab = await vm.DockWorkspace.WaitForDecompiledTextAsync(); var tab = await vm.DockWorkspace.WaitForDecompiledTextAsync();
TestCapture.Step("multi-selection-decompiled");
// Assert — both names AND a body-fragment from one of them confirm both bodies // Assert — both names AND a body-fragment from one of them confirm both bodies
// actually rendered (not just signatures). // actually rendered (not just signatures).
@ -431,18 +408,16 @@ public class DecompilerViewTests
// AssemblyTreeNode → emit a "manifest"-style summary header (file, identity, runtime, // AssemblyTreeNode → emit a "manifest"-style summary header (file, identity, runtime,
// architecture, global type) plus the assembly-level attributes block. // architecture, global type) plus the assembly-level attributes block.
// Arrange — boot, wait for assemblies, find System.Linq. // Arrange — boot, find System.Linq.
var window = AppComposition.Current.GetExport<MainWindow>(); var (_, vm) = await TestHarness.BootAsync(3);
window.Show();
var vm = (MainWindowViewModel)window.DataContext!;
await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3);
var assemblyNode = vm.AssemblyTreeModel.FindNode<AssemblyTreeNode>("System.Linq"); var assemblyNode = vm.AssemblyTreeModel.FindNode<AssemblyTreeNode>("System.Linq");
// Act — select the assembly node, wait for the decompile. // Act — select the assembly node, wait for the decompile.
vm.AssemblyTreeModel.SelectNode(assemblyNode); vm.AssemblyTreeModel.SelectNode(assemblyNode);
TestCapture.Step("selected-assembly");
var tab = await vm.DockWorkspace.WaitForDecompiledTextAsync(); var tab = await vm.DockWorkspace.WaitForDecompiledTextAsync();
TestCapture.Step("assembly-manifest");
// Assert — every documented header line and at least one assembly attribute land. // Assert — every documented header line and at least one assembly attribute land.
tab.Text.Should().Contain("// " + assemblyNode.LoadedAssembly.FileName); tab.Text.Should().Contain("// " + assemblyNode.LoadedAssembly.FileName);
@ -460,12 +435,9 @@ public class DecompilerViewTests
// DecompilerTextView.ApplyDocument must wire AvaloniaEdit's XmlFoldingStrategy so // DecompilerTextView.ApplyDocument must wire AvaloniaEdit's XmlFoldingStrategy so
// multi-line elements become collapsible. // multi-line elements become collapsible.
// Arrange — boot, wait for assemblies, seed the decompiler tab with an assembly view // Arrange — boot, seed the decompiler tab with an assembly view so the document/editor
// so the document/editor wiring is realised. // wiring is realised.
var window = AppComposition.Current.GetExport<MainWindow>(); var (_, vm) = await TestHarness.BootAsync(3);
window.Show();
var vm = (MainWindowViewModel)window.DataContext!;
await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3);
var assemblyNode = vm.AssemblyTreeModel.FindNode<AssemblyTreeNode>("System.Linq"); var assemblyNode = vm.AssemblyTreeModel.FindNode<AssemblyTreeNode>("System.Linq");
vm.AssemblyTreeModel.SelectNode(assemblyNode); vm.AssemblyTreeModel.SelectNode(assemblyNode);
@ -477,6 +449,7 @@ public class DecompilerViewTests
var view = new DecompilerTextView { DataContext = tab }; var view = new DecompilerTextView { DataContext = tab };
var host = new global::Avalonia.Controls.Window { Content = view, Width = 600, Height = 400 }; var host = new global::Avalonia.Controls.Window { Content = view, Width = 600, Height = 400 };
host.Show(); host.Show();
host.Capture("assembly-in-host");
// Act — drop a synthetic XML resource into the tab. XmlResourceEntryNode triggers // Act — drop a synthetic XML resource into the tab. XmlResourceEntryNode triggers
// SyntaxExtension=".xml" and the XmlFoldingStrategy install. // SyntaxExtension=".xml" and the XmlFoldingStrategy install.
@ -491,6 +464,7 @@ public class DecompilerViewTests
global::Avalonia.Threading.Dispatcher.UIThread.RunJobs(); global::Avalonia.Threading.Dispatcher.UIThread.RunJobs();
await Task.Delay(20); await Task.Delay(20);
} }
host.Capture("xml-folding");
// Assert — FoldingManager is installed (private field, reflected) and produced fold // Assert — FoldingManager is installed (private field, reflected) and produced fold
// ranges for the multi-line elements. // ranges for the multi-line elements.
@ -524,20 +498,16 @@ public class DecompilerViewTests
System.Threading.Tasks.TaskScheduler.UnobservedTaskException += handler; System.Threading.Tasks.TaskScheduler.UnobservedTaskException += handler;
try try
{ {
var window = AppComposition.Current.GetExport<MainWindow>(); var (_, vm) = await TestHarness.BootAsync(3);
window.Show();
var vm = (MainWindowViewModel)window.DataContext!; var method = vm.AssemblyTreeModel.FindNode<TypeTreeNode>(
await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3); "System.Linq", "System.Linq", "System.Linq.Enumerable")
.Expand().GetChild<MethodTreeNode>(m => m.MethodDefinition.Name == "AsEnumerable");
var typeNode = vm.AssemblyTreeModel.FindNode<TypeTreeNode>(
"System.Linq", "System.Linq", "System.Linq.Enumerable");
typeNode.IsExpanded = true;
var method = typeNode.Children.OfType<MethodTreeNode>()
.First(m => m.MethodDefinition.Name == "AsEnumerable");
vm.AssemblyTreeModel.SelectNode(method); vm.AssemblyTreeModel.SelectNode(method);
var tab = await vm.DockWorkspace.WaitForDecompiledTextAsync(); var tab = await vm.DockWorkspace.WaitForDecompiledTextAsync();
tab.Foldings.Should().NotBeNull( tab.Foldings.Should().NotBeNull(
"C# decompile of a method body should produce at least one fold"); "C# decompile of a method body should produce at least one fold");
TestCapture.Step("csharp-method");
// Switch the language — the buggy path was here. // Switch the language — the buggy path was here.
var languageService = AppComposition.Current.GetExport<LanguageService>(); var languageService = AppComposition.Current.GetExport<LanguageService>();
@ -546,6 +516,7 @@ public class DecompilerViewTests
languageService.CurrentLanguage = blockIL; languageService.CurrentLanguage = blockIL;
await vm.DockWorkspace.WaitForDecompiledTextAsync(); await vm.DockWorkspace.WaitForDecompiledTextAsync();
TestCapture.Step("ilast-method");
// Force GC to flush any unobserved Task faults that escaped via the dispatcher. // Force GC to flush any unobserved Task faults that escaped via the dispatcher.
System.GC.Collect(); System.GC.Collect();

Loading…
Cancel
Save