From 800a07d4c9e7ce0090011648911c10511553f242 Mon Sep 17 00:00:00 2001 From: Julien Lebosquain Date: Mon, 10 Aug 2026 15:43:45 +0200 Subject: [PATCH 1/3] Fix init order of SetupWithClassicDesktopLifetime --- src/Avalonia.Controls/AppBuilder.cs | 11 ++ .../ClassicDesktopStyleApplicationLifetime.cs | 92 ++++++--- .../ISetupApplicationLifetime.cs | 17 ++ .../DesktopStyleApplicationLifetimeTests.cs | 179 +++++++++++++++--- 4 files changed, 241 insertions(+), 58 deletions(-) create mode 100644 src/Avalonia.Controls/ApplicationLifetimes/ISetupApplicationLifetime.cs diff --git a/src/Avalonia.Controls/AppBuilder.cs b/src/Avalonia.Controls/AppBuilder.cs index ab9802a134d..9aa24afd165 100644 --- a/src/Avalonia.Controls/AppBuilder.cs +++ b/src/Avalonia.Controls/AppBuilder.cs @@ -353,12 +353,21 @@ private void Setup() SetupUnsafe(); } + /// + /// Allows to be called again after it has already been called once. + /// + internal static void ResetSetupForUnitTests() + => s_setupWasAlreadyCalled = false; + /// /// Setup method that doesn't check for input initalizers being set. /// Nor /// internal void SetupUnsafe() { + var setupLifetime = _lifetime as ISetupApplicationLifetime; + setupLifetime?.BeforeAppInit(); + _optionsInitializers?.Invoke(); RuntimePlatformServicesInitializer?.Invoke(); TextShapingSubsystemInitializer?.Invoke(); @@ -373,6 +382,8 @@ internal void SetupUnsafe() AfterApplicationSetupCallback?.Invoke(Self); AfterSetupCallback?.Invoke(Self); Instance.OnFrameworkInitializationCompleted(); + + setupLifetime?.AfterAppInit(); } } } diff --git a/src/Avalonia.Controls/ApplicationLifetimes/ClassicDesktopStyleApplicationLifetime.cs b/src/Avalonia.Controls/ApplicationLifetimes/ClassicDesktopStyleApplicationLifetime.cs index 8d509832fa2..1dc7776e5fb 100644 --- a/src/Avalonia.Controls/ApplicationLifetimes/ClassicDesktopStyleApplicationLifetime.cs +++ b/src/Avalonia.Controls/ApplicationLifetimes/ClassicDesktopStyleApplicationLifetime.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Runtime.CompilerServices; using System.Threading; using Avalonia.Collections; @@ -12,13 +13,20 @@ namespace Avalonia.Controls.ApplicationLifetimes { - public class ClassicDesktopStyleApplicationLifetime : IClassicDesktopStyleApplicationLifetime, IDisposable + public class ClassicDesktopStyleApplicationLifetime : + IClassicDesktopStyleApplicationLifetime, + ISetupApplicationLifetime, + IDisposable { private int _exitCode; private CancellationTokenSource? _cts; private bool _isShuttingDown; private readonly AvaloniaList _windows = new(); - private CompositeDisposable? _compositeDisposable; + private CompositeDisposable? _globalEventsSubscriptions; + private bool _beforeInitCalled; + private bool _afterInitCalled; + private bool _startupCalled; + private IPlatformLifetimeEventsImpl? _platformLifetimeEventsImpl; /// public event EventHandler? Startup; @@ -67,16 +75,11 @@ public bool TryShutdown(int exitCode = 0) return DoShutdown(new ShutdownRequestedEventArgs(), true, false, exitCode); } - internal void SubscribeGlobalEvents() + private void SubscribeGlobalEvents() { - if (_compositeDisposable is not null) - { - // There could be a case, when lifetime was setup without starting. - // Until developer started it manually later. To avoid API breaking changes, it will execute Setup method twice. - return; - } + Debug.Assert(_globalEventsSubscriptions is null); - _compositeDisposable = new CompositeDisposable( + _globalEventsSubscriptions = new CompositeDisposable( Window.WindowOpenedEvent.AddClassHandler(typeof(Window), (sender, _) => { var window = (Window)sender!; @@ -93,16 +96,33 @@ internal void SubscribeGlobalEvents() })); } - internal void SetupCore(string[] args) + private void BeforeInit() { + if (_beforeInitCalled) + return; + + _beforeInitCalled = true; SubscribeGlobalEvents(); + } + + void ISetupApplicationLifetime.BeforeAppInit() + => BeforeInit(); + + private void AfterInit() + { + if (_afterInitCalled) + return; - Startup?.Invoke(this, new ControlledApplicationLifetimeStartupEventArgs(args)); + _afterInitCalled = true; - var lifetimeEvents = AvaloniaLocator.Current.GetService(); + _platformLifetimeEventsImpl = AvaloniaLocator.Current.GetService(); + _platformLifetimeEventsImpl?.ShutdownRequested += OnShutdownRequested; + } - if (lifetimeEvents != null) - lifetimeEvents.ShutdownRequested += OnShutdownRequested; + void ISetupApplicationLifetime.AfterAppInit() + { + AfterInit(); + InvokeStartup(Args); } public int Start(string[] args) @@ -121,7 +141,11 @@ public int Start() internal int StartCore(string[] args) { - SetupCore(args); + // Before/AfterInit should have been called from ISetupApplicationLifetime. + // If somehow they weren't (e.g., for a manually started lifetime), do it now. + BeforeInit(); + AfterInit(); + InvokeStartup(args); _cts = new CancellationTokenSource(); @@ -135,6 +159,19 @@ internal int StartCore(string[] args) return _exitCode; } + private void InvokeStartup(string[]? args) + { + // Ideally, the Startup event should be invoked only when Start() is called. However, it was historically invoked + // in SetupWithClassicDesktopLifetime to account for programs that don't call Start at all, so let's keep this behavior. + // Technically, the args could be different between Setup and Start. In practice, they won't for all callers we control. + // TODO13: clean this up. + if (_startupCalled) + return; + + _startupCalled = true; + Startup?.Invoke(this, new ControlledApplicationLifetimeStartupEventArgs(args ?? [])); + } + [MethodImpl(MethodImplOptions.NoInlining)] private void ShowMainWindow() { @@ -143,8 +180,11 @@ private void ShowMainWindow() public void Dispose() { - _compositeDisposable?.Dispose(); - _compositeDisposable = null; + _globalEventsSubscriptions?.Dispose(); + _globalEventsSubscriptions = null; + + _platformLifetimeEventsImpl?.ShutdownRequested -= OnShutdownRequested; + _platformLifetimeEventsImpl = null; } private bool DoShutdown( @@ -222,15 +262,12 @@ namespace Avalonia /// public static class ClassicDesktopStyleApplicationLifetimeExtensions { - private static ClassicDesktopStyleApplicationLifetime PrepareLifetime(AppBuilder builder, string[] args, + private static ClassicDesktopStyleApplicationLifetime CreateLifetime( + string[] args, Action? lifetimeBuilder) { - var lifetime = new ClassicDesktopStyleApplicationLifetime(); - lifetime.SubscribeGlobalEvents(); - - lifetime.Args = args; + var lifetime = new ClassicDesktopStyleApplicationLifetime { Args = args }; lifetimeBuilder?.Invoke(lifetime); - return lifetime; } @@ -244,8 +281,7 @@ private static ClassicDesktopStyleApplicationLifetime PrepareLifetime(AppBuilder public static AppBuilder SetupWithClassicDesktopLifetime(this AppBuilder builder, string[] args, Action? lifetimeBuilder = null) { - var lifetime = PrepareLifetime(builder, args, lifetimeBuilder); - lifetime.SetupCore(args); + var lifetime = CreateLifetime(args, lifetimeBuilder); return builder.SetupWithLifetime(lifetime); } @@ -260,7 +296,7 @@ public static int StartWithClassicDesktopLifetime( this AppBuilder builder, string[] args, Action? lifetimeBuilder = null) { - var lifetime = PrepareLifetime(builder, args, lifetimeBuilder); + var lifetime = CreateLifetime(args, lifetimeBuilder); builder.SetupWithLifetime(lifetime); return lifetime.Start(args); } @@ -275,7 +311,7 @@ public static int StartWithClassicDesktopLifetime( public static int StartWithClassicDesktopLifetime( this AppBuilder builder, string[] args, ShutdownMode shutdownMode) { - var lifetime = PrepareLifetime(builder, args, l => l.ShutdownMode = shutdownMode); + var lifetime = CreateLifetime(args, l => l.ShutdownMode = shutdownMode); builder.SetupWithLifetime(lifetime); return lifetime.Start(args); } diff --git a/src/Avalonia.Controls/ApplicationLifetimes/ISetupApplicationLifetime.cs b/src/Avalonia.Controls/ApplicationLifetimes/ISetupApplicationLifetime.cs new file mode 100644 index 00000000000..25ce4410d21 --- /dev/null +++ b/src/Avalonia.Controls/ApplicationLifetimes/ISetupApplicationLifetime.cs @@ -0,0 +1,17 @@ +namespace Avalonia.Controls.ApplicationLifetimes; + +/// +/// An interface for lifetimes that need to execute extra code before and after initialization. +/// +internal interface ISetupApplicationLifetime +{ + /// + /// Called before anything is initialized: platforms, rendering, app, etc. aren't available yet. + /// + void BeforeAppInit(); + + /// + /// Called after the app has been initialized. + /// + void AfterAppInit(); +} diff --git a/tests/Avalonia.Controls.UnitTests/DesktopStyleApplicationLifetimeTests.cs b/tests/Avalonia.Controls.UnitTests/DesktopStyleApplicationLifetimeTests.cs index 9ae80dbacbb..0801bdd5d76 100644 --- a/tests/Avalonia.Controls.UnitTests/DesktopStyleApplicationLifetimeTests.cs +++ b/tests/Avalonia.Controls.UnitTests/DesktopStyleApplicationLifetimeTests.cs @@ -1,11 +1,7 @@ using System; using System.Collections.Generic; -using System.Threading; using Avalonia.Controls.ApplicationLifetimes; -using Avalonia.Controls.Platform; using Avalonia.Platform; -using Avalonia.Rendering; -using Avalonia.Rendering.Composition; using Avalonia.Threading; using Avalonia.UnitTests; using Moq; @@ -13,25 +9,15 @@ namespace Avalonia.Controls.UnitTests { - public class DesktopStyleApplicationLifetimeTests : ScopedTestBase { - IDispatcherImpl CreateDispatcherWithInstantMainLoop() - { - var mock = new Mock(); - mock.Setup(x => x.RunLoop(It.IsAny())) - .Callback(() => Dispatcher.UIThread.ExitAllFrames()); - mock.Setup(x => x.CurrentThreadIsLoopThread).Returns(true); - return mock.Object; - } - [Fact] public void Should_Set_ExitCode_After_Shutdown() { using (UnitTestApplication.Start(new TestServices())) using(var lifetime = new ClassicDesktopStyleApplicationLifetime()) { - lifetime.SetupCore(Array.Empty()); + Setup(lifetime); Dispatcher.UIThread.Post(() => lifetime.Shutdown(1337)); var exitCode = lifetime.Start(Array.Empty()); @@ -47,7 +33,7 @@ public void Should_Close_All_Remaining_Open_Windows_After_Explicit_Exit_Call() using (UnitTestApplication.Start(TestServices.StyledWindow)) using(var lifetime = new ClassicDesktopStyleApplicationLifetime()) { - lifetime.SetupCore(Array.Empty()); + Setup(lifetime); var windows = new List { new Window(), new Window(), new Window(), new Window() }; @@ -69,7 +55,7 @@ public void Should_Only_Exit_On_Explicit_Exit() using(var lifetime = new ClassicDesktopStyleApplicationLifetime()) { lifetime.ShutdownMode = ShutdownMode.OnExplicitShutdown; - lifetime.SetupCore(Array.Empty()); + Setup(lifetime); var hasExit = false; @@ -104,7 +90,7 @@ public void Should_Exit_After_MainWindow_Closed() using(var lifetime = new ClassicDesktopStyleApplicationLifetime()) { lifetime.ShutdownMode = ShutdownMode.OnMainWindowClose; - lifetime.SetupCore(Array.Empty()); + Setup(lifetime); var hasExit = false; @@ -133,7 +119,7 @@ public void OnMainWindowClose_Overrides_Secondary_Window_Cancellation() using(var lifetime = new ClassicDesktopStyleApplicationLifetime()) { lifetime.ShutdownMode = ShutdownMode.OnMainWindowClose; - lifetime.SetupCore(Array.Empty()); + Setup(lifetime); var hasExit = false; var secondaryWindowClosingExecuted = false; @@ -173,7 +159,7 @@ public void OnMainWindowClose_Overrides_Secondary_Window_Cancellation_From_TrySh using(var lifetime = new ClassicDesktopStyleApplicationLifetime()) { lifetime.ShutdownMode = ShutdownMode.OnMainWindowClose; - lifetime.SetupCore(Array.Empty()); + Setup(lifetime); var hasExit = false; var secondaryWindowClosingExecuted = false; @@ -213,7 +199,7 @@ public void Should_Exit_After_Last_Window_Closed() using(var lifetime = new ClassicDesktopStyleApplicationLifetime()) { lifetime.ShutdownMode = ShutdownMode.OnLastWindowClose; - lifetime.SetupCore(Array.Empty()); + Setup(lifetime); var hasExit = false; @@ -243,7 +229,7 @@ public void Show_Should_Add_Window_To_OpenWindows() using (UnitTestApplication.Start(TestServices.StyledWindow)) using(var lifetime = new ClassicDesktopStyleApplicationLifetime()) { - lifetime.SetupCore(Array.Empty()); + Setup(lifetime); var window = new Window(); @@ -259,7 +245,7 @@ public void Window_Should_Be_Added_To_OpenWindows_Only_Once() using (UnitTestApplication.Start(TestServices.StyledWindow)) using(var lifetime = new ClassicDesktopStyleApplicationLifetime()) { - lifetime.SetupCore(Array.Empty()); + Setup(lifetime); var window = new Window(); @@ -279,7 +265,7 @@ public void Close_Should_Remove_Window_From_OpenWindows() using (UnitTestApplication.Start(TestServices.StyledWindow)) using(var lifetime = new ClassicDesktopStyleApplicationLifetime()) { - lifetime.SetupCore(Array.Empty()); + Setup(lifetime); var window = new Window(); @@ -311,7 +297,7 @@ public void Impl_Closing_Should_Remove_Window_From_OpenWindows() using (UnitTestApplication.Start(services)) using(var lifetime = new ClassicDesktopStyleApplicationLifetime()) { - lifetime.SetupCore(Array.Empty()); + Setup(lifetime); var window = new Window(); @@ -361,7 +347,7 @@ public void MainWindow_Closed_Shutdown_Should_Be_Cancellable() using(var lifetime = new ClassicDesktopStyleApplicationLifetime()) { lifetime.ShutdownMode = ShutdownMode.OnMainWindowClose; - lifetime.SetupCore(Array.Empty()); + Setup(lifetime); var hasExit = false; @@ -399,7 +385,7 @@ public void LastWindow_Closed_Shutdown_Should_Be_Cancellable() using(var lifetime = new ClassicDesktopStyleApplicationLifetime()) { lifetime.ShutdownMode = ShutdownMode.OnLastWindowClose; - lifetime.SetupCore(Array.Empty()); + Setup(lifetime); var hasExit = false; @@ -438,7 +424,7 @@ public void TryShutdown_Cancellable_By_Preventing_Window_Close() using (UnitTestApplication.Start(TestServices.StyledWindow)) using(var lifetime = new ClassicDesktopStyleApplicationLifetime()) { - lifetime.SetupCore(Array.Empty()); + Setup(lifetime); lifetime.Exit += (_, _) => Assert.Fail("lifetime.Exit was called."); Dispatcher.UIThread.ShutdownStarted += UiThreadOnShutdownStarted; @@ -478,7 +464,7 @@ public void Shutdown_NotCancellable_By_Preventing_Window_Close() using (UnitTestApplication.Start(TestServices.StyledWindow.With())) using(var lifetime = new ClassicDesktopStyleApplicationLifetime()) { - lifetime.SetupCore(Array.Empty()); + Setup(lifetime); var hasExit = false; var closingRaised = 0; @@ -518,7 +504,7 @@ public void Shutdown_Doesnt_Raise_Shutdown_Requested() using (UnitTestApplication.Start(TestServices.StyledWindow)) using(var lifetime = new ClassicDesktopStyleApplicationLifetime()) { - lifetime.SetupCore(Array.Empty()); + Setup(lifetime); var hasExit = false; @@ -537,5 +523,138 @@ public void Shutdown_Doesnt_Raise_Shutdown_Requested() Assert.True(hasExit); } } + + [Fact] + public void SetupWithClassicDesktopLifetime_Should_Subscribe_To_Platform_ShutdownRequested() + { + var lifetimeEvents = new Mock(); + ClassicDesktopStyleApplicationLifetime? lifetime = null; + + CreateAppBuilder(lifetimeEvents.Object).SetupWithClassicDesktopLifetime( + [], + l => lifetime = (ClassicDesktopStyleApplicationLifetime)l); + + Assert.NotNull(lifetime); + + using (lifetime) + { + var window = new Window(); + window.Show(); + + var raised = 0; + + lifetime.ShutdownRequested += (_, e) => + { + e.Cancel = true; + ++raised; + }; + + lifetimeEvents.Raise(x => x.ShutdownRequested += null, new ShutdownRequestedEventArgs()); + + Assert.Equal(1, raised); + Assert.Equal([window], lifetime.Windows); + } + } + + [Fact] + public void SetupWithClassicDesktopLifetime_Should_Raise_Startup_After_Application_Initialization() + { + var events = new List(); + string[]? startupArgs = null; + ClassicDesktopStyleApplicationLifetime? lifetime = null; + + CreateAppBuilder(onFrameworkInitializationCompleted: () => events.Add("FrameworkInitializationCompleted")) + .SetupWithClassicDesktopLifetime( + ["foo", "bar"], + l => + { + lifetime = (ClassicDesktopStyleApplicationLifetime)l; + l.Startup += (_, e) => + { + events.Add("Startup"); + startupArgs = e.Args; + }; + }); + + Assert.NotNull(lifetime); + + using (lifetime) + { + Assert.Equal(new[] { "FrameworkInitializationCompleted", "Startup" }, events); + Assert.Equal(new[] { "foo", "bar" }, startupArgs); + } + } + + [Fact] + public void Start_After_SetupWithClassicDesktopLifetime_Should_Not_Raise_Startup_Twice() + { + ClassicDesktopStyleApplicationLifetime? lifetime = null; + var raised = 0; + + CreateAppBuilder().SetupWithClassicDesktopLifetime( + [], + l => + { + lifetime = (ClassicDesktopStyleApplicationLifetime)l; + l.Startup += (_, _) => ++raised; + }); + + Assert.NotNull(lifetime); + + using (lifetime) + { + Assert.Equal(1, raised); + + Dispatcher.UIThread.Post(Dispatcher.UIThread.ExitAllFrames); + lifetime.Start([]); + + Assert.Equal(1, raised); + } + } + + private static void Setup(ClassicDesktopStyleApplicationLifetime lifetime) + { + ISetupApplicationLifetime setupLifetime = lifetime; + setupLifetime.BeforeAppInit(); + setupLifetime.AfterAppInit(); + } + + private static AppBuilder CreateAppBuilder( + IPlatformLifetimeEventsImpl? platformLifetimeEvents = null, + Action? onFrameworkInitializationCompleted = null) + { + AppBuilder.ResetSetupForUnitTests(); + + return AppBuilder.Configure(() => new SetupTestApplication(onFrameworkInitializationCompleted)) + .UseRuntimePlatformSubsystem(() => { }) + .UseRenderingSubsystem(() => { }) + .UseTextShapingSubsystem(() => { }) + .UseWindowingSubsystem(() => + { + if (platformLifetimeEvents is not null) + AvaloniaLocator.CurrentMutable.Bind().ToConstant(platformLifetimeEvents); + }); + } + + private sealed class SetupTestApplication(Action? onFrameworkInitializationCompleted) + : UnitTestApplication(TestServices.StyledWindow) + { + private bool _servicesRegistered; + + public override void RegisterServices() + { + if (_servicesRegistered) + return; + + _servicesRegistered = true; + base.RegisterServices(); + } + + public override void OnFrameworkInitializationCompleted() + { + base.OnFrameworkInitializationCompleted(); + onFrameworkInitializationCompleted?.Invoke(); + } + } } } From c071735fccea79411a9b1306aa5430b168f2a97e Mon Sep 17 00:00:00 2001 From: Julien Lebosquain Date: Tue, 1 Sep 2026 15:15:50 +0200 Subject: [PATCH 2/3] Don't raise Startup in Setup --- .../ClassicDesktopStyleApplicationLifetime.cs | 17 +---------- .../DesktopStyleApplicationLifetimeTests.cs | 29 +++++++------------ 2 files changed, 12 insertions(+), 34 deletions(-) diff --git a/src/Avalonia.Controls/ApplicationLifetimes/ClassicDesktopStyleApplicationLifetime.cs b/src/Avalonia.Controls/ApplicationLifetimes/ClassicDesktopStyleApplicationLifetime.cs index 1dc7776e5fb..741a6380bc9 100644 --- a/src/Avalonia.Controls/ApplicationLifetimes/ClassicDesktopStyleApplicationLifetime.cs +++ b/src/Avalonia.Controls/ApplicationLifetimes/ClassicDesktopStyleApplicationLifetime.cs @@ -25,7 +25,6 @@ public class ClassicDesktopStyleApplicationLifetime : private CompositeDisposable? _globalEventsSubscriptions; private bool _beforeInitCalled; private bool _afterInitCalled; - private bool _startupCalled; private IPlatformLifetimeEventsImpl? _platformLifetimeEventsImpl; /// @@ -122,7 +121,6 @@ private void AfterInit() void ISetupApplicationLifetime.AfterAppInit() { AfterInit(); - InvokeStartup(Args); } public int Start(string[] args) @@ -145,7 +143,7 @@ internal int StartCore(string[] args) // If somehow they weren't (e.g., for a manually started lifetime), do it now. BeforeInit(); AfterInit(); - InvokeStartup(args); + Startup?.Invoke(this, new ControlledApplicationLifetimeStartupEventArgs(args)); _cts = new CancellationTokenSource(); @@ -159,19 +157,6 @@ internal int StartCore(string[] args) return _exitCode; } - private void InvokeStartup(string[]? args) - { - // Ideally, the Startup event should be invoked only when Start() is called. However, it was historically invoked - // in SetupWithClassicDesktopLifetime to account for programs that don't call Start at all, so let's keep this behavior. - // Technically, the args could be different between Setup and Start. In practice, they won't for all callers we control. - // TODO13: clean this up. - if (_startupCalled) - return; - - _startupCalled = true; - Startup?.Invoke(this, new ControlledApplicationLifetimeStartupEventArgs(args ?? [])); - } - [MethodImpl(MethodImplOptions.NoInlining)] private void ShowMainWindow() { diff --git a/tests/Avalonia.Controls.UnitTests/DesktopStyleApplicationLifetimeTests.cs b/tests/Avalonia.Controls.UnitTests/DesktopStyleApplicationLifetimeTests.cs index 0801bdd5d76..4f9a855591c 100644 --- a/tests/Avalonia.Controls.UnitTests/DesktopStyleApplicationLifetimeTests.cs +++ b/tests/Avalonia.Controls.UnitTests/DesktopStyleApplicationLifetimeTests.cs @@ -557,32 +557,25 @@ public void SetupWithClassicDesktopLifetime_Should_Subscribe_To_Platform_Shutdow } [Fact] - public void SetupWithClassicDesktopLifetime_Should_Raise_Startup_After_Application_Initialization() + public void SetupWithClassicDesktopLifetime_Should_Not_Raise_Startup() { - var events = new List(); - string[]? startupArgs = null; + var frameworkInitCalled = false; + var lifetimeBuilderCalled = false; + var startupRaised = false; ClassicDesktopStyleApplicationLifetime? lifetime = null; - CreateAppBuilder(onFrameworkInitializationCompleted: () => events.Add("FrameworkInitializationCompleted")) + CreateAppBuilder(onFrameworkInitializationCompleted: () => frameworkInitCalled = true) .SetupWithClassicDesktopLifetime( ["foo", "bar"], l => { - lifetime = (ClassicDesktopStyleApplicationLifetime)l; - l.Startup += (_, e) => - { - events.Add("Startup"); - startupArgs = e.Args; - }; + lifetimeBuilderCalled = true; + l.Startup += (_, _) => startupRaised = true; }); - Assert.NotNull(lifetime); - - using (lifetime) - { - Assert.Equal(new[] { "FrameworkInitializationCompleted", "Startup" }, events); - Assert.Equal(new[] { "foo", "bar" }, startupArgs); - } + Assert.True(frameworkInitCalled); + Assert.True(lifetimeBuilderCalled); + Assert.False(startupRaised); } [Fact] @@ -603,7 +596,7 @@ public void Start_After_SetupWithClassicDesktopLifetime_Should_Not_Raise_Startup using (lifetime) { - Assert.Equal(1, raised); + Assert.Equal(0, raised); Dispatcher.UIThread.Post(Dispatcher.UIThread.ExitAllFrames); lifetime.Start([]); From eb308cf7599b74722754a6ceeaa1bc4f0335a32a Mon Sep 17 00:00:00 2001 From: Julien Lebosquain Date: Tue, 1 Sep 2026 15:46:42 +0200 Subject: [PATCH 3/3] Fix warning --- .../DesktopStyleApplicationLifetimeTests.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/Avalonia.Controls.UnitTests/DesktopStyleApplicationLifetimeTests.cs b/tests/Avalonia.Controls.UnitTests/DesktopStyleApplicationLifetimeTests.cs index 4f9a855591c..05282a6ba89 100644 --- a/tests/Avalonia.Controls.UnitTests/DesktopStyleApplicationLifetimeTests.cs +++ b/tests/Avalonia.Controls.UnitTests/DesktopStyleApplicationLifetimeTests.cs @@ -562,7 +562,6 @@ public void SetupWithClassicDesktopLifetime_Should_Not_Raise_Startup() var frameworkInitCalled = false; var lifetimeBuilderCalled = false; var startupRaised = false; - ClassicDesktopStyleApplicationLifetime? lifetime = null; CreateAppBuilder(onFrameworkInitializationCompleted: () => frameworkInitCalled = true) .SetupWithClassicDesktopLifetime(