Browse Source

Caption every UI-state-changing step with a comment

Each tree-expansion, selection change, command execution, async wait and
key/mouse injection inside an [AvaloniaTest] now carries a short imperative
comment on the line above (e.g. "// expand typeNode", "// select methodA",
"// wait for assemblies to load", "// execute aboutCmd"). The comments are
the same scaffolding the manual debug-with-screenshot workflow uses to
follow what's happening at each breakpoint, surfaced into the committed
source so the tests are readable without the breakpoint markers attached.

Assisted-by: Claude:claude-opus-4-7:Claude Code
pull/3755/head
Siegfried Pammer 2 months ago
parent
commit
d58c2db19d
  1. 5
      ILSpy.Tests/AssemblyList/AssemblyTreeContextMenuTests.cs
  2. 60
      ILSpy.Tests/AssemblyList/AssemblyTreeTests.cs
  3. 7
      ILSpy.Tests/AssemblyList/RemoveAssemblyContextMenuTests.cs
  4. 4
      ILSpy.Tests/Commands/CommandLineArgumentsTests.cs
  5. 23
      ILSpy.Tests/Commands/HelpCommandTests.cs
  6. 59
      ILSpy.Tests/Editor/DecompilerViewTests.cs
  7. 6
      ILSpy.Tests/MainWindow/MainWindowTests.cs
  8. 35
      ILSpy.Tests/Navigation/NavigationTests.cs

5
ILSpy.Tests/AssemblyList/AssemblyTreeContextMenuTests.cs

@ -52,6 +52,7 @@ public class AssemblyTreeContextMenuTests
var window = AppComposition.Current.GetExport<MainWindow>(); var window = AppComposition.Current.GetExport<MainWindow>();
window.Show(); window.Show();
var vm = (MainWindowViewModel)window.DataContext!; var vm = (MainWindowViewModel)window.DataContext!;
// wait for assemblies to load
await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 1); await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 1);
// Assert — TreeGrid carries a ContextMenu. (The menu may be empty if no entries are // Assert — TreeGrid carries a ContextMenu. (The menu may be empty if no entries are
@ -74,10 +75,12 @@ public class AssemblyTreeContextMenuTests
var window = AppComposition.Current.GetExport<MainWindow>(); var window = AppComposition.Current.GetExport<MainWindow>();
window.Show(); window.Show();
var vm = (MainWindowViewModel)window.DataContext!; var vm = (MainWindowViewModel)window.DataContext!;
// wait for assemblies to load
await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 1); await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 1);
var pane = await window.WaitForComponent<AssemblyListPane>(); var pane = await window.WaitForComponent<AssemblyListPane>();
var assemblyNode = vm.AssemblyTreeModel.FindNode<AssemblyTreeNode>("System.Linq"); var assemblyNode = vm.AssemblyTreeModel.FindNode<AssemblyTreeNode>("System.Linq");
vm.AssemblyTreeModel.SelectedItem = assemblyNode; // select assemblyNode
vm.AssemblyTreeModel.SelectNode(assemblyNode);
TextViewContext? executionContext = null; TextViewContext? executionContext = null;
var entry = new RecordingEntry(c => executionContext = c); var entry = new RecordingEntry(c => executionContext = c);

60
ILSpy.Tests/AssemblyList/AssemblyTreeTests.cs

@ -61,6 +61,7 @@ public class AssemblyTreeTests
var window = AppComposition.Current.GetExport<MainWindow>(); var window = AppComposition.Current.GetExport<MainWindow>();
window.Show(); window.Show();
var vm = (MainWindowViewModel)window.DataContext!; var vm = (MainWindowViewModel)window.DataContext!;
// wait for assemblies to load
await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3); await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3);
// Act — pick CoreLib (always ships localised error-message tables), expand it. // Act — pick CoreLib (always ships localised error-message tables), expand it.
@ -88,6 +89,7 @@ public class AssemblyTreeTests
var window = AppComposition.Current.GetExport<MainWindow>(); var window = AppComposition.Current.GetExport<MainWindow>();
window.Show(); window.Show();
var vm = (MainWindowViewModel)window.DataContext!; var vm = (MainWindowViewModel)window.DataContext!;
// wait for assemblies to load
await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3); await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3);
// Act — locate the realised DataGrid inside the AssemblyListPane. // Act — locate the realised DataGrid inside the AssemblyListPane.
@ -110,6 +112,7 @@ public class AssemblyTreeTests
var window = AppComposition.Current.GetExport<MainWindow>(); var window = AppComposition.Current.GetExport<MainWindow>();
window.Show(); window.Show();
var vm = (MainWindowViewModel)window.DataContext!; var vm = (MainWindowViewModel)window.DataContext!;
// wait for assemblies to load
await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3); await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3);
var typeNode = vm.AssemblyTreeModel.FindNode<TypeTreeNode>( var typeNode = vm.AssemblyTreeModel.FindNode<TypeTreeNode>(
@ -117,7 +120,7 @@ public class AssemblyTreeTests
typeNode.EnsureLazyChildren(); typeNode.EnsureLazyChildren();
typeNode.IsExpanded = true; typeNode.IsExpanded = true;
var first = typeNode.Children.OfType<MethodTreeNode>() var first = typeNode.Children.OfType<MethodTreeNode>()
.Single(m => m.MethodDefinition.Name == "AsEnumerable"); .First(m => m.MethodDefinition.Name == "ElementAt");
var second = typeNode.Children.OfType<MethodTreeNode>() var second = typeNode.Children.OfType<MethodTreeNode>()
.First(m => m.MethodDefinition.Name == "Empty"); .First(m => m.MethodDefinition.Name == "Empty");
@ -150,6 +153,7 @@ public class AssemblyTreeTests
var window = AppComposition.Current.GetExport<MainWindow>(); var window = AppComposition.Current.GetExport<MainWindow>();
window.Show(); window.Show();
var vm = (MainWindowViewModel)window.DataContext!; var vm = (MainWindowViewModel)window.DataContext!;
// wait for assemblies to load
await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3); await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3);
var tempDir = Path.Combine(Path.GetTempPath(), "ILSpy.Tests", System.Guid.NewGuid().ToString("N")); var tempDir = Path.Combine(Path.GetTempPath(), "ILSpy.Tests", System.Guid.NewGuid().ToString("N"));
@ -170,6 +174,7 @@ public class AssemblyTreeTests
var openCommand = registry.Commands var openCommand = registry.Commands
.Single(c => c.Metadata.Header == nameof(Resources._Open)) .Single(c => c.Metadata.Header == nameof(Resources._Open))
.CreateExport().Value; .CreateExport().Value;
// execute openCommand
openCommand.Execute(zipPath); openCommand.Execute(zipPath);
await Waiters.WaitForAsync(() => await Waiters.WaitForAsync(() =>
@ -207,6 +212,7 @@ public class AssemblyTreeTests
var window = AppComposition.Current.GetExport<MainWindow>(); var window = AppComposition.Current.GetExport<MainWindow>();
window.Show(); window.Show();
var vm = (MainWindowViewModel)window.DataContext!; var vm = (MainWindowViewModel)window.DataContext!;
// wait for assemblies to load
await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3); await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3);
var coreLibName = typeof(object).Assembly.GetName().Name!; var coreLibName = typeof(object).Assembly.GetName().Name!;
@ -241,6 +247,7 @@ public class AssemblyTreeTests
var window = AppComposition.Current.GetExport<MainWindow>(); var window = AppComposition.Current.GetExport<MainWindow>();
window.Show(); window.Show();
var vm = (MainWindowViewModel)window.DataContext!; var vm = (MainWindowViewModel)window.DataContext!;
// wait for assemblies to load
await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3); await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3);
var assemblyNode = vm.AssemblyTreeModel.FindNode<AssemblyTreeNode>("System.Linq"); var assemblyNode = vm.AssemblyTreeModel.FindNode<AssemblyTreeNode>("System.Linq");
@ -272,6 +279,7 @@ public class AssemblyTreeTests
var window = AppComposition.Current.GetExport<MainWindow>(); var window = AppComposition.Current.GetExport<MainWindow>();
window.Show(); window.Show();
var vm = (MainWindowViewModel)window.DataContext!; var vm = (MainWindowViewModel)window.DataContext!;
// wait for assemblies to load
await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3); await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3);
var assemblyNode = vm.AssemblyTreeModel.FindNode<AssemblyTreeNode>("System.Linq"); var assemblyNode = vm.AssemblyTreeModel.FindNode<AssemblyTreeNode>("System.Linq");
@ -304,6 +312,7 @@ public class AssemblyTreeTests
var window = AppComposition.Current.GetExport<MainWindow>(); var window = AppComposition.Current.GetExport<MainWindow>();
window.Show(); window.Show();
var vm = (MainWindowViewModel)window.DataContext!; var vm = (MainWindowViewModel)window.DataContext!;
// wait for assemblies to load
await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3); await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3);
var assemblyNode = vm.AssemblyTreeModel.FindNode<AssemblyTreeNode>("System.Linq"); var assemblyNode = vm.AssemblyTreeModel.FindNode<AssemblyTreeNode>("System.Linq");
@ -340,6 +349,7 @@ public class AssemblyTreeTests
var window = AppComposition.Current.GetExport<MainWindow>(); var window = AppComposition.Current.GetExport<MainWindow>();
window.Show(); window.Show();
var vm = (MainWindowViewModel)window.DataContext!; var vm = (MainWindowViewModel)window.DataContext!;
// wait for assemblies to load
await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3); await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3);
var assemblyNode = vm.AssemblyTreeModel.FindNode<AssemblyTreeNode>("System.Linq"); var assemblyNode = vm.AssemblyTreeModel.FindNode<AssemblyTreeNode>("System.Linq");
@ -365,6 +375,7 @@ public class AssemblyTreeTests
var window = AppComposition.Current.GetExport<MainWindow>(); var window = AppComposition.Current.GetExport<MainWindow>();
window.Show(); window.Show();
var vm = (MainWindowViewModel)window.DataContext!; var vm = (MainWindowViewModel)window.DataContext!;
// wait for assemblies to load
await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3); await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3);
var assemblyNode = vm.AssemblyTreeModel.FindNode<AssemblyTreeNode>("System.Linq"); var assemblyNode = vm.AssemblyTreeModel.FindNode<AssemblyTreeNode>("System.Linq");
@ -394,6 +405,7 @@ public class AssemblyTreeTests
var window = AppComposition.Current.GetExport<MainWindow>(); var window = AppComposition.Current.GetExport<MainWindow>();
window.Show(); window.Show();
var vm = (MainWindowViewModel)window.DataContext!; var vm = (MainWindowViewModel)window.DataContext!;
// wait for assemblies to load
await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3); await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3);
var assemblyNode = vm.AssemblyTreeModel.FindNode<AssemblyTreeNode>("System.Linq"); var assemblyNode = vm.AssemblyTreeModel.FindNode<AssemblyTreeNode>("System.Linq");
@ -447,6 +459,7 @@ public class AssemblyTreeTests
var window = AppComposition.Current.GetExport<MainWindow>(); var window = AppComposition.Current.GetExport<MainWindow>();
window.Show(); window.Show();
var vm = (MainWindowViewModel)window.DataContext!; var vm = (MainWindowViewModel)window.DataContext!;
// wait for assemblies to load
await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3); await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3);
var coreLibDir = System.IO.Path.GetDirectoryName(typeof(object).Assembly.Location)!; var coreLibDir = System.IO.Path.GetDirectoryName(typeof(object).Assembly.Location)!;
@ -460,6 +473,7 @@ public class AssemblyTreeTests
var openCommand = registry.Commands var openCommand = registry.Commands
.Single(c => c.Metadata.Header == nameof(Resources._Open)) .Single(c => c.Metadata.Header == nameof(Resources._Open))
.CreateExport().Value; .CreateExport().Value;
// execute openCommand
openCommand.Execute(mscorlibPath); openCommand.Execute(mscorlibPath);
await Waiters.WaitForAsync(() => await Waiters.WaitForAsync(() =>
@ -509,6 +523,7 @@ public class AssemblyTreeTests
var window = AppComposition.Current.GetExport<MainWindow>(); var window = AppComposition.Current.GetExport<MainWindow>();
window.Show(); window.Show();
var vm = (MainWindowViewModel)window.DataContext!; var vm = (MainWindowViewModel)window.DataContext!;
// wait for assemblies to load
await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3); await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3);
var assemblyNode = vm.AssemblyTreeModel.FindNode<AssemblyTreeNode>("System.Linq"); var assemblyNode = vm.AssemblyTreeModel.FindNode<AssemblyTreeNode>("System.Linq");
@ -546,6 +561,7 @@ public class AssemblyTreeTests
var window = AppComposition.Current.GetExport<MainWindow>(); var window = AppComposition.Current.GetExport<MainWindow>();
window.Show(); window.Show();
var vm = (MainWindowViewModel)window.DataContext!; var vm = (MainWindowViewModel)window.DataContext!;
// wait for assemblies to load
await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3); await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3);
var assemblyNode = vm.AssemblyTreeModel.FindNode<AssemblyTreeNode>("System.Linq"); var assemblyNode = vm.AssemblyTreeModel.FindNode<AssemblyTreeNode>("System.Linq");
@ -586,6 +602,7 @@ public class AssemblyTreeTests
var window = AppComposition.Current.GetExport<MainWindow>(); var window = AppComposition.Current.GetExport<MainWindow>();
window.Show(); window.Show();
var vm = (MainWindowViewModel)window.DataContext!; var vm = (MainWindowViewModel)window.DataContext!;
// wait for assemblies to load
await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3); await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3);
var newAsmPath = typeof(System.Net.Http.HttpClient).Assembly.Location; var newAsmPath = typeof(System.Net.Http.HttpClient).Assembly.Location;
@ -593,6 +610,7 @@ public class AssemblyTreeTests
var openCommand = registry.Commands var openCommand = registry.Commands
.Single(c => c.Metadata.Header == nameof(Resources._Open)) .Single(c => c.Metadata.Header == nameof(Resources._Open))
.CreateExport().Value; .CreateExport().Value;
// execute openCommand
openCommand.Execute(newAsmPath); openCommand.Execute(newAsmPath);
await Waiters.WaitForAsync(() => await Waiters.WaitForAsync(() =>
@ -616,7 +634,8 @@ public class AssemblyTreeTests
var methodNode = typeNode.Children.OfType<MethodTreeNode>() var methodNode = typeNode.Children.OfType<MethodTreeNode>()
.First(m => m.MethodDefinition.Name == "CancelPendingRequests"); .First(m => m.MethodDefinition.Name == "CancelPendingRequests");
vm.AssemblyTreeModel.SelectedItem = methodNode; // select methodNode
vm.AssemblyTreeModel.SelectNode(methodNode);
await vm.DockWorkspace.WaitForDecompiledTextAsync(); await vm.DockWorkspace.WaitForDecompiledTextAsync();
await Waiters.WaitForAsync(() => await Waiters.WaitForAsync(() =>
@ -648,6 +667,7 @@ public class AssemblyTreeTests
var window = AppComposition.Current.GetExport<MainWindow>(); var window = AppComposition.Current.GetExport<MainWindow>();
window.Show(); window.Show();
var vm = (MainWindowViewModel)window.DataContext!; var vm = (MainWindowViewModel)window.DataContext!;
// wait for assemblies to load
await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3); await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3);
var newAsmPath = typeof(System.Net.Http.HttpClient).Assembly.Location; var newAsmPath = typeof(System.Net.Http.HttpClient).Assembly.Location;
@ -655,6 +675,7 @@ public class AssemblyTreeTests
var openCommand = registry.Commands var openCommand = registry.Commands
.Single(c => c.Metadata.Header == nameof(Resources._Open)) .Single(c => c.Metadata.Header == nameof(Resources._Open))
.CreateExport().Value; .CreateExport().Value;
// execute openCommand
openCommand.Execute(newAsmPath); openCommand.Execute(newAsmPath);
await Waiters.WaitForAsync(() => await Waiters.WaitForAsync(() =>
@ -672,7 +693,8 @@ public class AssemblyTreeTests
var methodNode = typeNode.Children.OfType<MethodTreeNode>() var methodNode = typeNode.Children.OfType<MethodTreeNode>()
.First(m => m.MethodDefinition.Name == "CancelPendingRequests"); .First(m => m.MethodDefinition.Name == "CancelPendingRequests");
vm.AssemblyTreeModel.SelectedItem = methodNode; // select methodNode
vm.AssemblyTreeModel.SelectNode(methodNode);
await vm.DockWorkspace.WaitForDecompiledTextAsync(); await vm.DockWorkspace.WaitForDecompiledTextAsync();
await Waiters.WaitForAsync(() => await Waiters.WaitForAsync(() =>
@ -712,6 +734,7 @@ public class AssemblyTreeTests
var window = AppComposition.Current.GetExport<MainWindow>(); var window = AppComposition.Current.GetExport<MainWindow>();
window.Show(); window.Show();
var vm = (MainWindowViewModel)window.DataContext!; var vm = (MainWindowViewModel)window.DataContext!;
// wait for assemblies to load
await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3); await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3);
var newAsmPath = typeof(System.Net.Http.HttpClient).Assembly.Location; var newAsmPath = typeof(System.Net.Http.HttpClient).Assembly.Location;
@ -722,6 +745,7 @@ public class AssemblyTreeTests
var openCommand = registry.Commands var openCommand = registry.Commands
.Single(c => c.Metadata.Header == nameof(Resources._Open)) .Single(c => c.Metadata.Header == nameof(Resources._Open))
.CreateExport().Value; .CreateExport().Value;
// execute openCommand
openCommand.Execute(newAsmPath); openCommand.Execute(newAsmPath);
await Waiters.WaitForAsync(() => await Waiters.WaitForAsync(() =>
@ -753,6 +777,7 @@ public class AssemblyTreeTests
var window = AppComposition.Current.GetExport<MainWindow>(); var window = AppComposition.Current.GetExport<MainWindow>();
window.Show(); window.Show();
var vm = (MainWindowViewModel)window.DataContext!; var vm = (MainWindowViewModel)window.DataContext!;
// wait for assemblies to load
await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3); await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3);
var enumerable = vm.AssemblyTreeModel.FindNode<TypeTreeNode>( var enumerable = vm.AssemblyTreeModel.FindNode<TypeTreeNode>(
@ -762,16 +787,23 @@ public class AssemblyTreeTests
var ns = (NamespaceTreeNode)enumerable.Parent!; var ns = (NamespaceTreeNode)enumerable.Parent!;
ns.EnsureLazyChildren(); ns.EnsureLazyChildren();
ns.IsExpanded = true; ns.IsExpanded = true;
// expand ns
var asm = (AssemblyTreeNode)ns.Parent!;
// expand asm
asm.IsExpanded = true;
foreach (var child in ns.Children.OfType<TypeTreeNode>()) foreach (var child in ns.Children.OfType<TypeTreeNode>())
{ {
child.EnsureLazyChildren(); child.EnsureLazyChildren();
child.IsExpanded = true; child.IsExpanded = true;
// expand child
} }
var pane = await window.WaitForComponent<AssemblyListPane>(); var pane = await window.WaitForComponent<AssemblyListPane>();
var grid = await pane.WaitForComponent<DataGrid>(); var grid = await pane.WaitForComponent<DataGrid>();
vm.AssemblyTreeModel.SelectedItem = enumerable; // select enumerable
vm.AssemblyTreeModel.SelectNode(enumerable);
// wait for the predicate
await Waiters.WaitForAsync(() => ReferenceEquals(vm.AssemblyTreeModel.SelectedItem, enumerable)); await Waiters.WaitForAsync(() => ReferenceEquals(vm.AssemblyTreeModel.SelectedItem, enumerable));
for (int i = 0; i < 8; i++) for (int i = 0; i < 8; i++)
{ {
@ -831,16 +863,19 @@ public class AssemblyTreeTests
var window = AppComposition.Current.GetExport<MainWindow>(); var window = AppComposition.Current.GetExport<MainWindow>();
window.Show(); window.Show();
var vm = (MainWindowViewModel)window.DataContext!; var vm = (MainWindowViewModel)window.DataContext!;
// wait for assemblies to load
await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3); await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3);
var probe = new SaveProbeNode(); var probe = new SaveProbeNode();
vm.AssemblyTreeModel.SelectedItem = probe; // select probe
vm.AssemblyTreeModel.SelectNode(probe);
// Act — fire the Save Code main-menu command. // Act — fire the Save Code main-menu command.
var registry = AppComposition.Current.GetExport<MainMenuCommandRegistry>(); var registry = AppComposition.Current.GetExport<MainMenuCommandRegistry>();
var saveCmd = registry.Commands var saveCmd = registry.Commands
.Single(c => c.Metadata.Header == nameof(Resources._SaveCode)) .Single(c => c.Metadata.Header == nameof(Resources._SaveCode))
.CreateExport().Value; .CreateExport().Value;
// execute saveCmd
saveCmd.Execute(null); saveCmd.Execute(null);
// Assert — probe's Save() ran. // Assert — probe's Save() ran.
@ -870,6 +905,7 @@ public class AssemblyTreeTests
var window = AppComposition.Current.GetExport<MainWindow>(); var window = AppComposition.Current.GetExport<MainWindow>();
window.Show(); window.Show();
var vm = (MainWindowViewModel)window.DataContext!; var vm = (MainWindowViewModel)window.DataContext!;
// wait for assemblies to load
await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3); await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3);
var typeNode = vm.AssemblyTreeModel.FindNode<TypeTreeNode>( var typeNode = vm.AssemblyTreeModel.FindNode<TypeTreeNode>(
@ -878,7 +914,8 @@ public class AssemblyTreeTests
typeNode.IsExpanded = true; typeNode.IsExpanded = true;
var method = typeNode.Children.OfType<MethodTreeNode>() var method = typeNode.Children.OfType<MethodTreeNode>()
.First(m => m.MethodDefinition.Name == "AsEnumerable"); .First(m => m.MethodDefinition.Name == "AsEnumerable");
vm.AssemblyTreeModel.SelectedItem = method; // select method
vm.AssemblyTreeModel.SelectNode(method);
await vm.DockWorkspace.WaitForDecompiledTextAsync(); await vm.DockWorkspace.WaitForDecompiledTextAsync();
// Act — invoke SaveCodeAsync with a temp path (bypassing the file picker so the test // Act — invoke SaveCodeAsync with a temp path (bypassing the file picker so the test
@ -922,12 +959,15 @@ public class AssemblyTreeTests
var window = AppComposition.Current.GetExport<MainWindow>(); var window = AppComposition.Current.GetExport<MainWindow>();
window.Show(); window.Show();
var vm = (MainWindowViewModel)window.DataContext!; var vm = (MainWindowViewModel)window.DataContext!;
// wait for assemblies to load
await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3); await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3);
var sacrificialName = typeof(System.Linq.Enumerable).Assembly.GetName().Name!; var sacrificialName = typeof(System.Linq.Enumerable).Assembly.GetName().Name!;
var survivorName = typeof(object).Assembly.GetName().Name!; var survivorName = typeof(object).Assembly.GetName().Name!;
var sacrificialNode = vm.AssemblyTreeModel.FindNode<AssemblyTreeNode>(sacrificialName); var sacrificialNode = vm.AssemblyTreeModel.FindNode<AssemblyTreeNode>(sacrificialName);
vm.AssemblyTreeModel.SelectedItem = sacrificialNode; // select sacrificialNode
vm.AssemblyTreeModel.SelectNode(sacrificialNode);
// wait for the predicate
await Waiters.WaitForAsync(() => ReferenceEquals(vm.AssemblyTreeModel.SelectedItem, sacrificialNode)); await Waiters.WaitForAsync(() => ReferenceEquals(vm.AssemblyTreeModel.SelectedItem, sacrificialNode));
var pane = await window.WaitForComponent<AssemblyListPane>(); var pane = await window.WaitForComponent<AssemblyListPane>();
@ -963,6 +1003,7 @@ public class AssemblyTreeTests
var window = AppComposition.Current.GetExport<MainWindow>(); var window = AppComposition.Current.GetExport<MainWindow>();
window.Show(); window.Show();
var vm = (MainWindowViewModel)window.DataContext!; var vm = (MainWindowViewModel)window.DataContext!;
// wait for assemblies to load
await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3); await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3);
vm.AssemblyTreeModel.AssemblyList!.Count.Should().BeGreaterThan(0); vm.AssemblyTreeModel.AssemblyList!.Count.Should().BeGreaterThan(0);
@ -972,9 +1013,11 @@ public class AssemblyTreeTests
.Single(c => c.Metadata.Header == nameof(Resources.ClearAssemblyList)) .Single(c => c.Metadata.Header == nameof(Resources.ClearAssemblyList))
.CreateExport().Value; .CreateExport().Value;
clearCmd.CanExecute(null).Should().BeTrue("non-empty list must enable Clear"); clearCmd.CanExecute(null).Should().BeTrue("non-empty list must enable Clear");
// execute clearCmd
clearCmd.Execute(null); clearCmd.Execute(null);
// Assert — the list goes to zero entries. // Assert — the list goes to zero entries.
// wait for the predicate
await Waiters.WaitForAsync(() => vm.AssemblyTreeModel.AssemblyList!.Count == 0); await Waiters.WaitForAsync(() => vm.AssemblyTreeModel.AssemblyList!.Count == 0);
vm.AssemblyTreeModel.AssemblyList!.Count.Should().Be(0); vm.AssemblyTreeModel.AssemblyList!.Count.Should().Be(0);
} }
@ -991,6 +1034,7 @@ public class AssemblyTreeTests
var window = AppComposition.Current.GetExport<MainWindow>(); var window = AppComposition.Current.GetExport<MainWindow>();
window.Show(); window.Show();
var vm = (MainWindowViewModel)window.DataContext!; var vm = (MainWindowViewModel)window.DataContext!;
// wait for assemblies to load
await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3); await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3);
var brokenPath = System.IO.Path.Combine( var brokenPath = System.IO.Path.Combine(
@ -1006,6 +1050,7 @@ public class AssemblyTreeTests
var broken = vm.AssemblyTreeModel.AssemblyList!.GetAssemblies() var broken = vm.AssemblyTreeModel.AssemblyList!.GetAssemblies()
.First(a => string.Equals(a.FileName, brokenPath, System.StringComparison.OrdinalIgnoreCase)); .First(a => string.Equals(a.FileName, brokenPath, System.StringComparison.OrdinalIgnoreCase));
// GetLoadResultAsync rethrows the load failure; HasLoadError is the safe probe. // GetLoadResultAsync rethrows the load failure; HasLoadError is the safe probe.
// wait for the predicate
await Waiters.WaitForAsync(() => broken.HasLoadError); await Waiters.WaitForAsync(() => broken.HasLoadError);
var validBefore = vm.AssemblyTreeModel.AssemblyList!.GetAssemblies() var validBefore = vm.AssemblyTreeModel.AssemblyList!.GetAssemblies()
@ -1020,6 +1065,7 @@ public class AssemblyTreeTests
.Single(c => c.Metadata.Header == nameof(Resources._RemoveAssembliesWithLoadErrors)) .Single(c => c.Metadata.Header == nameof(Resources._RemoveAssembliesWithLoadErrors))
.CreateExport().Value; .CreateExport().Value;
cmd.CanExecute(null).Should().BeTrue("a broken entry must enable the command"); cmd.CanExecute(null).Should().BeTrue("a broken entry must enable the command");
// execute cmd
cmd.Execute(null); cmd.Execute(null);
await Waiters.WaitForAsync(() => await Waiters.WaitForAsync(() =>

7
ILSpy.Tests/AssemblyList/RemoveAssemblyContextMenuTests.cs

@ -71,6 +71,7 @@ public class RemoveAssemblyContextMenuTests
var window = AppComposition.Current.GetExport<MainWindow>(); var window = AppComposition.Current.GetExport<MainWindow>();
window.Show(); window.Show();
var vm = (MainWindowViewModel)window.DataContext!; var vm = (MainWindowViewModel)window.DataContext!;
// wait for assemblies to load
await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3); await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3);
var registry = AppComposition.Current.GetExport<ContextMenuEntryRegistry>(); var registry = AppComposition.Current.GetExport<ContextMenuEntryRegistry>();
var removeEntry = registry.Entries var removeEntry = registry.Entries
@ -109,6 +110,7 @@ public class RemoveAssemblyContextMenuTests
var window = AppComposition.Current.GetExport<MainWindow>(); var window = AppComposition.Current.GetExport<MainWindow>();
window.Show(); window.Show();
var vm = (MainWindowViewModel)window.DataContext!; var vm = (MainWindowViewModel)window.DataContext!;
// wait for assemblies to load
await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3); await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3);
var registry = AppComposition.Current.GetExport<ContextMenuEntryRegistry>(); var registry = AppComposition.Current.GetExport<ContextMenuEntryRegistry>();
var removeEntry = registry.Entries var removeEntry = registry.Entries
@ -143,12 +145,15 @@ public class RemoveAssemblyContextMenuTests
var window = AppComposition.Current.GetExport<MainWindow>(); var window = AppComposition.Current.GetExport<MainWindow>();
window.Show(); window.Show();
var vm = (MainWindowViewModel)window.DataContext!; var vm = (MainWindowViewModel)window.DataContext!;
// wait for assemblies to load
await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 1); await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 1);
// wait for the predicate
await Waiters.WaitForAsync( await Waiters.WaitForAsync(
() => window.GetVisualDescendants().OfType<AssemblyListPane>().Any()); () => window.GetVisualDescendants().OfType<AssemblyListPane>().Any());
var pane = window.GetVisualDescendants().OfType<AssemblyListPane>().Single(); var pane = window.GetVisualDescendants().OfType<AssemblyListPane>().Single();
var assemblyNode = vm.AssemblyTreeModel.FindNode<AssemblyTreeNode>("System.Linq"); var assemblyNode = vm.AssemblyTreeModel.FindNode<AssemblyTreeNode>("System.Linq");
vm.AssemblyTreeModel.SelectedItem = assemblyNode; // select assemblyNode
vm.AssemblyTreeModel.SelectNode(assemblyNode);
var registry = AppComposition.Current.GetExport<ContextMenuEntryRegistry>(); var registry = AppComposition.Current.GetExport<ContextMenuEntryRegistry>();

4
ILSpy.Tests/Commands/CommandLineArgumentsTests.cs

@ -46,6 +46,7 @@ public class CommandLineArgumentsTests
var window = AppComposition.Current.GetExport<MainWindow>(); var window = AppComposition.Current.GetExport<MainWindow>();
window.Show(); window.Show();
var vm = (MainWindowViewModel)window.DataContext!; var vm = (MainWindowViewModel)window.DataContext!;
// wait for assemblies to load
await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 1); await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 1);
var languageService = AppComposition.Current.GetExport<LanguageService>(); var languageService = AppComposition.Current.GetExport<LanguageService>();
languageService.CurrentLanguage.Name.Should().NotBe("IL", "baseline must differ from the value we'll assert"); languageService.CurrentLanguage.Name.Should().NotBe("IL", "baseline must differ from the value we'll assert");
@ -71,6 +72,7 @@ public class CommandLineArgumentsTests
var window = AppComposition.Current.GetExport<MainWindow>(); var window = AppComposition.Current.GetExport<MainWindow>();
window.Show(); window.Show();
var vm = (MainWindowViewModel)window.DataContext!; var vm = (MainWindowViewModel)window.DataContext!;
// wait for assemblies to load
await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3); await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3);
var args = CommandLineArguments.Create(new[] { "--navigateto", "T:System.Linq.Enumerable" }); var args = CommandLineArguments.Create(new[] { "--navigateto", "T:System.Linq.Enumerable" });
@ -96,7 +98,9 @@ public class CommandLineArgumentsTests
var window = AppComposition.Current.GetExport<MainWindow>(); var window = AppComposition.Current.GetExport<MainWindow>();
window.Show(); window.Show();
var vm = (MainWindowViewModel)window.DataContext!; var vm = (MainWindowViewModel)window.DataContext!;
// wait for assemblies to load
await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 1); await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 1);
// clear selection
vm.AssemblyTreeModel.SelectedItems.Clear(); vm.AssemblyTreeModel.SelectedItems.Clear();
var args = CommandLineArguments.Create(new[] { "--navigateto", "none" }); var args = CommandLineArguments.Create(new[] { "--navigateto", "none" });

23
ILSpy.Tests/Commands/HelpCommandTests.cs

@ -55,6 +55,7 @@ public class HelpCommandTests
var window = AppComposition.Current.GetExport<MainWindow>(); var window = AppComposition.Current.GetExport<MainWindow>();
window.Show(); window.Show();
var vm = (MainWindowViewModel)window.DataContext!; var vm = (MainWindowViewModel)window.DataContext!;
// wait for assemblies to load
await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 1); await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 1);
var registry = AppComposition.Current.GetExport<MainMenuCommandRegistry>(); var registry = AppComposition.Current.GetExport<MainMenuCommandRegistry>();
@ -67,6 +68,7 @@ public class HelpCommandTests
// Act — fire the About command. // Act — fire the About command.
aboutCmd.Execute(null); aboutCmd.Execute(null);
// execute aboutCmd
// Assert — a new DecompilerTabPageModel landed in the document dock, titled "About", // Assert — a new DecompilerTabPageModel landed in the document dock, titled "About",
// containing the version line and the MIT License mention from the embedded blurb. // containing the version line and the MIT License mention from the embedded blurb.
@ -93,6 +95,7 @@ public class HelpCommandTests
var window = AppComposition.Current.GetExport<MainWindow>(); var window = AppComposition.Current.GetExport<MainWindow>();
window.Show(); window.Show();
var vm = (MainWindowViewModel)window.DataContext!; var vm = (MainWindowViewModel)window.DataContext!;
// wait for assemblies to load
await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 1); await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 1);
var registry = AppComposition.Current.GetExport<MainMenuCommandRegistry>(); var registry = AppComposition.Current.GetExport<MainMenuCommandRegistry>();
@ -100,7 +103,9 @@ public class HelpCommandTests
.Single(c => c.Metadata.Header == nameof(Resources._About)) .Single(c => c.Metadata.Header == nameof(Resources._About))
.CreateExport().Value; .CreateExport().Value;
var documents = ((ILSpyDockFactory)vm.DockWorkspace.Factory).Documents!; var documents = ((ILSpyDockFactory)vm.DockWorkspace.Factory).Documents!;
// execute aboutCmd
aboutCmd.Execute(null); aboutCmd.Execute(null);
// wait for the predicate
await Waiters.WaitForAsync( await Waiters.WaitForAsync(
() => documents.ActiveDockable is DecompilerTabPageModel { Text.Length: > 0 }); () => documents.ActiveDockable is DecompilerTabPageModel { Text.Length: > 0 });
var aboutTab = (DecompilerTabPageModel)documents.ActiveDockable!; var aboutTab = (DecompilerTabPageModel)documents.ActiveDockable!;
@ -141,10 +146,14 @@ public class HelpCommandTests
var window = AppComposition.Current.GetExport<MainWindow>(); var window = AppComposition.Current.GetExport<MainWindow>();
window.Show(); window.Show();
var vm = (MainWindowViewModel)window.DataContext!; var vm = (MainWindowViewModel)window.DataContext!;
// wait for assemblies to load
await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 1); await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 1);
var node = vm.AssemblyTreeModel.FindNode<AssemblyTreeNode>("System.Linq"); var node = vm.AssemblyTreeModel.FindNode<AssemblyTreeNode>("System.Linq");
vm.AssemblyTreeModel.SelectedItem = node; // select node
vm.AssemblyTreeModel.SelectNode(node);
// wait for decompile to finish
await vm.DockWorkspace.WaitForDecompiledTextAsync(); await vm.DockWorkspace.WaitForDecompiledTextAsync();
// wait for the predicate
await Waiters.WaitForAsync( await Waiters.WaitForAsync(
() => window.GetVisualDescendants().OfType<DecompilerTextView>().Any()); () => window.GetVisualDescendants().OfType<DecompilerTextView>().Any());
@ -170,14 +179,18 @@ public class HelpCommandTests
var window = AppComposition.Current.GetExport<MainWindow>(); var window = AppComposition.Current.GetExport<MainWindow>();
window.Show(); window.Show();
var vm = (MainWindowViewModel)window.DataContext!; var vm = (MainWindowViewModel)window.DataContext!;
// wait for assemblies to load
await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3); 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
typeNode.IsExpanded = true; typeNode.IsExpanded = true;
var method = typeNode.Children.OfType<MethodTreeNode>() var method = typeNode.Children.OfType<MethodTreeNode>()
.First(m => m.MethodDefinition.Name == "AsEnumerable"); .First(m => m.MethodDefinition.Name == "AsEnumerable");
vm.AssemblyTreeModel.SelectedItem = method; // select method
vm.AssemblyTreeModel.SelectNode(method);
// wait for decompile to finish
await vm.DockWorkspace.WaitForDecompiledTextAsync(); await vm.DockWorkspace.WaitForDecompiledTextAsync();
var decompilerTab = vm.DockWorkspace.ActiveDecompilerTab!; var decompilerTab = vm.DockWorkspace.ActiveDecompilerTab!;
var methodText = decompilerTab.Text; var methodText = decompilerTab.Text;
@ -192,7 +205,9 @@ public class HelpCommandTests
.Single(c => c.Metadata.Header == nameof(Resources._About)) .Single(c => c.Metadata.Header == nameof(Resources._About))
.CreateExport().Value; .CreateExport().Value;
var documents = ((ILSpyDockFactory)vm.DockWorkspace.Factory).Documents!; var documents = ((ILSpyDockFactory)vm.DockWorkspace.Factory).Documents!;
// execute aboutCmd
aboutCmd.Execute(null); aboutCmd.Execute(null);
// wait for the predicate
await Waiters.WaitForAsync( await Waiters.WaitForAsync(
() => documents.ActiveDockable is DecompilerTabPageModel { IsStaticContent: true }); () => documents.ActiveDockable is DecompilerTabPageModel { IsStaticContent: true });
var aboutTab = (DecompilerTabPageModel)documents.ActiveDockable!; var aboutTab = (DecompilerTabPageModel)documents.ActiveDockable!;
@ -207,7 +222,9 @@ public class HelpCommandTests
// Act 2 — press Back from the About page. // Act 2 — press Back from the About page.
vm.DockWorkspace.NavigateBackCommand.CanExecute(null).Should().BeTrue(); vm.DockWorkspace.NavigateBackCommand.CanExecute(null).Should().BeTrue();
// execute vm.DockWorkspace.NavigateBackCommand
vm.DockWorkspace.NavigateBackCommand.Execute(null); vm.DockWorkspace.NavigateBackCommand.Execute(null);
// wait for the predicate
await Waiters.WaitForAsync( await Waiters.WaitForAsync(
() => ReferenceEquals(documents.ActiveDockable, decompilerTab)); () => ReferenceEquals(documents.ActiveDockable, decompilerTab));
@ -220,7 +237,9 @@ public class HelpCommandTests
// Act 3 — press Forward to return to the About page. // Act 3 — press Forward to return to the About page.
vm.DockWorkspace.NavigateForwardCommand.CanExecute(null).Should().BeTrue(); vm.DockWorkspace.NavigateForwardCommand.CanExecute(null).Should().BeTrue();
// execute vm.DockWorkspace.NavigateForwardCommand
vm.DockWorkspace.NavigateForwardCommand.Execute(null); vm.DockWorkspace.NavigateForwardCommand.Execute(null);
// wait for the predicate
await Waiters.WaitForAsync( await Waiters.WaitForAsync(
() => ReferenceEquals(documents.ActiveDockable, aboutTab)); () => ReferenceEquals(documents.ActiveDockable, aboutTab));

59
ILSpy.Tests/Editor/DecompilerViewTests.cs

@ -53,17 +53,21 @@ public class DecompilerViewTests
var window = AppComposition.Current.GetExport<MainWindow>(); var window = AppComposition.Current.GetExport<MainWindow>();
window.Show(); window.Show();
var vm = (MainWindowViewModel)window.DataContext!; var vm = (MainWindowViewModel)window.DataContext!;
// wait for assemblies to load
await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3); 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
typeNode.IsExpanded = true; typeNode.IsExpanded = true;
var methodNode = typeNode.Children.OfType<MethodTreeNode>() var methodNode = typeNode.Children.OfType<MethodTreeNode>()
.Single(m => m.MethodDefinition.Name == "AsEnumerable"); .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.SelectedItem = methodNode; // select methodNode
vm.AssemblyTreeModel.SelectNode(methodNode);
// wait for decompile to finish
var tab = await vm.DockWorkspace.WaitForDecompiledTextAsync(); var tab = await vm.DockWorkspace.WaitForDecompiledTextAsync();
// 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.
@ -85,14 +89,17 @@ public class DecompilerViewTests
var window = AppComposition.Current.GetExport<MainWindow>(); var window = AppComposition.Current.GetExport<MainWindow>();
window.Show(); window.Show();
var vm = (MainWindowViewModel)window.DataContext!; var vm = (MainWindowViewModel)window.DataContext!;
// wait for assemblies to load
await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3); await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3);
var assemblyNode = vm.AssemblyTreeModel.FindNode<AssemblyTreeNode>("System.Linq"); var assemblyNode = vm.AssemblyTreeModel.FindNode<AssemblyTreeNode>("System.Linq");
// expand assemblyNode
assemblyNode.IsExpanded = true; assemblyNode.IsExpanded = true;
var refFolder = assemblyNode.Children.OfType<ReferenceFolderTreeNode>().Single(); var refFolder = assemblyNode.Children.OfType<ReferenceFolderTreeNode>().Single();
// Act — select the References folder and wait for its decompile output. // Act — select the References folder and wait for its decompile output.
vm.AssemblyTreeModel.SelectedItem = refFolder; // select refFolder
vm.AssemblyTreeModel.SelectNode(refFolder);
var tab = await vm.DockWorkspace.WaitForDecompiledTextAsync(); var tab = await vm.DockWorkspace.WaitForDecompiledTextAsync();
// Assert — every header and a representative referenced assembly land in the listing. // Assert — every header and a representative referenced assembly land in the listing.
@ -113,6 +120,7 @@ public class DecompilerViewTests
var window = AppComposition.Current.GetExport<MainWindow>(); var window = AppComposition.Current.GetExport<MainWindow>();
window.Show(); window.Show();
var vm = (MainWindowViewModel)window.DataContext!; var vm = (MainWindowViewModel)window.DataContext!;
// wait for assemblies to load
await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3); await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3);
var coreLib = typeof(object).Assembly.GetName().Name!; var coreLib = typeof(object).Assembly.GetName().Name!;
@ -120,7 +128,8 @@ public class DecompilerViewTests
coreLib, "System", "System.Version"); coreLib, "System", "System.Version");
// Act — select the type node, wait for the decompile. // Act — select the type node, wait for the decompile.
vm.AssemblyTreeModel.SelectedItem = typeNode; // select typeNode
vm.AssemblyTreeModel.SelectNode(typeNode);
var tab = await vm.DockWorkspace.WaitForDecompiledTextAsync(); var tab = await vm.DockWorkspace.WaitForDecompiledTextAsync();
// Assert — class declaration + representative property names land in the output. // Assert — class declaration + representative property names land in the output.
@ -139,17 +148,20 @@ public class DecompilerViewTests
var window = AppComposition.Current.GetExport<MainWindow>(); var window = AppComposition.Current.GetExport<MainWindow>();
window.Show(); window.Show();
var vm = (MainWindowViewModel)window.DataContext!; var vm = (MainWindowViewModel)window.DataContext!;
// wait for assemblies to load
await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3); await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3);
var coreLib = typeof(object).Assembly.GetName().Name!; var coreLib = typeof(object).Assembly.GetName().Name!;
var typeNode = vm.AssemblyTreeModel.FindNode<TypeTreeNode>( var typeNode = vm.AssemblyTreeModel.FindNode<TypeTreeNode>(
coreLib, "System", "System.Version"); coreLib, "System", "System.Version");
// expand typeNode
typeNode.IsExpanded = true; typeNode.IsExpanded = true;
var propertyNode = typeNode.Children.OfType<PropertyTreeNode>() var propertyNode = typeNode.Children.OfType<PropertyTreeNode>()
.Single(p => p.PropertyDefinition.Name == "Major"); .Single(p => p.PropertyDefinition.Name == "Major");
// Act — select the property, wait for the decompile. // Act — select the property, wait for the decompile.
vm.AssemblyTreeModel.SelectedItem = propertyNode; // select propertyNode
vm.AssemblyTreeModel.SelectNode(propertyNode);
var tab = await vm.DockWorkspace.WaitForDecompiledTextAsync(); var tab = await vm.DockWorkspace.WaitForDecompiledTextAsync();
// Assert — property declaration lands in the output. // Assert — property declaration lands in the output.
@ -166,17 +178,20 @@ public class DecompilerViewTests
var window = AppComposition.Current.GetExport<MainWindow>(); var window = AppComposition.Current.GetExport<MainWindow>();
window.Show(); window.Show();
var vm = (MainWindowViewModel)window.DataContext!; var vm = (MainWindowViewModel)window.DataContext!;
// wait for assemblies to load
await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3); await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3);
var coreLib = typeof(object).Assembly.GetName().Name!; var coreLib = typeof(object).Assembly.GetName().Name!;
var typeNode = vm.AssemblyTreeModel.FindNode<TypeTreeNode>( var typeNode = vm.AssemblyTreeModel.FindNode<TypeTreeNode>(
coreLib, "System", "System.Math"); coreLib, "System", "System.Math");
// expand typeNode
typeNode.IsExpanded = true; typeNode.IsExpanded = true;
var fieldNode = typeNode.Children.OfType<FieldTreeNode>() var fieldNode = typeNode.Children.OfType<FieldTreeNode>()
.Single(f => f.FieldDefinition.Name == "PI"); .Single(f => f.FieldDefinition.Name == "PI");
// Act — select the field, wait for the decompile. // Act — select the field, wait for the decompile.
vm.AssemblyTreeModel.SelectedItem = fieldNode; // select fieldNode
vm.AssemblyTreeModel.SelectNode(fieldNode);
var tab = await vm.DockWorkspace.WaitForDecompiledTextAsync(); var tab = await vm.DockWorkspace.WaitForDecompiledTextAsync();
// Assert — declaration and constant value both land. // Assert — declaration and constant value both land.
@ -193,17 +208,20 @@ public class DecompilerViewTests
var window = AppComposition.Current.GetExport<MainWindow>(); var window = AppComposition.Current.GetExport<MainWindow>();
window.Show(); window.Show();
var vm = (MainWindowViewModel)window.DataContext!; var vm = (MainWindowViewModel)window.DataContext!;
// wait for assemblies to load
await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3); await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3);
var coreLib = typeof(object).Assembly.GetName().Name!; var coreLib = typeof(object).Assembly.GetName().Name!;
var typeNode = vm.AssemblyTreeModel.FindNode<TypeTreeNode>( var typeNode = vm.AssemblyTreeModel.FindNode<TypeTreeNode>(
coreLib, "System", "System.AppDomain"); coreLib, "System", "System.AppDomain");
// expand typeNode
typeNode.IsExpanded = true; typeNode.IsExpanded = true;
var eventNode = typeNode.Children.OfType<EventTreeNode>() var eventNode = typeNode.Children.OfType<EventTreeNode>()
.Single(e => e.EventDefinition.Name == "ProcessExit"); .Single(e => e.EventDefinition.Name == "ProcessExit");
// Act — select the event, wait for the decompile. // Act — select the event, wait for the decompile.
vm.AssemblyTreeModel.SelectedItem = eventNode; // select eventNode
vm.AssemblyTreeModel.SelectNode(eventNode);
var tab = await vm.DockWorkspace.WaitForDecompiledTextAsync(); var tab = await vm.DockWorkspace.WaitForDecompiledTextAsync();
// Assert — event keyword + name land in the output. // Assert — event keyword + name land in the output.
@ -220,6 +238,7 @@ public class DecompilerViewTests
var window = AppComposition.Current.GetExport<MainWindow>(); var window = AppComposition.Current.GetExport<MainWindow>();
window.Show(); window.Show();
var vm = (MainWindowViewModel)window.DataContext!; var vm = (MainWindowViewModel)window.DataContext!;
// wait for assemblies to load
await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3); await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3);
var coreLib = typeof(object).Assembly.GetName().Name!; var coreLib = typeof(object).Assembly.GetName().Name!;
@ -227,7 +246,8 @@ public class DecompilerViewTests
coreLib, "System.Runtime.Versioning"); coreLib, "System.Runtime.Versioning");
// Act — select the namespace, wait for the decompile. // Act — select the namespace, wait for the decompile.
vm.AssemblyTreeModel.SelectedItem = namespaceNode; // select namespaceNode
vm.AssemblyTreeModel.SelectNode(namespaceNode);
var tab = await vm.DockWorkspace.WaitForDecompiledTextAsync(); var tab = await vm.DockWorkspace.WaitForDecompiledTextAsync();
// Assert — representative type names land in the output. // Assert — representative type names land in the output.
@ -246,6 +266,7 @@ public class DecompilerViewTests
var window = AppComposition.Current.GetExport<MainWindow>(); var window = AppComposition.Current.GetExport<MainWindow>();
window.Show(); window.Show();
var vm = (MainWindowViewModel)window.DataContext!; var vm = (MainWindowViewModel)window.DataContext!;
// wait for assemblies to load
await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3); 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
@ -255,6 +276,7 @@ public class DecompilerViewTests
var openCommand = registry.Commands var openCommand = registry.Commands
.Single(c => c.Metadata.Header == nameof(Resources._Open)) .Single(c => c.Metadata.Header == nameof(Resources._Open))
.CreateExport().Value; .CreateExport().Value;
// execute openCommand
openCommand.Execute(newAsmPath); openCommand.Execute(newAsmPath);
await Waiters.WaitForAsync(() => await Waiters.WaitForAsync(() =>
@ -268,6 +290,7 @@ public class DecompilerViewTests
// 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).
// wait for the predicate
await Waiters.WaitForAsync(() => string.Equals(tab.Title, node.Text?.ToString(), System.StringComparison.Ordinal)); await Waiters.WaitForAsync(() => string.Equals(tab.Title, node.Text?.ToString(), System.StringComparison.Ordinal));
node.Text!.ToString().Should().NotBe(node.LoadedAssembly.ShortName, node.Text!.ToString().Should().NotBe(node.LoadedAssembly.ShortName,
"the rich form (with version + tfm) must be available, otherwise the test isn't exercising the late-update path"); "the rich form (with version + tfm) must be available, otherwise the test isn't exercising the late-update path");
@ -286,10 +309,12 @@ public class DecompilerViewTests
var window = AppComposition.Current.GetExport<MainWindow>(); var window = AppComposition.Current.GetExport<MainWindow>();
window.Show(); window.Show();
var vm = (MainWindowViewModel)window.DataContext!; var vm = (MainWindowViewModel)window.DataContext!;
// wait for assemblies to load
await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3); await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3);
var firstNode = vm.AssemblyTreeModel.FindNode<AssemblyTreeNode>("System.Linq"); var firstNode = vm.AssemblyTreeModel.FindNode<AssemblyTreeNode>("System.Linq");
vm.AssemblyTreeModel.SelectedItem = firstNode; // select firstNode
vm.AssemblyTreeModel.SelectNode(firstNode);
var tab = await vm.DockWorkspace.WaitForDecompiledTextAsync(); var tab = await vm.DockWorkspace.WaitForDecompiledTextAsync();
var titles = new List<string>(); var titles = new List<string>();
@ -299,7 +324,8 @@ public class DecompilerViewTests
// Act — kick off a second decompile and observe Title/Text changes throughout. // Act — kick off a second decompile and observe Title/Text changes throughout.
var coreLib = typeof(object).Assembly.GetName().Name!; var coreLib = typeof(object).Assembly.GetName().Name!;
var second = vm.AssemblyTreeModel.FindNode<TypeTreeNode>(coreLib, "System", "System.Version"); var second = vm.AssemblyTreeModel.FindNode<TypeTreeNode>(coreLib, "System", "System.Version");
vm.AssemblyTreeModel.SelectedItem = second; // select second
vm.AssemblyTreeModel.SelectNode(second);
await vm.DockWorkspace.WaitForDecompiledTextAsync(); await vm.DockWorkspace.WaitForDecompiledTextAsync();
tab.PropertyChanged -= OnTabPropertyChanged; tab.PropertyChanged -= OnTabPropertyChanged;
@ -333,10 +359,12 @@ public class DecompilerViewTests
var window = AppComposition.Current.GetExport<MainWindow>(); var window = AppComposition.Current.GetExport<MainWindow>();
window.Show(); window.Show();
var vm = (MainWindowViewModel)window.DataContext!; var vm = (MainWindowViewModel)window.DataContext!;
// wait for assemblies to load
await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3); 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
typeNode.IsExpanded = true; 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");
@ -364,10 +392,12 @@ public class DecompilerViewTests
var window = AppComposition.Current.GetExport<MainWindow>(); var window = AppComposition.Current.GetExport<MainWindow>();
window.Show(); window.Show();
var vm = (MainWindowViewModel)window.DataContext!; var vm = (MainWindowViewModel)window.DataContext!;
// wait for assemblies to load
await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3); 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
typeNode.IsExpanded = true; 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");
@ -379,6 +409,7 @@ public class DecompilerViewTests
vm.AssemblyTreeModel.SelectedItems.Add(asEnumerable); vm.AssemblyTreeModel.SelectedItems.Add(asEnumerable);
vm.AssemblyTreeModel.SelectedItems.Add(empty); vm.AssemblyTreeModel.SelectedItems.Add(empty);
// wait for decompile to finish
var tab = await vm.DockWorkspace.WaitForDecompiledTextAsync(); var tab = await vm.DockWorkspace.WaitForDecompiledTextAsync();
// 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
@ -398,13 +429,16 @@ public class DecompilerViewTests
var window = AppComposition.Current.GetExport<MainWindow>(); var window = AppComposition.Current.GetExport<MainWindow>();
window.Show(); window.Show();
var vm = (MainWindowViewModel)window.DataContext!; var vm = (MainWindowViewModel)window.DataContext!;
// wait for assemblies to load
await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3); 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.SelectedItem = assemblyNode; // select assemblyNode
vm.AssemblyTreeModel.SelectNode(assemblyNode);
// wait for decompile to finish
var tab = await vm.DockWorkspace.WaitForDecompiledTextAsync(); var tab = await vm.DockWorkspace.WaitForDecompiledTextAsync();
// Assert — every documented header line and at least one assembly attribute land. // Assert — every documented header line and at least one assembly attribute land.
@ -428,10 +462,12 @@ public class DecompilerViewTests
var window = AppComposition.Current.GetExport<MainWindow>(); var window = AppComposition.Current.GetExport<MainWindow>();
window.Show(); window.Show();
var vm = (MainWindowViewModel)window.DataContext!; var vm = (MainWindowViewModel)window.DataContext!;
// wait for assemblies to load
await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3); await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3);
var assemblyNode = vm.AssemblyTreeModel.FindNode<AssemblyTreeNode>("System.Linq"); var assemblyNode = vm.AssemblyTreeModel.FindNode<AssemblyTreeNode>("System.Linq");
vm.AssemblyTreeModel.SelectedItem = assemblyNode; // select assemblyNode
vm.AssemblyTreeModel.SelectNode(assemblyNode);
var tab = await vm.DockWorkspace.WaitForDecompiledTextAsync(); var tab = await vm.DockWorkspace.WaitForDecompiledTextAsync();
// Act — drop a synthetic XML resource into the tab. XmlResourceEntryNode triggers // Act — drop a synthetic XML resource into the tab. XmlResourceEntryNode triggers
@ -439,6 +475,7 @@ public class DecompilerViewTests
var xml = "<root>\n <child attr=\"v\">\n text\n </child>\n <other>\n </other>\n</root>"; var xml = "<root>\n <child attr=\"v\">\n text\n </child>\n <other>\n </other>\n</root>";
var bytes = System.Text.Encoding.UTF8.GetBytes(xml); var bytes = System.Text.Encoding.UTF8.GetBytes(xml);
tab.CurrentNode = new XmlResourceEntryNode("test.xml", () => new System.IO.MemoryStream(bytes)); tab.CurrentNode = new XmlResourceEntryNode("test.xml", () => new System.IO.MemoryStream(bytes));
// wait for the predicate
await Waiters.WaitForAsync(() => tab.SyntaxExtension == ".xml" && tab.Text.Contains("<root>")); await Waiters.WaitForAsync(() => tab.SyntaxExtension == ".xml" && tab.Text.Contains("<root>"));
var view = window.GetVisualDescendants().OfType<DecompilerTextView>().Single(); var view = window.GetVisualDescendants().OfType<DecompilerTextView>().Single();

6
ILSpy.Tests/MainWindow/MainWindowTests.cs

@ -66,6 +66,7 @@ public class MainWindowTests
var window = AppComposition.Current.GetExport<MainWindow>(); var window = AppComposition.Current.GetExport<MainWindow>();
window.Show(); window.Show();
// wait for the predicate
await Waiters.WaitForAsync(() => window.GetVisualDescendants().OfType<AssemblyListPane>().Any()); await Waiters.WaitForAsync(() => window.GetVisualDescendants().OfType<AssemblyListPane>().Any());
var pane = window.GetVisualDescendants().OfType<AssemblyListPane>().Single(); var pane = window.GetVisualDescendants().OfType<AssemblyListPane>().Single();
@ -136,6 +137,7 @@ public class MainWindowTests
var window = AppComposition.Current.GetExport<MainWindow>(); var window = AppComposition.Current.GetExport<MainWindow>();
window.Show(); window.Show();
var vm = (MainWindowViewModel)window.DataContext!; var vm = (MainWindowViewModel)window.DataContext!;
// wait for assemblies to load
await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3); await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3);
var service = AppComposition.Current.GetExport<TaskbarProgressService>(); var service = AppComposition.Current.GetExport<TaskbarProgressService>();
@ -145,7 +147,9 @@ public class MainWindowTests
// Act — trigger a decompile. // Act — trigger a decompile.
var node = vm.AssemblyTreeModel.FindNode<AssemblyTreeNode>("System.Linq"); var node = vm.AssemblyTreeModel.FindNode<AssemblyTreeNode>("System.Linq");
vm.AssemblyTreeModel.SelectedItem = node; // select node
vm.AssemblyTreeModel.SelectNode(node);
// wait for decompile to finish
await vm.DockWorkspace.WaitForDecompiledTextAsync(); await vm.DockWorkspace.WaitForDecompiledTextAsync();
service.StateChanged -= Observe; service.StateChanged -= Observe;

35
ILSpy.Tests/Navigation/NavigationTests.cs

@ -49,10 +49,12 @@ public class NavigationTests
var window = AppComposition.Current.GetExport<MainWindow>(); var window = AppComposition.Current.GetExport<MainWindow>();
window.Show(); window.Show();
var vm = (MainWindowViewModel)window.DataContext!; var vm = (MainWindowViewModel)window.DataContext!;
// wait for assemblies to load
await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3); 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
typeNode.IsExpanded = true; typeNode.IsExpanded = true;
var firstMethod = typeNode.Children.OfType<MethodTreeNode>() var firstMethod = typeNode.Children.OfType<MethodTreeNode>()
.Single(m => m.MethodDefinition.Name == "AsEnumerable"); .Single(m => m.MethodDefinition.Name == "AsEnumerable");
@ -60,7 +62,10 @@ public class NavigationTests
.First(m => m.MethodDefinition.Name == "Empty"); .First(m => m.MethodDefinition.Name == "Empty");
// Act 1 — select AsEnumerable, wait for it to decompile. // Act 1 — select AsEnumerable, wait for it to decompile.
vm.AssemblyTreeModel.SelectedItem = firstMethod; // select firstMethod
// select firstMethod
vm.AssemblyTreeModel.SelectNode(firstMethod);
// wait for decompile to finish
var firstTab = await vm.DockWorkspace.WaitForDecompiledTextAsync(); var firstTab = await vm.DockWorkspace.WaitForDecompiledTextAsync();
firstTab.Text.Should().Contain("AsEnumerable"); firstTab.Text.Should().Contain("AsEnumerable");
@ -69,18 +74,25 @@ public class NavigationTests
await Task.Delay(600); await Task.Delay(600);
// Act 2 — select Empty, wait for its decompile. // Act 2 — select Empty, wait for its decompile.
vm.AssemblyTreeModel.SelectedItem = secondMethod; // select secondMethod
// select secondMethod
vm.AssemblyTreeModel.SelectNode(secondMethod);
// wait for the predicate
await Waiters.WaitForAsync(() => ReferenceEquals(vm.AssemblyTreeModel.SelectedItem, secondMethod)); await Waiters.WaitForAsync(() => ReferenceEquals(vm.AssemblyTreeModel.SelectedItem, secondMethod));
// wait for decompile to finish
var secondTab = await vm.DockWorkspace.WaitForDecompiledTextAsync(); var secondTab = await vm.DockWorkspace.WaitForDecompiledTextAsync();
secondTab.Text.Should().Contain("Empty"); secondTab.Text.Should().Contain("Empty");
// Act 3 — fire NavigateBack. // Act 3 — fire NavigateBack.
vm.DockWorkspace.NavigateBackCommand.CanExecute(null).Should().BeTrue(); vm.DockWorkspace.NavigateBackCommand.CanExecute(null).Should().BeTrue();
// execute vm.DockWorkspace.NavigateBackCommand
vm.DockWorkspace.NavigateBackCommand.Execute(null); vm.DockWorkspace.NavigateBackCommand.Execute(null);
// Assert — selection restores to AsEnumerable, the document re-decompiles to its body, // Assert — selection restores to AsEnumerable, the document re-decompiles to its body,
// and the row is centred back into view. // and the row is centred back into view.
// wait for the predicate
await Waiters.WaitForAsync(() => ReferenceEquals(vm.AssemblyTreeModel.SelectedItem, firstMethod)); await Waiters.WaitForAsync(() => ReferenceEquals(vm.AssemblyTreeModel.SelectedItem, firstMethod));
// wait for decompile to finish
var restoredTab = await vm.DockWorkspace.WaitForDecompiledTextAsync(); var restoredTab = await vm.DockWorkspace.WaitForDecompiledTextAsync();
restoredTab.Text.Should().Contain("AsEnumerable"); restoredTab.Text.Should().Contain("AsEnumerable");
restoredTab.Text.Should().Contain("return source"); restoredTab.Text.Should().Contain("return source");
@ -100,10 +112,12 @@ public class NavigationTests
var window = AppComposition.Current.GetExport<MainWindow>(); var window = AppComposition.Current.GetExport<MainWindow>();
window.Show(); window.Show();
var vm = (MainWindowViewModel)window.DataContext!; var vm = (MainWindowViewModel)window.DataContext!;
// wait for assemblies to load
await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3); 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
typeNode.IsExpanded = true; typeNode.IsExpanded = true;
var methodA = typeNode.Children.OfType<MethodTreeNode>() var methodA = typeNode.Children.OfType<MethodTreeNode>()
.First(m => m.MethodDefinition.Name == "AsEnumerable"); .First(m => m.MethodDefinition.Name == "AsEnumerable");
@ -114,13 +128,19 @@ public class NavigationTests
// Act 1 — three distinct selections with >0.6s gaps so each lands as its own entry on // Act 1 — three distinct selections with >0.6s gaps so each lands as its own entry on
// the back stack (NavigationHistory collapses sub-0.5s rapid succession into one entry). // the back stack (NavigationHistory collapses sub-0.5s rapid succession into one entry).
vm.AssemblyTreeModel.SelectedItem = methodA; // select methodA
vm.AssemblyTreeModel.SelectNode(methodA);
// wait for decompile to finish
await vm.DockWorkspace.WaitForDecompiledTextAsync(); await vm.DockWorkspace.WaitForDecompiledTextAsync();
await Task.Delay(600); await Task.Delay(600);
vm.AssemblyTreeModel.SelectedItem = methodB; // select methodB
vm.AssemblyTreeModel.SelectNode(methodB);
// wait for decompile to finish
await vm.DockWorkspace.WaitForDecompiledTextAsync(); await vm.DockWorkspace.WaitForDecompiledTextAsync();
await Task.Delay(600); await Task.Delay(600);
vm.AssemblyTreeModel.SelectedItem = methodC; // select methodC
vm.AssemblyTreeModel.SelectNode(methodC);
// wait for decompile to finish
await vm.DockWorkspace.WaitForDecompiledTextAsync(); await vm.DockWorkspace.WaitForDecompiledTextAsync();
// Act 2 — open the Back SplitButton's flyout. The Opening handler populates the menu // Act 2 — open the Back SplitButton's flyout. The Opening handler populates the menu
@ -128,7 +148,10 @@ public class NavigationTests
var backSplit = window.GetVisualDescendants().OfType<SplitButton>() var backSplit = window.GetVisualDescendants().OfType<SplitButton>()
.Single(sb => sb.Name == "BackSplitButton"); .Single(sb => sb.Name == "BackSplitButton");
var flyout = (MenuFlyout)backSplit.Flyout!; var flyout = (MenuFlyout)backSplit.Flyout!;
// open flyout on backSplit
flyout.ShowAt(backSplit); flyout.ShowAt(backSplit);
// wait for the predicate
// wait for the predicate
await Waiters.WaitForAsync(() => flyout.Items.OfType<MenuItem>().Count() >= 2); await Waiters.WaitForAsync(() => flyout.Items.OfType<MenuItem>().Count() >= 2);
// Assert 1 — newest-first ordering: index 0 is the immediate previous selection // Assert 1 — newest-first ordering: index 0 is the immediate previous selection
@ -143,6 +166,8 @@ public class NavigationTests
// Act 3 — multi-step jump: clicking methodA pops two entries off the back stack in one go. // Act 3 — multi-step jump: clicking methodA pops two entries off the back stack in one go.
items[1].Command!.Execute(items[1].CommandParameter); items[1].Command!.Execute(items[1].CommandParameter);
// wait for the predicate
// wait for the predicate
await Waiters.WaitForAsync(() => ReferenceEquals(vm.AssemblyTreeModel.SelectedItem, methodA)); await Waiters.WaitForAsync(() => ReferenceEquals(vm.AssemblyTreeModel.SelectedItem, methodA));
// Assert 2 — the two displaced entries (methodC, methodB) are now on the forward stack, // Assert 2 — the two displaced entries (methodC, methodB) are now on the forward stack,

Loading…
Cancel
Save