diff --git a/ILSpy.Tests/Diagnostics/StartupPerfTests.cs b/ILSpy.Tests/Diagnostics/StartupPerfTests.cs
index e7a485ff9..14395d88b 100644
--- a/ILSpy.Tests/Diagnostics/StartupPerfTests.cs
+++ b/ILSpy.Tests/Diagnostics/StartupPerfTests.cs
@@ -148,6 +148,57 @@ public class StartupPerfTests
}
}
+ ///
+ /// CI-runnable variant of .
+ /// Same shape but with a much smaller assembly count (8 vs 200) so it can run inside the
+ /// regular suite — catches order-of-magnitude regressions in the open + load pipeline
+ /// without the 30+ second cost of the full benchmark. NOT [Explicit] on purpose.
+ ///
+ [AvaloniaTest]
+ public async Task BindTree_With_Small_AssemblyList_Settles_In_Reasonable_Time()
+ {
+ const int Copies = 8;
+ var tempDir = Path.Combine(Path.GetTempPath(), "ILSpy.PerfTest.CI", Guid.NewGuid().ToString("N"));
+ Directory.CreateDirectory(tempDir);
+ try
+ {
+ var sourcePath = typeof(object).Assembly.Location;
+ var clones = new string[Copies];
+ for (int i = 0; i < Copies; i++)
+ {
+ clones[i] = Path.Combine(tempDir, $"copy{i:D2}.dll");
+ File.Copy(sourcePath, clones[i]);
+ }
+
+ var window = AppComposition.Current.GetExport();
+ window.Show();
+ var vm = (MainWindowViewModel)window.DataContext!;
+ await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3);
+ var baselineCount = vm.AssemblyTreeModel.AssemblyList!.GetAssemblies().Length;
+
+ var swTotal = Stopwatch.StartNew();
+ foreach (var path in clones)
+ vm.AssemblyTreeModel.AssemblyList!.OpenAssembly(path);
+ await Waiters.WaitForAsync(
+ () => vm.AssemblyTreeModel.AssemblyList!.GetAssemblies().Length >= baselineCount + Copies,
+ timeout: TimeSpan.FromSeconds(60));
+ swTotal.Stop();
+
+ // Relaxed CI threshold — 8 assemblies should never take more than 15s, even on a
+ // shared CI runner. A 10× regression of the open-and-settle pipeline trips this.
+ swTotal.Elapsed.Should().BeLessThan(TimeSpan.FromSeconds(15),
+ "opening + settling 8 LoadedAssembly entries should complete in well under 15s");
+ vm.AssemblyTreeModel.AssemblyList!.GetAssemblies().Length
+ .Should().BeGreaterThanOrEqualTo(baselineCount + Copies);
+ }
+ finally
+ {
+ try
+ { Directory.Delete(tempDir, recursive: true); }
+ catch { /* cleanup must never fail */ }
+ }
+ }
+
const int ResponsivenessAssemblyCount = 200;
[Explicit("Perf benchmark — emits dispatcher-latency stats for manual inspection")]