diff --git a/ILSpy.Tests/DecompileAsEnumerableTest.cs b/ILSpy.Tests/DecompileAsEnumerableTest.cs deleted file mode 100644 index 074cd07d8..000000000 --- a/ILSpy.Tests/DecompileAsEnumerableTest.cs +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) 2026 AlphaSierraPapa for the SharpDevelop Team -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of this -// software and associated documentation files (the "Software"), to deal in the Software -// without restriction, including without limitation the rights to use, copy, modify, merge, -// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons -// to whom the Software is furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all copies or -// substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, -// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR -// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE -// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. - -using System.Linq; -using System.Threading.Tasks; - -using AwesomeAssertions; - -using Avalonia.Headless.NUnit; - -using global::ILSpy.AppEnv; -using global::ILSpy.TreeNodes; -using global::ILSpy.ViewModels; -using global::ILSpy.Views; - -using NUnit.Framework; - -namespace ICSharpCode.ILSpy.Tests; - -[TestFixture] -public class DecompileAsEnumerableTest -{ - [AvaloniaTest] - public async Task Selecting_AsEnumerable_Method_Decompiles_Into_Document_View() - { - var window = AppComposition.Current.GetExport(); - window.Show(); - var vm = (MainWindowViewModel)window.DataContext!; - - // MainWindow.Opened triggers AssemblyTreeModel.Initialize, which queues the default - // assembly list (System.Private.CoreLib, System, System.Linq) for async loading. - await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3); - - // Walk via the production FindNodeByPath (used by SessionSettings.ActiveTreeViewPath - // save/restore) down to the type. First segment is the assembly short name; the helper - // substitutes the file path AssemblyTreeNode.ToString returns. - var typeNode = vm.AssemblyTreeModel.FindNode( - "System.Linq", "System.Linq", "System.Linq.Enumerable"); - - // Method-level stable identities are built via ILAmbience and impractical to author by - // hand, so we expand the type and pick the overload by name. Enumerable has only one - // AsEnumerable, so this is unambiguous. - typeNode.EnsureLazyChildren(); - var methodNode = typeNode.Children.OfType() - .Single(m => m.MethodDefinition.Name == "AsEnumerable"); - methodNode.MethodDefinition.IsExtensionMethod.Should().BeTrue(); - - // Selecting the node routes through DockWorkspace -> the active decompiler tab. - vm.AssemblyTreeModel.SelectedItem = methodNode; - - var tab = await vm.DockWorkspace.WaitForDecompiledTextAsync(); - tab.Text.Should().Contain("AsEnumerable"); - tab.Text.Should().Contain("IEnumerable"); - tab.Text.Should().Contain("return source"); - - // Selecting a deeply-nested node must scroll the assembly tree so it's centered. - methodNode.Should().Be().CenteredInView(); - - window.CaptureAndShow(); - } -} diff --git a/ILSpy.Tests/MainWindow/MainWindowTests.cs b/ILSpy.Tests/MainWindow/MainWindowTests.cs index b67f10bd6..d2d12239c 100644 --- a/ILSpy.Tests/MainWindow/MainWindowTests.cs +++ b/ILSpy.Tests/MainWindow/MainWindowTests.cs @@ -16,13 +16,20 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +using System.Linq; +using System.Threading.Tasks; + using Avalonia.Headless.NUnit; using AwesomeAssertions; -using global::ILSpy.AppEnv; -using global::ILSpy.ViewModels; -using global::ILSpy.Views; +using ICSharpCode.ILSpy.Properties; + +using ILSpy.AppEnv; +using ILSpy.Commands; +using ILSpy.TreeNodes; +using ILSpy.ViewModels; +using ILSpy.Views; using NUnit.Framework; @@ -40,7 +47,96 @@ public class MainWindowTests window.IsVisible.Should().BeTrue(); window.Title.Should().Be("ILSpy"); window.DataContext.Should().BeOfType(); + } + + [AvaloniaTest] + public async Task Selecting_AsEnumerable_Method_Decompiles_Into_Document_View() + { + var window = AppComposition.Current.GetExport(); + window.Show(); + var vm = (MainWindowViewModel)window.DataContext!; + await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3); + + var typeNode = vm.AssemblyTreeModel.FindNode( + "System.Linq", "System.Linq", "System.Linq.Enumerable"); + typeNode.EnsureLazyChildren(); + var methodNode = typeNode.Children.OfType() + .Single(m => m.MethodDefinition.Name == "AsEnumerable"); + methodNode.MethodDefinition.IsExtensionMethod.Should().BeTrue(); + + vm.AssemblyTreeModel.SelectedItem = methodNode; + var tab = await vm.DockWorkspace.WaitForDecompiledTextAsync(); + tab.Text.Should().Contain("AsEnumerable"); + tab.Text.Should().Contain("IEnumerable"); + tab.Text.Should().Contain("return source"); + + methodNode.Should().Be().CenteredInView(); + } + + [AvaloniaTest] + public async Task Back_Navigation_Restores_Previously_Selected_Node() + { + var window = AppComposition.Current.GetExport(); + window.Show(); + var vm = (MainWindowViewModel)window.DataContext!; + await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3); + + var typeNode = vm.AssemblyTreeModel.FindNode( + "System.Linq", "System.Linq", "System.Linq.Enumerable"); + typeNode.EnsureLazyChildren(); + var firstMethod = typeNode.Children.OfType() + .Single(m => m.MethodDefinition.Name == "AsEnumerable"); + var secondMethod = typeNode.Children.OfType() + .First(m => m.MethodDefinition.Name == "Empty"); + + vm.AssemblyTreeModel.SelectedItem = firstMethod; + var firstTab = await vm.DockWorkspace.WaitForDecompiledTextAsync(); + firstTab.Text.Should().Contain("AsEnumerable"); + + vm.AssemblyTreeModel.SelectedItem = secondMethod; + await Waiters.WaitForAsync(() => ReferenceEquals(vm.AssemblyTreeModel.SelectedItem, secondMethod)); + var secondTab = await vm.DockWorkspace.WaitForDecompiledTextAsync(); + secondTab.Text.Should().Contain("Empty"); + + vm.DockWorkspace.NavigateBackCommand.CanExecute(null).Should().BeTrue(); + vm.DockWorkspace.NavigateBackCommand.Execute(null); + + await Waiters.WaitForAsync(() => ReferenceEquals(vm.AssemblyTreeModel.SelectedItem, firstMethod)); + var restoredTab = await vm.DockWorkspace.WaitForDecompiledTextAsync(); + restoredTab.Text.Should().Contain("AsEnumerable"); + restoredTab.Text.Should().Contain("return source"); + + firstMethod.Should().Be().CenteredInView(); + } + + [AvaloniaTest] + public async Task Opening_Assembly_Adds_It_To_The_Tree() + { + var window = AppComposition.Current.GetExport(); + window.Show(); + var vm = (MainWindowViewModel)window.DataContext!; + await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3); + + var newAsmPath = typeof(System.Net.Http.HttpClient).Assembly.Location; + var initialCount = vm.AssemblyTreeModel.AssemblyList!.Count; + + var registry = AppComposition.Current.GetExport(); + var openCommand = registry.Commands + .Single(c => c.Metadata.Header == nameof(Resources._Open)) + .CreateExport().Value; + openCommand.Execute(newAsmPath); + + await Waiters.WaitForAsync(() => + vm.AssemblyTreeModel.AssemblyList!.GetAssemblies().Any(a => + string.Equals(a.FileName, newAsmPath, System.StringComparison.OrdinalIgnoreCase))); + + var loaded = vm.AssemblyTreeModel.AssemblyList!.GetAssemblies() + .First(a => string.Equals(a.FileName, newAsmPath, System.StringComparison.OrdinalIgnoreCase)); + await loaded.GetLoadResultAsync(); - window.CaptureAndShow(); + vm.AssemblyTreeModel.AssemblyList!.Count.Should().Be(initialCount + 1); + var node = vm.AssemblyTreeModel.FindNode(loaded.ShortName); + node.LoadedAssembly.Should().BeSameAs(loaded); + node.IsSelected.Should().BeTrue(); } } diff --git a/ILSpy.Tests/TestApp.axaml.cs b/ILSpy.Tests/TestApp.axaml.cs index 12a124715..b9baf6fb6 100644 --- a/ILSpy.Tests/TestApp.axaml.cs +++ b/ILSpy.Tests/TestApp.axaml.cs @@ -23,7 +23,7 @@ using ICSharpCode.ILSpyX.Settings; using ILSpy.AppEnv; -using ProductionApp = global::ILSpy.App; +using ProductionApp = ILSpy.App; namespace ICSharpCode.ILSpy.Tests;