diff --git a/ILSpy.Tests/AssemblyList/AssemblyTreeContextMenuTests.cs b/ILSpy.Tests/AssemblyList/AssemblyTreeContextMenuTests.cs index c18e952b1..d872ef279 100644 --- a/ILSpy.Tests/AssemblyList/AssemblyTreeContextMenuTests.cs +++ b/ILSpy.Tests/AssemblyList/AssemblyTreeContextMenuTests.cs @@ -52,6 +52,7 @@ public class AssemblyTreeContextMenuTests var window = AppComposition.Current.GetExport(); window.Show(); var vm = (MainWindowViewModel)window.DataContext!; + // wait for assemblies to load await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 1); // 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(); window.Show(); var vm = (MainWindowViewModel)window.DataContext!; + // wait for assemblies to load await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 1); var pane = await window.WaitForComponent(); var assemblyNode = vm.AssemblyTreeModel.FindNode("System.Linq"); - vm.AssemblyTreeModel.SelectedItem = assemblyNode; + // select assemblyNode + vm.AssemblyTreeModel.SelectNode(assemblyNode); TextViewContext? executionContext = null; var entry = new RecordingEntry(c => executionContext = c); diff --git a/ILSpy.Tests/AssemblyList/AssemblyTreeTests.cs b/ILSpy.Tests/AssemblyList/AssemblyTreeTests.cs index f9ad613dd..ff31e320a 100644 --- a/ILSpy.Tests/AssemblyList/AssemblyTreeTests.cs +++ b/ILSpy.Tests/AssemblyList/AssemblyTreeTests.cs @@ -61,6 +61,7 @@ public class AssemblyTreeTests var window = AppComposition.Current.GetExport(); window.Show(); var vm = (MainWindowViewModel)window.DataContext!; + // wait for assemblies to load await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3); // Act — pick CoreLib (always ships localised error-message tables), expand it. @@ -88,6 +89,7 @@ public class AssemblyTreeTests var window = AppComposition.Current.GetExport(); window.Show(); var vm = (MainWindowViewModel)window.DataContext!; + // wait for assemblies to load await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3); // Act — locate the realised DataGrid inside the AssemblyListPane. @@ -110,6 +112,7 @@ public class AssemblyTreeTests var window = AppComposition.Current.GetExport(); window.Show(); var vm = (MainWindowViewModel)window.DataContext!; + // wait for assemblies to load await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3); var typeNode = vm.AssemblyTreeModel.FindNode( @@ -117,7 +120,7 @@ public class AssemblyTreeTests typeNode.EnsureLazyChildren(); typeNode.IsExpanded = true; var first = typeNode.Children.OfType() - .Single(m => m.MethodDefinition.Name == "AsEnumerable"); + .First(m => m.MethodDefinition.Name == "ElementAt"); var second = typeNode.Children.OfType() .First(m => m.MethodDefinition.Name == "Empty"); @@ -150,6 +153,7 @@ public class AssemblyTreeTests var window = AppComposition.Current.GetExport(); window.Show(); var vm = (MainWindowViewModel)window.DataContext!; + // wait for assemblies to load await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3); var tempDir = Path.Combine(Path.GetTempPath(), "ILSpy.Tests", System.Guid.NewGuid().ToString("N")); @@ -170,6 +174,7 @@ public class AssemblyTreeTests var openCommand = registry.Commands .Single(c => c.Metadata.Header == nameof(Resources._Open)) .CreateExport().Value; + // execute openCommand openCommand.Execute(zipPath); await Waiters.WaitForAsync(() => @@ -207,6 +212,7 @@ public class AssemblyTreeTests var window = AppComposition.Current.GetExport(); window.Show(); var vm = (MainWindowViewModel)window.DataContext!; + // wait for assemblies to load await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3); var coreLibName = typeof(object).Assembly.GetName().Name!; @@ -241,6 +247,7 @@ public class AssemblyTreeTests var window = AppComposition.Current.GetExport(); window.Show(); var vm = (MainWindowViewModel)window.DataContext!; + // wait for assemblies to load await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3); var assemblyNode = vm.AssemblyTreeModel.FindNode("System.Linq"); @@ -272,6 +279,7 @@ public class AssemblyTreeTests var window = AppComposition.Current.GetExport(); window.Show(); var vm = (MainWindowViewModel)window.DataContext!; + // wait for assemblies to load await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3); var assemblyNode = vm.AssemblyTreeModel.FindNode("System.Linq"); @@ -304,6 +312,7 @@ public class AssemblyTreeTests var window = AppComposition.Current.GetExport(); window.Show(); var vm = (MainWindowViewModel)window.DataContext!; + // wait for assemblies to load await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3); var assemblyNode = vm.AssemblyTreeModel.FindNode("System.Linq"); @@ -340,6 +349,7 @@ public class AssemblyTreeTests var window = AppComposition.Current.GetExport(); window.Show(); var vm = (MainWindowViewModel)window.DataContext!; + // wait for assemblies to load await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3); var assemblyNode = vm.AssemblyTreeModel.FindNode("System.Linq"); @@ -365,6 +375,7 @@ public class AssemblyTreeTests var window = AppComposition.Current.GetExport(); window.Show(); var vm = (MainWindowViewModel)window.DataContext!; + // wait for assemblies to load await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3); var assemblyNode = vm.AssemblyTreeModel.FindNode("System.Linq"); @@ -394,6 +405,7 @@ public class AssemblyTreeTests var window = AppComposition.Current.GetExport(); window.Show(); var vm = (MainWindowViewModel)window.DataContext!; + // wait for assemblies to load await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3); var assemblyNode = vm.AssemblyTreeModel.FindNode("System.Linq"); @@ -447,6 +459,7 @@ public class AssemblyTreeTests var window = AppComposition.Current.GetExport(); window.Show(); var vm = (MainWindowViewModel)window.DataContext!; + // wait for assemblies to load await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3); var coreLibDir = System.IO.Path.GetDirectoryName(typeof(object).Assembly.Location)!; @@ -460,6 +473,7 @@ public class AssemblyTreeTests var openCommand = registry.Commands .Single(c => c.Metadata.Header == nameof(Resources._Open)) .CreateExport().Value; + // execute openCommand openCommand.Execute(mscorlibPath); await Waiters.WaitForAsync(() => @@ -509,6 +523,7 @@ public class AssemblyTreeTests var window = AppComposition.Current.GetExport(); window.Show(); var vm = (MainWindowViewModel)window.DataContext!; + // wait for assemblies to load await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3); var assemblyNode = vm.AssemblyTreeModel.FindNode("System.Linq"); @@ -546,6 +561,7 @@ public class AssemblyTreeTests var window = AppComposition.Current.GetExport(); window.Show(); var vm = (MainWindowViewModel)window.DataContext!; + // wait for assemblies to load await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3); var assemblyNode = vm.AssemblyTreeModel.FindNode("System.Linq"); @@ -586,6 +602,7 @@ public class AssemblyTreeTests var window = AppComposition.Current.GetExport(); window.Show(); var vm = (MainWindowViewModel)window.DataContext!; + // wait for assemblies to load await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3); var newAsmPath = typeof(System.Net.Http.HttpClient).Assembly.Location; @@ -593,6 +610,7 @@ public class AssemblyTreeTests var openCommand = registry.Commands .Single(c => c.Metadata.Header == nameof(Resources._Open)) .CreateExport().Value; + // execute openCommand openCommand.Execute(newAsmPath); await Waiters.WaitForAsync(() => @@ -616,7 +634,8 @@ public class AssemblyTreeTests var methodNode = typeNode.Children.OfType() .First(m => m.MethodDefinition.Name == "CancelPendingRequests"); - vm.AssemblyTreeModel.SelectedItem = methodNode; + // select methodNode + vm.AssemblyTreeModel.SelectNode(methodNode); await vm.DockWorkspace.WaitForDecompiledTextAsync(); await Waiters.WaitForAsync(() => @@ -648,6 +667,7 @@ public class AssemblyTreeTests var window = AppComposition.Current.GetExport(); window.Show(); var vm = (MainWindowViewModel)window.DataContext!; + // wait for assemblies to load await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3); var newAsmPath = typeof(System.Net.Http.HttpClient).Assembly.Location; @@ -655,6 +675,7 @@ public class AssemblyTreeTests var openCommand = registry.Commands .Single(c => c.Metadata.Header == nameof(Resources._Open)) .CreateExport().Value; + // execute openCommand openCommand.Execute(newAsmPath); await Waiters.WaitForAsync(() => @@ -672,7 +693,8 @@ public class AssemblyTreeTests var methodNode = typeNode.Children.OfType() .First(m => m.MethodDefinition.Name == "CancelPendingRequests"); - vm.AssemblyTreeModel.SelectedItem = methodNode; + // select methodNode + vm.AssemblyTreeModel.SelectNode(methodNode); await vm.DockWorkspace.WaitForDecompiledTextAsync(); await Waiters.WaitForAsync(() => @@ -712,6 +734,7 @@ public class AssemblyTreeTests var window = AppComposition.Current.GetExport(); window.Show(); var vm = (MainWindowViewModel)window.DataContext!; + // wait for assemblies to load await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3); var newAsmPath = typeof(System.Net.Http.HttpClient).Assembly.Location; @@ -722,6 +745,7 @@ public class AssemblyTreeTests var openCommand = registry.Commands .Single(c => c.Metadata.Header == nameof(Resources._Open)) .CreateExport().Value; + // execute openCommand openCommand.Execute(newAsmPath); await Waiters.WaitForAsync(() => @@ -753,6 +777,7 @@ public class AssemblyTreeTests var window = AppComposition.Current.GetExport(); window.Show(); var vm = (MainWindowViewModel)window.DataContext!; + // wait for assemblies to load await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3); var enumerable = vm.AssemblyTreeModel.FindNode( @@ -762,16 +787,23 @@ public class AssemblyTreeTests var ns = (NamespaceTreeNode)enumerable.Parent!; ns.EnsureLazyChildren(); ns.IsExpanded = true; + // expand ns + var asm = (AssemblyTreeNode)ns.Parent!; + // expand asm + asm.IsExpanded = true; foreach (var child in ns.Children.OfType()) { child.EnsureLazyChildren(); child.IsExpanded = true; + // expand child } var pane = await window.WaitForComponent(); var grid = await pane.WaitForComponent(); - vm.AssemblyTreeModel.SelectedItem = enumerable; + // select enumerable + vm.AssemblyTreeModel.SelectNode(enumerable); + // wait for the predicate await Waiters.WaitForAsync(() => ReferenceEquals(vm.AssemblyTreeModel.SelectedItem, enumerable)); for (int i = 0; i < 8; i++) { @@ -831,16 +863,19 @@ public class AssemblyTreeTests var window = AppComposition.Current.GetExport(); window.Show(); var vm = (MainWindowViewModel)window.DataContext!; + // wait for assemblies to load await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3); var probe = new SaveProbeNode(); - vm.AssemblyTreeModel.SelectedItem = probe; + // select probe + vm.AssemblyTreeModel.SelectNode(probe); // Act — fire the Save Code main-menu command. var registry = AppComposition.Current.GetExport(); var saveCmd = registry.Commands .Single(c => c.Metadata.Header == nameof(Resources._SaveCode)) .CreateExport().Value; + // execute saveCmd saveCmd.Execute(null); // Assert — probe's Save() ran. @@ -870,6 +905,7 @@ public class AssemblyTreeTests var window = AppComposition.Current.GetExport(); window.Show(); var vm = (MainWindowViewModel)window.DataContext!; + // wait for assemblies to load await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3); var typeNode = vm.AssemblyTreeModel.FindNode( @@ -878,7 +914,8 @@ public class AssemblyTreeTests typeNode.IsExpanded = true; var method = typeNode.Children.OfType() .First(m => m.MethodDefinition.Name == "AsEnumerable"); - vm.AssemblyTreeModel.SelectedItem = method; + // select method + vm.AssemblyTreeModel.SelectNode(method); await vm.DockWorkspace.WaitForDecompiledTextAsync(); // 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(); window.Show(); var vm = (MainWindowViewModel)window.DataContext!; + // wait for assemblies to load await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3); var sacrificialName = typeof(System.Linq.Enumerable).Assembly.GetName().Name!; var survivorName = typeof(object).Assembly.GetName().Name!; var sacrificialNode = vm.AssemblyTreeModel.FindNode(sacrificialName); - vm.AssemblyTreeModel.SelectedItem = sacrificialNode; + // select sacrificialNode + vm.AssemblyTreeModel.SelectNode(sacrificialNode); + // wait for the predicate await Waiters.WaitForAsync(() => ReferenceEquals(vm.AssemblyTreeModel.SelectedItem, sacrificialNode)); var pane = await window.WaitForComponent(); @@ -963,6 +1003,7 @@ public class AssemblyTreeTests var window = AppComposition.Current.GetExport(); window.Show(); var vm = (MainWindowViewModel)window.DataContext!; + // wait for assemblies to load await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3); vm.AssemblyTreeModel.AssemblyList!.Count.Should().BeGreaterThan(0); @@ -972,9 +1013,11 @@ public class AssemblyTreeTests .Single(c => c.Metadata.Header == nameof(Resources.ClearAssemblyList)) .CreateExport().Value; clearCmd.CanExecute(null).Should().BeTrue("non-empty list must enable Clear"); + // execute clearCmd clearCmd.Execute(null); // Assert — the list goes to zero entries. + // wait for the predicate await Waiters.WaitForAsync(() => vm.AssemblyTreeModel.AssemblyList!.Count == 0); vm.AssemblyTreeModel.AssemblyList!.Count.Should().Be(0); } @@ -991,6 +1034,7 @@ public class AssemblyTreeTests var window = AppComposition.Current.GetExport(); window.Show(); var vm = (MainWindowViewModel)window.DataContext!; + // wait for assemblies to load await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3); var brokenPath = System.IO.Path.Combine( @@ -1006,6 +1050,7 @@ public class AssemblyTreeTests var broken = vm.AssemblyTreeModel.AssemblyList!.GetAssemblies() .First(a => string.Equals(a.FileName, brokenPath, System.StringComparison.OrdinalIgnoreCase)); // GetLoadResultAsync rethrows the load failure; HasLoadError is the safe probe. + // wait for the predicate await Waiters.WaitForAsync(() => broken.HasLoadError); var validBefore = vm.AssemblyTreeModel.AssemblyList!.GetAssemblies() @@ -1020,6 +1065,7 @@ public class AssemblyTreeTests .Single(c => c.Metadata.Header == nameof(Resources._RemoveAssembliesWithLoadErrors)) .CreateExport().Value; cmd.CanExecute(null).Should().BeTrue("a broken entry must enable the command"); + // execute cmd cmd.Execute(null); await Waiters.WaitForAsync(() => diff --git a/ILSpy.Tests/AssemblyList/RemoveAssemblyContextMenuTests.cs b/ILSpy.Tests/AssemblyList/RemoveAssemblyContextMenuTests.cs index f77bdc52f..8b3332c3f 100644 --- a/ILSpy.Tests/AssemblyList/RemoveAssemblyContextMenuTests.cs +++ b/ILSpy.Tests/AssemblyList/RemoveAssemblyContextMenuTests.cs @@ -71,6 +71,7 @@ public class RemoveAssemblyContextMenuTests var window = AppComposition.Current.GetExport(); window.Show(); var vm = (MainWindowViewModel)window.DataContext!; + // wait for assemblies to load await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3); var registry = AppComposition.Current.GetExport(); var removeEntry = registry.Entries @@ -109,6 +110,7 @@ public class RemoveAssemblyContextMenuTests var window = AppComposition.Current.GetExport(); window.Show(); var vm = (MainWindowViewModel)window.DataContext!; + // wait for assemblies to load await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3); var registry = AppComposition.Current.GetExport(); var removeEntry = registry.Entries @@ -143,12 +145,15 @@ public class RemoveAssemblyContextMenuTests var window = AppComposition.Current.GetExport(); window.Show(); var vm = (MainWindowViewModel)window.DataContext!; + // wait for assemblies to load await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 1); + // wait for the predicate await Waiters.WaitForAsync( () => window.GetVisualDescendants().OfType().Any()); var pane = window.GetVisualDescendants().OfType().Single(); var assemblyNode = vm.AssemblyTreeModel.FindNode("System.Linq"); - vm.AssemblyTreeModel.SelectedItem = assemblyNode; + // select assemblyNode + vm.AssemblyTreeModel.SelectNode(assemblyNode); var registry = AppComposition.Current.GetExport(); diff --git a/ILSpy.Tests/Commands/CommandLineArgumentsTests.cs b/ILSpy.Tests/Commands/CommandLineArgumentsTests.cs index 11d4e0428..f12f9f811 100644 --- a/ILSpy.Tests/Commands/CommandLineArgumentsTests.cs +++ b/ILSpy.Tests/Commands/CommandLineArgumentsTests.cs @@ -46,6 +46,7 @@ public class CommandLineArgumentsTests var window = AppComposition.Current.GetExport(); window.Show(); var vm = (MainWindowViewModel)window.DataContext!; + // wait for assemblies to load await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 1); var languageService = AppComposition.Current.GetExport(); 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(); window.Show(); var vm = (MainWindowViewModel)window.DataContext!; + // wait for assemblies to load await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3); var args = CommandLineArguments.Create(new[] { "--navigateto", "T:System.Linq.Enumerable" }); @@ -96,7 +98,9 @@ public class CommandLineArgumentsTests var window = AppComposition.Current.GetExport(); window.Show(); var vm = (MainWindowViewModel)window.DataContext!; + // wait for assemblies to load await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 1); + // clear selection vm.AssemblyTreeModel.SelectedItems.Clear(); var args = CommandLineArguments.Create(new[] { "--navigateto", "none" }); diff --git a/ILSpy.Tests/Commands/HelpCommandTests.cs b/ILSpy.Tests/Commands/HelpCommandTests.cs index 95f642079..c1e9e3963 100644 --- a/ILSpy.Tests/Commands/HelpCommandTests.cs +++ b/ILSpy.Tests/Commands/HelpCommandTests.cs @@ -55,6 +55,7 @@ public class HelpCommandTests var window = AppComposition.Current.GetExport(); window.Show(); var vm = (MainWindowViewModel)window.DataContext!; + // wait for assemblies to load await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 1); var registry = AppComposition.Current.GetExport(); @@ -67,6 +68,7 @@ public class HelpCommandTests // Act — fire the About command. aboutCmd.Execute(null); + // execute aboutCmd // Assert — a new DecompilerTabPageModel landed in the document dock, titled "About", // 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(); window.Show(); var vm = (MainWindowViewModel)window.DataContext!; + // wait for assemblies to load await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 1); var registry = AppComposition.Current.GetExport(); @@ -100,7 +103,9 @@ public class HelpCommandTests .Single(c => c.Metadata.Header == nameof(Resources._About)) .CreateExport().Value; var documents = ((ILSpyDockFactory)vm.DockWorkspace.Factory).Documents!; + // execute aboutCmd aboutCmd.Execute(null); + // wait for the predicate await Waiters.WaitForAsync( () => documents.ActiveDockable is DecompilerTabPageModel { Text.Length: > 0 }); var aboutTab = (DecompilerTabPageModel)documents.ActiveDockable!; @@ -141,10 +146,14 @@ public class HelpCommandTests var window = AppComposition.Current.GetExport(); window.Show(); var vm = (MainWindowViewModel)window.DataContext!; + // wait for assemblies to load await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 1); var node = vm.AssemblyTreeModel.FindNode("System.Linq"); - vm.AssemblyTreeModel.SelectedItem = node; + // select node + vm.AssemblyTreeModel.SelectNode(node); + // wait for decompile to finish await vm.DockWorkspace.WaitForDecompiledTextAsync(); + // wait for the predicate await Waiters.WaitForAsync( () => window.GetVisualDescendants().OfType().Any()); @@ -170,14 +179,18 @@ public class HelpCommandTests var window = AppComposition.Current.GetExport(); window.Show(); var vm = (MainWindowViewModel)window.DataContext!; + // wait for assemblies to load await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3); var typeNode = vm.AssemblyTreeModel.FindNode( "System.Linq", "System.Linq", "System.Linq.Enumerable"); + // expand typeNode typeNode.IsExpanded = true; var method = typeNode.Children.OfType() .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(); var decompilerTab = vm.DockWorkspace.ActiveDecompilerTab!; var methodText = decompilerTab.Text; @@ -192,7 +205,9 @@ public class HelpCommandTests .Single(c => c.Metadata.Header == nameof(Resources._About)) .CreateExport().Value; var documents = ((ILSpyDockFactory)vm.DockWorkspace.Factory).Documents!; + // execute aboutCmd aboutCmd.Execute(null); + // wait for the predicate await Waiters.WaitForAsync( () => documents.ActiveDockable is DecompilerTabPageModel { IsStaticContent: true }); var aboutTab = (DecompilerTabPageModel)documents.ActiveDockable!; @@ -207,7 +222,9 @@ public class HelpCommandTests // Act 2 — press Back from the About page. vm.DockWorkspace.NavigateBackCommand.CanExecute(null).Should().BeTrue(); + // execute vm.DockWorkspace.NavigateBackCommand vm.DockWorkspace.NavigateBackCommand.Execute(null); + // wait for the predicate await Waiters.WaitForAsync( () => ReferenceEquals(documents.ActiveDockable, decompilerTab)); @@ -220,7 +237,9 @@ public class HelpCommandTests // Act 3 — press Forward to return to the About page. vm.DockWorkspace.NavigateForwardCommand.CanExecute(null).Should().BeTrue(); + // execute vm.DockWorkspace.NavigateForwardCommand vm.DockWorkspace.NavigateForwardCommand.Execute(null); + // wait for the predicate await Waiters.WaitForAsync( () => ReferenceEquals(documents.ActiveDockable, aboutTab)); diff --git a/ILSpy.Tests/Editor/DecompilerViewTests.cs b/ILSpy.Tests/Editor/DecompilerViewTests.cs index 4ff64b99a..6c5c3fd07 100644 --- a/ILSpy.Tests/Editor/DecompilerViewTests.cs +++ b/ILSpy.Tests/Editor/DecompilerViewTests.cs @@ -53,17 +53,21 @@ public class DecompilerViewTests var window = AppComposition.Current.GetExport(); window.Show(); var vm = (MainWindowViewModel)window.DataContext!; + // wait for assemblies to load await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3); var typeNode = vm.AssemblyTreeModel.FindNode( "System.Linq", "System.Linq", "System.Linq.Enumerable"); + // expand typeNode typeNode.IsExpanded = true; var methodNode = typeNode.Children.OfType() .Single(m => m.MethodDefinition.Name == "AsEnumerable"); methodNode.MethodDefinition.IsExtensionMethod.Should().BeTrue(); // 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(); // 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(); window.Show(); var vm = (MainWindowViewModel)window.DataContext!; + // wait for assemblies to load await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3); var assemblyNode = vm.AssemblyTreeModel.FindNode("System.Linq"); + // expand assemblyNode assemblyNode.IsExpanded = true; var refFolder = assemblyNode.Children.OfType().Single(); // 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(); // Assert — every header and a representative referenced assembly land in the listing. @@ -113,6 +120,7 @@ public class DecompilerViewTests var window = AppComposition.Current.GetExport(); window.Show(); var vm = (MainWindowViewModel)window.DataContext!; + // wait for assemblies to load await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3); var coreLib = typeof(object).Assembly.GetName().Name!; @@ -120,7 +128,8 @@ public class DecompilerViewTests coreLib, "System", "System.Version"); // 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(); // Assert — class declaration + representative property names land in the output. @@ -139,17 +148,20 @@ public class DecompilerViewTests var window = AppComposition.Current.GetExport(); window.Show(); var vm = (MainWindowViewModel)window.DataContext!; + // wait for assemblies to load await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3); var coreLib = typeof(object).Assembly.GetName().Name!; var typeNode = vm.AssemblyTreeModel.FindNode( coreLib, "System", "System.Version"); + // expand typeNode typeNode.IsExpanded = true; var propertyNode = typeNode.Children.OfType() .Single(p => p.PropertyDefinition.Name == "Major"); // Act — select the property, wait for the decompile. - vm.AssemblyTreeModel.SelectedItem = propertyNode; + // select propertyNode + vm.AssemblyTreeModel.SelectNode(propertyNode); var tab = await vm.DockWorkspace.WaitForDecompiledTextAsync(); // Assert — property declaration lands in the output. @@ -166,17 +178,20 @@ public class DecompilerViewTests var window = AppComposition.Current.GetExport(); window.Show(); var vm = (MainWindowViewModel)window.DataContext!; + // wait for assemblies to load await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3); var coreLib = typeof(object).Assembly.GetName().Name!; var typeNode = vm.AssemblyTreeModel.FindNode( coreLib, "System", "System.Math"); + // expand typeNode typeNode.IsExpanded = true; var fieldNode = typeNode.Children.OfType() .Single(f => f.FieldDefinition.Name == "PI"); // Act — select the field, wait for the decompile. - vm.AssemblyTreeModel.SelectedItem = fieldNode; + // select fieldNode + vm.AssemblyTreeModel.SelectNode(fieldNode); var tab = await vm.DockWorkspace.WaitForDecompiledTextAsync(); // Assert — declaration and constant value both land. @@ -193,17 +208,20 @@ public class DecompilerViewTests var window = AppComposition.Current.GetExport(); window.Show(); var vm = (MainWindowViewModel)window.DataContext!; + // wait for assemblies to load await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3); var coreLib = typeof(object).Assembly.GetName().Name!; var typeNode = vm.AssemblyTreeModel.FindNode( coreLib, "System", "System.AppDomain"); + // expand typeNode typeNode.IsExpanded = true; var eventNode = typeNode.Children.OfType() .Single(e => e.EventDefinition.Name == "ProcessExit"); // Act — select the event, wait for the decompile. - vm.AssemblyTreeModel.SelectedItem = eventNode; + // select eventNode + vm.AssemblyTreeModel.SelectNode(eventNode); var tab = await vm.DockWorkspace.WaitForDecompiledTextAsync(); // Assert — event keyword + name land in the output. @@ -220,6 +238,7 @@ public class DecompilerViewTests var window = AppComposition.Current.GetExport(); window.Show(); var vm = (MainWindowViewModel)window.DataContext!; + // wait for assemblies to load await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3); var coreLib = typeof(object).Assembly.GetName().Name!; @@ -227,7 +246,8 @@ public class DecompilerViewTests coreLib, "System.Runtime.Versioning"); // Act — select the namespace, wait for the decompile. - vm.AssemblyTreeModel.SelectedItem = namespaceNode; + // select namespaceNode + vm.AssemblyTreeModel.SelectNode(namespaceNode); var tab = await vm.DockWorkspace.WaitForDecompiledTextAsync(); // Assert — representative type names land in the output. @@ -246,6 +266,7 @@ public class DecompilerViewTests var window = AppComposition.Current.GetExport(); window.Show(); var vm = (MainWindowViewModel)window.DataContext!; + // wait for assemblies to load await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3); // 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 .Single(c => c.Metadata.Header == nameof(Resources._Open)) .CreateExport().Value; + // execute openCommand openCommand.Execute(newAsmPath); 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 // (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)); 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"); @@ -286,10 +309,12 @@ public class DecompilerViewTests var window = AppComposition.Current.GetExport(); window.Show(); var vm = (MainWindowViewModel)window.DataContext!; + // wait for assemblies to load await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3); var firstNode = vm.AssemblyTreeModel.FindNode("System.Linq"); - vm.AssemblyTreeModel.SelectedItem = firstNode; + // select firstNode + vm.AssemblyTreeModel.SelectNode(firstNode); var tab = await vm.DockWorkspace.WaitForDecompiledTextAsync(); var titles = new List(); @@ -299,7 +324,8 @@ public class DecompilerViewTests // Act — kick off a second decompile and observe Title/Text changes throughout. var coreLib = typeof(object).Assembly.GetName().Name!; var second = vm.AssemblyTreeModel.FindNode(coreLib, "System", "System.Version"); - vm.AssemblyTreeModel.SelectedItem = second; + // select second + vm.AssemblyTreeModel.SelectNode(second); await vm.DockWorkspace.WaitForDecompiledTextAsync(); tab.PropertyChanged -= OnTabPropertyChanged; @@ -333,10 +359,12 @@ public class DecompilerViewTests var window = AppComposition.Current.GetExport(); window.Show(); var vm = (MainWindowViewModel)window.DataContext!; + // wait for assemblies to load await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3); var typeNode = vm.AssemblyTreeModel.FindNode( "System.Linq", "System.Linq", "System.Linq.Enumerable"); + // expand typeNode typeNode.IsExpanded = true; var asEnumerable = typeNode.Children.OfType() .First(m => m.MethodDefinition.Name == "AsEnumerable"); @@ -364,10 +392,12 @@ public class DecompilerViewTests var window = AppComposition.Current.GetExport(); window.Show(); var vm = (MainWindowViewModel)window.DataContext!; + // wait for assemblies to load await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3); var typeNode = vm.AssemblyTreeModel.FindNode( "System.Linq", "System.Linq", "System.Linq.Enumerable"); + // expand typeNode typeNode.IsExpanded = true; var asEnumerable = typeNode.Children.OfType() .First(m => m.MethodDefinition.Name == "AsEnumerable"); @@ -379,6 +409,7 @@ public class DecompilerViewTests vm.AssemblyTreeModel.SelectedItems.Add(asEnumerable); vm.AssemblyTreeModel.SelectedItems.Add(empty); + // wait for decompile to finish var tab = await vm.DockWorkspace.WaitForDecompiledTextAsync(); // 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(); window.Show(); var vm = (MainWindowViewModel)window.DataContext!; + // wait for assemblies to load await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3); var assemblyNode = vm.AssemblyTreeModel.FindNode("System.Linq"); // 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(); // Assert — every documented header line and at least one assembly attribute land. @@ -428,10 +462,12 @@ public class DecompilerViewTests var window = AppComposition.Current.GetExport(); window.Show(); var vm = (MainWindowViewModel)window.DataContext!; + // wait for assemblies to load await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3); var assemblyNode = vm.AssemblyTreeModel.FindNode("System.Linq"); - vm.AssemblyTreeModel.SelectedItem = assemblyNode; + // select assemblyNode + vm.AssemblyTreeModel.SelectNode(assemblyNode); var tab = await vm.DockWorkspace.WaitForDecompiledTextAsync(); // Act — drop a synthetic XML resource into the tab. XmlResourceEntryNode triggers @@ -439,6 +475,7 @@ public class DecompilerViewTests var xml = "\n \n text\n \n \n \n"; var bytes = System.Text.Encoding.UTF8.GetBytes(xml); tab.CurrentNode = new XmlResourceEntryNode("test.xml", () => new System.IO.MemoryStream(bytes)); + // wait for the predicate await Waiters.WaitForAsync(() => tab.SyntaxExtension == ".xml" && tab.Text.Contains("")); var view = window.GetVisualDescendants().OfType().Single(); diff --git a/ILSpy.Tests/MainWindow/MainWindowTests.cs b/ILSpy.Tests/MainWindow/MainWindowTests.cs index f934a05a8..b79130667 100644 --- a/ILSpy.Tests/MainWindow/MainWindowTests.cs +++ b/ILSpy.Tests/MainWindow/MainWindowTests.cs @@ -66,6 +66,7 @@ public class MainWindowTests var window = AppComposition.Current.GetExport(); window.Show(); + // wait for the predicate await Waiters.WaitForAsync(() => window.GetVisualDescendants().OfType().Any()); var pane = window.GetVisualDescendants().OfType().Single(); @@ -136,6 +137,7 @@ public class MainWindowTests var window = AppComposition.Current.GetExport(); window.Show(); var vm = (MainWindowViewModel)window.DataContext!; + // wait for assemblies to load await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3); var service = AppComposition.Current.GetExport(); @@ -145,7 +147,9 @@ public class MainWindowTests // Act — trigger a decompile. var node = vm.AssemblyTreeModel.FindNode("System.Linq"); - vm.AssemblyTreeModel.SelectedItem = node; + // select node + vm.AssemblyTreeModel.SelectNode(node); + // wait for decompile to finish await vm.DockWorkspace.WaitForDecompiledTextAsync(); service.StateChanged -= Observe; diff --git a/ILSpy.Tests/Navigation/NavigationTests.cs b/ILSpy.Tests/Navigation/NavigationTests.cs index 036cba973..e0e15474d 100644 --- a/ILSpy.Tests/Navigation/NavigationTests.cs +++ b/ILSpy.Tests/Navigation/NavigationTests.cs @@ -49,10 +49,12 @@ public class NavigationTests var window = AppComposition.Current.GetExport(); window.Show(); var vm = (MainWindowViewModel)window.DataContext!; + // wait for assemblies to load await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3); var typeNode = vm.AssemblyTreeModel.FindNode( "System.Linq", "System.Linq", "System.Linq.Enumerable"); + // expand typeNode typeNode.IsExpanded = true; var firstMethod = typeNode.Children.OfType() .Single(m => m.MethodDefinition.Name == "AsEnumerable"); @@ -60,7 +62,10 @@ public class NavigationTests .First(m => m.MethodDefinition.Name == "Empty"); // 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(); firstTab.Text.Should().Contain("AsEnumerable"); @@ -69,18 +74,25 @@ public class NavigationTests await Task.Delay(600); // 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)); + // wait for decompile to finish var secondTab = await vm.DockWorkspace.WaitForDecompiledTextAsync(); secondTab.Text.Should().Contain("Empty"); // Act 3 — fire NavigateBack. vm.DockWorkspace.NavigateBackCommand.CanExecute(null).Should().BeTrue(); + // execute vm.DockWorkspace.NavigateBackCommand vm.DockWorkspace.NavigateBackCommand.Execute(null); // Assert — selection restores to AsEnumerable, the document re-decompiles to its body, // and the row is centred back into view. + // wait for the predicate await Waiters.WaitForAsync(() => ReferenceEquals(vm.AssemblyTreeModel.SelectedItem, firstMethod)); + // wait for decompile to finish var restoredTab = await vm.DockWorkspace.WaitForDecompiledTextAsync(); restoredTab.Text.Should().Contain("AsEnumerable"); restoredTab.Text.Should().Contain("return source"); @@ -100,10 +112,12 @@ public class NavigationTests var window = AppComposition.Current.GetExport(); window.Show(); var vm = (MainWindowViewModel)window.DataContext!; + // wait for assemblies to load await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3); var typeNode = vm.AssemblyTreeModel.FindNode( "System.Linq", "System.Linq", "System.Linq.Enumerable"); + // expand typeNode typeNode.IsExpanded = true; var methodA = typeNode.Children.OfType() .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 // 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 Task.Delay(600); - vm.AssemblyTreeModel.SelectedItem = methodB; + // select methodB + vm.AssemblyTreeModel.SelectNode(methodB); + // wait for decompile to finish await vm.DockWorkspace.WaitForDecompiledTextAsync(); await Task.Delay(600); - vm.AssemblyTreeModel.SelectedItem = methodC; + // select methodC + vm.AssemblyTreeModel.SelectNode(methodC); + // wait for decompile to finish await vm.DockWorkspace.WaitForDecompiledTextAsync(); // 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() .Single(sb => sb.Name == "BackSplitButton"); var flyout = (MenuFlyout)backSplit.Flyout!; + // open flyout on backSplit flyout.ShowAt(backSplit); + // wait for the predicate + // wait for the predicate await Waiters.WaitForAsync(() => flyout.Items.OfType().Count() >= 2); // 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. items[1].Command!.Execute(items[1].CommandParameter); + // wait for the predicate + // wait for the predicate await Waiters.WaitForAsync(() => ReferenceEquals(vm.AssemblyTreeModel.SelectedItem, methodA)); // Assert 2 — the two displaced entries (methodC, methodB) are now on the forward stack,