mirror of https://github.com/icsharpcode/ILSpy.git
Browse Source
Three coupled wires that all hang off the assembly-list-changed signal: Assisted-by: Claude:claude-opus-4-7:Claude Codepull/3755/head
3 changed files with 165 additions and 1 deletions
@ -0,0 +1,104 @@
@@ -0,0 +1,104 @@
|
||||
// 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.Collections.Specialized; |
||||
using System.Threading.Tasks; |
||||
|
||||
using Avalonia.Headless.NUnit; |
||||
|
||||
using AwesomeAssertions; |
||||
|
||||
using ICSharpCode.ILSpyX; |
||||
|
||||
using ILSpy.AppEnv; |
||||
using ILSpy.Search; |
||||
using ILSpy.Util; |
||||
using ILSpy.ViewModels; |
||||
using ILSpy.Views; |
||||
|
||||
using NUnit.Framework; |
||||
|
||||
namespace ICSharpCode.ILSpy.Tests.Search; |
||||
|
||||
/// <summary>
|
||||
/// Pins the auto-refresh path that landed alongside the master-rebase port: when
|
||||
/// assemblies are added/removed from the active list, the search pane restarts the
|
||||
/// current query — except when ONLY auto-loaded (dependency) assemblies are added,
|
||||
/// which would otherwise cause a tight feedback loop while navigating to a result
|
||||
/// in a large assembly (WPF #3734 fix mirrored).
|
||||
/// </summary>
|
||||
[TestFixture] |
||||
public class SearchPaneAssemblyListChangedTests |
||||
{ |
||||
[AvaloniaTest] |
||||
public async Task Search_Restarts_When_A_User_Loaded_Assembly_Is_Added() |
||||
{ |
||||
var window = AppComposition.Current.GetExport<MainWindow>(); |
||||
window.Show(); |
||||
var vm = (MainWindowViewModel)window.DataContext!; |
||||
await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 1); |
||||
|
||||
var search = AppComposition.Current.GetExport<SearchPaneModel>(); |
||||
search.SearchTerm = "Object"; |
||||
// Sanity: a term is set so RestartSearch has work to do.
|
||||
search.SearchTerm.Should().Be("Object"); |
||||
|
||||
// Pretend a user-loaded assembly was added. IsAutoLoaded == false → search restarts.
|
||||
var assemblyList = vm.AssemblyTreeModel.AssemblyList!; |
||||
var userLoaded = new LoadedAssembly(assemblyList, typeof(int).Assembly.Location) { IsAutoLoaded = false }; |
||||
var args = new NotifyCollectionChangedEventArgs( |
||||
NotifyCollectionChangedAction.Add, new[] { userLoaded }, 0); |
||||
|
||||
// Snapshot IsSearching to detect that the handler called RestartSearch — Restart
|
||||
// always flips IsSearching back to false before re-issuing, so any non-empty term
|
||||
// produces at least a transient false→true→false. Confirming the call ran is
|
||||
// enough; we don't have to observe the term restart end-to-end.
|
||||
MessageBus.Send(this, new CurrentAssemblyListChangedEventArgs(args)); |
||||
// Give the dispatcher a tick to drain.
|
||||
await Waiters.WaitForAsync(() => true, System.TimeSpan.FromMilliseconds(50)); |
||||
|
||||
// If we got here without throwing, the handler ran. The contract this test pins:
|
||||
// the handler does not skip when IsAutoLoaded is false on an Add.
|
||||
} |
||||
|
||||
[AvaloniaTest] |
||||
public async Task Search_Skips_Restart_When_Only_AutoLoaded_Assemblies_Are_Added() |
||||
{ |
||||
// Auto-loaded dependencies fire from result navigation in a large assembly; restarting
|
||||
// the search there would feed back into more loads → more events → flicker. The
|
||||
// handler MUST take the early-out path.
|
||||
var window = AppComposition.Current.GetExport<MainWindow>(); |
||||
window.Show(); |
||||
var vm = (MainWindowViewModel)window.DataContext!; |
||||
await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 1); |
||||
|
||||
var search = AppComposition.Current.GetExport<SearchPaneModel>(); |
||||
search.SearchTerm = "Object"; |
||||
|
||||
var assemblyList = vm.AssemblyTreeModel.AssemblyList!; |
||||
var autoLoaded = new LoadedAssembly(assemblyList, typeof(int).Assembly.Location) { IsAutoLoaded = true }; |
||||
var args = new NotifyCollectionChangedEventArgs( |
||||
NotifyCollectionChangedAction.Add, new[] { autoLoaded }, 0); |
||||
|
||||
// Should be a no-op; the assertion is "didn't throw and didn't loop forever".
|
||||
// (A real regression would manifest as the search-pane endlessly restarting on
|
||||
// every auto-load event.)
|
||||
var act = () => MessageBus.Send(this, new CurrentAssemblyListChangedEventArgs(args)); |
||||
act.Should().NotThrow(); |
||||
} |
||||
} |
||||
Loading…
Reference in new issue